Browse Source

[2372] initial parentheses support

JINMEI Tatuya 12 years ago
parent
commit
8baae42823

+ 55 - 8
src/lib/dns/master_lexer.cc

@@ -34,12 +34,13 @@ typedef boost::shared_ptr<master_lexer_internal::InputSource> InputSourcePtr;
 using namespace master_lexer_internal;
 
 struct MasterLexer::MasterLexerImpl {
-    MasterLexerImpl() : source_(NULL), last_was_eol_(false),
+    MasterLexerImpl() : source_(NULL), paren_count_(0), last_was_eol_(false),
                         token_(Token::NOT_STARTED)
     {}
 
     std::vector<InputSourcePtr> sources_;
     InputSource* source_;       // current source
+    size_t paren_count_;
     bool last_was_eol_;
     Token token_;
 };
@@ -130,15 +131,20 @@ namespace master_lexer_internal {
 typedef MasterLexer::Token Token; // convenience shortcut
 
 bool
-State::wasLastEOL(MasterLexer& lexer) const {
+State::wasLastEOL(const MasterLexer& lexer) const {
     return (lexer.impl_->last_was_eol_);
 }
 
-const MasterLexer::Token
-State::getToken(MasterLexer& lexer) const {
+const MasterLexer::Token&
+State::getToken(const MasterLexer& lexer) const {
     return (lexer.impl_->token_);
 }
 
+size_t
+State::getParenCount(const MasterLexer& lexer) const {
+    return (lexer.impl_->paren_count_);
+}
+
 class Start : public State {
 public:
     Start() {}
@@ -158,19 +164,48 @@ public:
     }
 };
 
+class String : public State {
+public:
+    String() {}
+    virtual const State* handle(MasterLexer& /*lexer*/,
+                                MasterLexer::Options& /*options*/,
+                                MasterLexer::Options /*orig_options*/) const
+    {
+        return (NULL);
+    }
+};
+
 namespace {
 const Start START_STATE;
 const CRLF CRLF_STATE;
+const String STRING_STATE;
 }
 
 const State&
-State::getInstance(ID /*state_id*/) {
-    return (START_STATE);
+State::getInstance(ID state_id) {
+    switch (state_id) {
+    case Start:
+        return (START_STATE);
+    case CRLF:
+        return (CRLF_STATE);
+    case EatLine:
+        return (CRLF_STATE);    // XXX
+    case String:
+        return (STRING_STATE);    // XXX
+    }
+}
+
+inline void
+cancelOptions(MasterLexer::Options& options,
+              MasterLexer::Options canceled_options)
+{
+    options = static_cast<MasterLexer::Options>(
+        options & static_cast<MasterLexer::Options>(~canceled_options));
 }
 
 const State*
 Start::handle(MasterLexer& lexer, MasterLexer::Options& options,
-              MasterLexer::Options /*orig_options*/) const
+              MasterLexer::Options orig_options) const
 {
     while (true) {
         const int c = getLexerImpl(lexer)->source_->getChar();
@@ -191,8 +226,20 @@ Start::handle(MasterLexer& lexer, MasterLexer::Options& options,
             getLexerImpl(lexer)->last_was_eol_ = true;
             getLexerImpl(lexer)->token_ = Token(Token::END_OF_LINE);
             return (NULL);
+        } else if (c == '(') {
+            getLexerImpl(lexer)->last_was_eol_ = false;
+            cancelOptions(options,
+                          MasterLexer::END_OF_LINE | MasterLexer::INITIAL_WS);
+            ++getLexerImpl(lexer)->paren_count_;
+            continue;
+        } else if (c == ')') {
+            // TBD: unbalanced case
+            --getLexerImpl(lexer)->paren_count_;
+            options = orig_options; // TBD: only when count becomes 0
+        } else {
+            getLexerImpl(lexer)->last_was_eol_ = false;
+            return (&STRING_STATE);
         }
-        return (&CRLF_STATE); // placeholder
     }
 }
 

+ 12 - 0
src/lib/dns/master_lexer.h

@@ -55,6 +55,8 @@ public:
         INITIAL_WS = 1, ///< recognize begin-of-line spaces
         QSTRING = INITIAL_WS << 1, ///< recognize quoted string
         NUMBER = QSTRING << 1, ///< recognize numeric text as integer
+        END_OF_LINE = NUMBER << 1 ///< recognize end of line as a token
+                                 /// (not user-settable)
     };
 
     /// \brief The constructor.
@@ -152,6 +154,16 @@ private:
     MasterLexerImpl* impl_;
 };
 
+/// \brief Operator to combine \c MasterLexer options
+///
+/// This is a trivial shortcut so that compound options can be specified
+/// in an intuitive way.
+inline MasterLexer::Options
+operator|(MasterLexer::Options o1, MasterLexer::Options o2) {
+    return (static_cast<MasterLexer::Options>(
+                static_cast<unsigned>(o1) | static_cast<unsigned>(o2)));
+}
+
 /// \brief Tokens for \c MasterLexer
 ///
 /// This is a simple value-class encapsulating a type of a lexer token and

+ 5 - 3
src/lib/dns/master_lexer_state.h

@@ -28,7 +28,8 @@ public:
     enum ID {
         Start,                  ///< TBD
         CRLF,
-        EatLine
+        EatLine,
+        String
     };
     virtual const State* handle(MasterLexer& lexer,
                                 MasterLexer::Options& options,
@@ -38,8 +39,9 @@ public:
     static const State& getInstance(ID state_id);
 
     /// \name Read-only accessors for testing purposes.
-    bool wasLastEOL(MasterLexer& lexer) const;
-    const MasterLexer::Token getToken(MasterLexer& lexer) const;
+    bool wasLastEOL(const MasterLexer& lexer) const;
+    const MasterLexer::Token& getToken(const MasterLexer& lexer) const;
+    size_t getParenCount(const MasterLexer& lexer) const;
 
 protected:
     MasterLexer::MasterLexerImpl* getLexerImpl(MasterLexer& lexer) const {

+ 37 - 2
src/lib/dns/tests/master_lexer_state_unittest.cc

@@ -30,15 +30,18 @@ class MasterLexerStateTest : public ::testing::Test {
 protected:
     MasterLexerStateTest() : s_start(State::getInstance(State::Start)),
                              s_crlf(State::getInstance(State::CRLF)),
-                             options(MasterLexer::NONE)
+                             s_string(State::getInstance(State::String)),
+                             options(MasterLexer::END_OF_LINE),
+                             orig_options(options)
     {
         lexer.open(ss);
     }
     const State& s_start;
     const State& s_crlf;
+    const State& s_string;
     MasterLexer lexer;
     std::stringstream ss;
-    MasterLexer::Options options;
+    MasterLexer::Options options, orig_options;
 };
 
 // Common check for the end-of-file condition.
@@ -69,6 +72,8 @@ TEST_F(MasterLexerStateTest, startToEOL) {
     // The next lexer session will reach EOF.  Same eof check should pass.
     s_start.handle(lexer, options);
     eofCheck(s_start, lexer);
+
+    // TBD: EOL after (
 }
 
 TEST_F(MasterLexerStateTest, space) {
@@ -95,4 +100,34 @@ TEST_F(MasterLexerStateTest, space) {
     EXPECT_EQ(Token::INITIAL_WS, s_start.getToken(lexer).getType());
 }
 
+TEST_F(MasterLexerStateTest, parentheses) {
+    ss << "\n(a)";         // add \n to check if 'was EOL' is set to false
+
+    const State* s_next = s_start.handle(lexer, options); // handle \n
+    EXPECT_EQ(static_cast<const State*>(NULL), s_next);
+
+    // Now handle '('
+    EXPECT_EQ(0, s_start.getParenCount(lexer)); // check pre condition
+    options = MasterLexer::END_OF_LINE | MasterLexer::INITIAL_WS;
+    s_next = s_start.handle(lexer, options, options);
+    EXPECT_EQ(&s_string, s_next); // should recognize 'a' as string
+
+    // Check post '(' conditions.  paren_count should be incremented, and
+    // end-of-line and ws should be canceled at the first open paren
+    EXPECT_FALSE(s_start.wasLastEOL(lexer));
+    EXPECT_TRUE((options & MasterLexer::END_OF_LINE) == 0); // eol is canceled
+    EXPECT_TRUE((options & MasterLexer::INITIAL_WS) == 0); // same for init WS
+    EXPECT_EQ(1, s_start.getParenCount(lexer));
+
+    // Then handle ')'.  eol and init_ws are kept cleared, and will be set
+    // again.
+    s_next = s_start.handle(lexer, options, (MasterLexer::END_OF_LINE |
+                                             MasterLexer::INITIAL_WS));
+    EXPECT_EQ(0, s_start.getParenCount(lexer));
+    EXPECT_TRUE((options & MasterLexer::END_OF_LINE) != 0);
+    EXPECT_TRUE((options & MasterLexer::INITIAL_WS) != 0);
+
+    // TBD: Test case: '(;'
+}
+
 }