#ifndef SIMPLE_PARSER_H #define SIMPLE_PARSER_H /* Copyright (c) 2024, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ #include "simple_tokenizer.h" /* A set of templates for constructing a recursive-descent LL(1) parser. This code utilizes the "Policy Based Design" approach. For details see: https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design One is supposed to define classes corresponding to grammar productions. The class should inherit from the grammar rule template. For example, a grammar rule foo := bar, baz is implemented with class Bar ... ; // "bar" is parsed into Bar object class Baz ... ; // "baz" is parsed into Baz object // "foo" is parsed into a Foo object. class Foo: public Parser_templates::AND2 { using AND2::AND2; ... }; Parsing code is generated by inheriting AND2's constructors with "using" like shown above. All grammar rule-based classes should also have - a capability to construct an "empty"(i.e. invalid) object with the default constructor. This will be invoked when parsing fails. - operator bool() which returns true if the object is non-empty (i.e. valid) and false otherwise. Parsing is done by constructing parser output from the parser object: Foo parsed_output(parser); PARSER_Impl here is a class implementing a tokenizer and error condition storage, like Extended_string_tokenizer. Steps to make your own parser: First of all, you implement a tokenizer class. It can derive from Extended_string_tokenizer. See for examples: - class Tokenizer in item_numconvfunc.cc - class Optimizer_hint_tokenizer in opt_hints_parser.h The tokenizer class should implement: - a enum TokenID enumerating all possible tokens - a class Token. Normally it should contain two components: * a const string pointer with length, they will point to the fragment of the parsed text corresponding to each token returned by the tokenizer * a TokenID - a method get_token() On the next step you implement a parser class. See for examples: - class item_numconvfunc.cc in item_numconvfunc.cc - class Optimizer_hint_parser in opt_hints_parser.h The parser class should derive from the tokeniner class and from the clas Parser_templates. The parser class should implement: - a method empty_token() - a method null_token() - a method shift() - returning the current lookahead tokend and loading the next lookahead token into the member m_look_ahead_token. - a method Token(TokenID id) - checking the current looakead token ID and doing shift if it matches the current lookahead token in m_look_ahead_token. - a set of grammar rules using templates implemented in this file */ /* A future change proposal: Let's change all rule constructors to have a "const Parser &p" parameter. This will help to avoid having two versions of empty: - empty(const Parser &p) - empty() */ class Parser_templates { protected: /* Containers to collect parsed data into the goal structures. These classes are needed to avoid a boiler plate code. */ /* CONTAINER1 wraps a user defined container class A (designed to store some grammar branch) in the way to make class A suitable for passing to OR_CONTAINER* by adding some common (boiler plate) method implementations, e.g: - deleting copying constructor and operator= - implementing moving constructor and operator= - implementing methods empty() Unlike OR_CONTAINER* (see below), CONTAINER1 assumes that A derives only from one parent class. */ template class CONTAINER1: public A { using SELF= CONTAINER1; using A::A; public: // Delete copying CONTAINER1(const SELF & rhs) = delete; SELF & operator=(const SELF & rhs) = delete; // Initialization from itself CONTAINER1(SELF && rhs) = default; SELF & operator=(SELF && rhs) = default; // Initialization from its components explicit CONTAINER1(A && rhs) :A(std::move(rhs)) { } // Generating empty values static SELF empty(const PARSER &p) { return SELF(A(AParent::Container::empty(p))); } static SELF empty() { return SELF(A(AParent::Container::empty())); } }; /* OR_CONTAINER* to be passed as a CONTANER parameter to the ORxC parsing templates. OR_CONTAINER* derives from parts (components), which store the data parsed by alternative branches in the grammar. When one part is initialized from some data, all other parts are initialized to the value returned by the empty() method of the corresponding part. */ // Make a single container from two other containers suitable for ORxC template class OR_CONTAINER2: public AB { using SELF= OR_CONTAINER2; static_assert(std::is_base_of_v, "AB must derive from A"); static_assert(std::is_base_of_v, "AB must derive from B"); static_assert(std::negation_v>, "Invalid use of OR_CONTAINER2"); public: using AB::AB; // Initialization on parse error OR_CONTAINER2() :AB(A(), B()) { } // Initialization from its components OR_CONTAINER2(A && rhs) :AB(std::move(rhs), B::Container::empty()) { } OR_CONTAINER2(B && rhs) :AB(A::Container::empty(), std::move(rhs)) { } // Delete copying OR_CONTAINER2(const SELF & rhs) = delete; SELF & operator=(const SELF & rhs) = delete; // Initialization from itself OR_CONTAINER2(SELF && rhs) = default; SELF & operator=(SELF && rhs) = default; // Generating empty values explicit OR_CONTAINER2(AB && rhs) :AB(std::move(rhs)) { } static SELF empty(const PARSER &p) { return SELF(AB(A::Container::empty(p), B::Container::empty(p))); } static SELF empty() { return SELF(AB(A::Container::empty(), B::Container::empty())); } }; // Make a single container from three other containers suitable for ORxC template class OR_CONTAINER3: public ABC { using SELF= OR_CONTAINER3; static_assert(std::is_base_of_v, "ABC must derive from A"); static_assert(std::is_base_of_v, "ABC must derive from B"); static_assert(std::is_base_of_v, "ABC must derive from C"); static_assert(std::negation_v>, "Invalid use of OR_CONTAINER3"); public: using ABC::ABC; // Initialization on parse error OR_CONTAINER3() :ABC(A(), B(), C()) { } // Initialization from its components OR_CONTAINER3(A && rhs) :ABC(std::move(rhs), B::Container::empty(), C::Container::empty()) { } OR_CONTAINER3(B && rhs) :ABC(A::Container::empty(), std::move(rhs), C::Container::empty()) { } OR_CONTAINER3(C && rhs) :ABC(A::Container::empty(), B::Container::empty(), std::move(rhs)) { } explicit OR_CONTAINER3(ABC && rhs) :ABC(std::move(rhs)) { } // Delete copying OR_CONTAINER3(const SELF & rhs) = delete; SELF & operator=(const SELF & rhs) = delete; // Initialization from itself OR_CONTAINER3(SELF && rhs) = default; SELF & operator=(SELF && rhs) = default; // Gerating empty values static SELF empty(const PARSER &p) { return SELF(ABC(A::Container::empty(p), B::Container::empty(p), C::Container::empty(p))); } static SELF empty() { return SELF(ABC(A::Container::empty(), B::Container::empty(), C::Container::empty())); } }; // Make a single container from four other containers suitable for ORxC template class OR_CONTAINER4: public ABCD { using SELF= OR_CONTAINER4; static_assert(std::is_base_of_v, "ABCD must derive from A"); static_assert(std::is_base_of_v, "ABCD must derive from B"); static_assert(std::is_base_of_v, "ABCD must derive from C"); static_assert(std::is_base_of_v, "ABCD must derive from D"); static_assert(std::negation_v>, "Invalid use of OR_CONTAINER4"); public: using ABCD::ABCD; // Initialization on parse error OR_CONTAINER4() :ABCD(A(), B(), C(), D()) { } // Initialization from its components OR_CONTAINER4(A && rhs) :ABCD(std::move(rhs), B::Container::empty(), C::Container::empty(), D::Container::empty()) { } OR_CONTAINER4(B && rhs) :ABCD(A::Container::empty(), std::move(rhs), C::Container::empty(), D::Container::empty()) { } OR_CONTAINER4(C && rhs) :ABCD(A::Container::empty(), B::Container::empty(), std::move(rhs), D::Container::empty()) { } OR_CONTAINER4(D && rhs) :ABCD(A::Container::empty(), B::Container::empty(), C::Container::empty(), std::move(rhs)) { } explicit OR_CONTAINER4(ABCD && rhs) :ABCD(std::move(rhs)) { } // Delete copying OR_CONTAINER4(const SELF & rhs) = delete; SELF & operator=(const SELF & rhs) = delete; // Initializing from itself OR_CONTAINER4(SELF && rhs) = default; SELF & operator=(SELF && rhs) = default; // Generating empty values static SELF empty(const PARSER &p) { return SELF(ABCD(A::Container::empty(p), B::Container::empty(p), C::Container::empty(p), D::Container::empty(p))); } static SELF empty() { return SELF(ABCD(A::Container::empty(), B::Container::empty(), C::Container::empty(), D::Container::empty())); } }; // Templates to parse common rule sequences /* A parser for an optional rule: opt_rule ::= [ rule ] Template parameters: - PARSER - the main parser class - RULE_PARSE - the rule which we want to make optional in some grammar */ template class OPT: public RULE_PARSER { public: OPT() = default; #ifdef SIMPLE_PARSER_V2 // Delete copying OPT(const OPT & rhs) = delete; OPT & operator=(const OPT & rhs) = delete; // Assigning from itself OPT(OPT && rhs) = default; OPT & operator=(OPT && rhs) = default; #endif OPT(PARSER *p) :RULE_PARSER(p) { if (!RULE_PARSER::operator bool() && !p->is_error()) { RULE_PARSER::operator=(RULE_PARSER::empty(*p)); DBUG_ASSERT(RULE_PARSER::operator bool()); } else if (p->is_error() && RULE_PARSER::operator bool()) { #ifdef SIMPLE_PARSER_V2 /* RULE_PARSER is responsible to implement operator= in the way that it frees all allocated memory. */ RULE_PARSER::operator=(RULE_PARSER()); DBUG_ASSERT(!RULE_PARSER::operator bool()); #endif } } }; /* A rule consisting of a single token, e.g.: rule ::= @ rule ::= IDENT */ template class TokenParser: public PARSER::Token { public: TokenParser() { } #ifdef SIMPLE_PARSER_V2 TokenParser(TokenParser && rhs) = default; TokenParser & operator=(TokenParser && rhs) = default; #endif TokenParser(const class PARSER::Token &tok) = delete; TokenParser & operator=(const class PARSER::Token &tok) = delete; explicit TokenParser(class PARSER::Token &&tok) :PARSER::Token(std::move(tok)) { } TokenParser & operator=(const class PARSER::Token &&tok) { PARSER::Token::operator=(std::move(tok)); return *this; } TokenParser(PARSER *p) :PARSER::Token(p->token(tid)) { } static TokenParser empty(const PARSER &p) { return TokenParser(p.empty_token()); } static TokenParser empty() { return TokenParser(PARSER::Token::empty()); } using Opt= OPT; }; /* A rule consisting of a choice of multiple tokens rule ::= TOK1 | TOK2 | TOK3 Which tokens are good or wrong for this rule is determined by the template parameter class COND which must have a static method: bool allowed_token_id(TokenID id). It gets the lookahead token id as a parameter and returns: - true for good tokens - false for bad tokens */ template class TokenChoice: public PARSER::Token { public: TokenChoice() { } /* Pass the parser's lookahead token id to COND::allowed_token_id() to determine if it's a good or bad token. */ TokenChoice(PARSER *p) :PARSER::Token(COND::allowed_token_id(p->look_ahead_token_id()) ? p->shift() : p->null_token()) { DBUG_ASSERT(!p->is_error() || !PARSER::Token::operator bool()); } TokenChoice(const class PARSER::Token &tok) :PARSER::Token(tok) { } static TokenChoice empty(const PARSER &parser) { return TokenChoice(parser.empty_token()); } static TokenChoice empty() { return PARSER::Token::empty(); } using Opt= OPT; }; template class TokenChoiceCond2 { public: static bool allowed_token_id(typename PARSER::TokenID id) { return id == a || id == b; } }; template class TokenChoiceCond3 { public: static bool allowed_token_id(typename PARSER::TokenID id) { return id == a || id == b || id == c; } }; /* A rule consisting of two other rules in a row: rule ::= rule1 rule2 */ template class AND2: public A, public B { public: AND2() = default; // Delete copying AND2(const AND2 & rhs) = delete; AND2 & operator=(const AND2 & rhs) = delete; // Initializing from itself AND2(AND2 && rhs) = default; AND2 & operator=(AND2 &&rhs) = default; // Initializing from its components AND2(A &&a, B &&b) :A(std::move(a)), B(std::move(b)) { } AND2(PARSER *p) :A(p), B(A::operator bool() ? B(p) : B()) { if (A::operator bool() && !B::operator bool()) { p->set_syntax_error(); // Reset A to have A, B reported as "false" by their operator bool() A::operator=(std::move(A())); } DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() && B::operator bool(); } static AND2 empty(const PARSER &p) { return AND2(A::empty(p), B::empty(p)); } using Opt= OPT>; }; /* A rule consisting of three other rules in a row: rule ::= rule1 rule2 rule3 */ template class AND3: public A, public B, public C { public: AND3() = default; // Delete copying AND3(const AND3 & rhs) = delete; AND3 & operator=(const AND3 & rhs) = delete; // Initializing from itself AND3(AND3 && rhs) = default; AND3 & operator=(AND3 &&rhs) = default; // Initializing from components AND3(A &&a, B &&b, C &&c) :A(std::move(a)), B(std::move(b)), C(std::move(c)) { } AND3(PARSER *p) :A(p), B(A::operator bool() ? B(p) : B()), C(A::operator bool() && B::operator bool() ? C(p) : C()) { if (A::operator bool() && (!B::operator bool() || !C::operator bool())) { p->set_syntax_error(); // Reset A to have A, B, C reported as "false" by their operator bool() A::operator=(std::move(A())); B::operator=(std::move(B())); C::operator=(std::move(C())); } DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() && B::operator bool() && C::operator bool(); } static AND3 empty(const PARSER &p) { return AND3(A::empty(p), B::empty(p), C::empty(p)); } }; /* A rule consisting of four other rules in a row: rule ::= rule1 rule2 rule3 rule4 */ template class AND4: public A, public B, public C, public D { public: AND4() = default; // Delete copying AND4(const AND4 & rhs) = delete; AND4 & operator=(const AND4 & rhs) = delete; // Initializing from itself AND4(AND4 && rhs) = default; AND4 & operator=(AND4 &&rhs) = default; // Initializing from components AND4(A &&a, B &&b, C &&c, D &&d) :A(std::move(a)), B(std::move(b)), C(std::move(c)), D(std::move(d)) { } AND4(PARSER *p) :A(p), B(A::operator bool() ? B(p) : B()), C(A::operator bool() && B::operator bool() ? C(p) : C()), D(A::operator bool() && B::operator bool() && C::operator bool() ? D(p) : D()) { if (A::operator bool() && (!B::operator bool() || !C::operator bool() || !D::operator bool())) { p->set_syntax_error(); // Reset A to have A, B, C reported as "false" by their operator bool() A::operator=(A()); B::operator=(B()); C::operator=(C()); D::operator=(D()); } DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() && B::operator bool() && C::operator bool() && D::operator bool(); } static AND4 empty(const PARSER &p) { return AND4(A::empty(p), B::empty(p), C::empty(), D::empty()); } }; /* A rule consisting of a choice of rwo rules: rule ::= rule1 | rule2 For the cases when the two branches have incompatible storage. */ template class OR2: public A, public B { public: OR2() = default; // Delete copying OR2(const OR2 & rhs) = delete; OR2 & operator=(const OR2 & rhs) = delete; // Initializing from itself OR2(OR2 &&rhs) = default; OR2 & operator=(OR2 &&rhs) = default; // Initializing from components OR2(A &&a, B &&b) :A(std::move(a)), B(std::move(b)) { } OR2(A && rhs) :A(std::move(rhs)), B() { } OR2(B && rhs) :A(), B(std::move(rhs)) { } OR2(PARSER *p) :A(p), B(A::operator bool() ? B() :B(p)) { DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() || B::operator bool(); } static OR2 empty(const PARSER &p) { return OR2(A::empty(p), B::empty(p)); } }; /* A rule consisting of a choice of rwo rules, e.g. rule ::= rule1 | rule2 For the cases when the two branches have a compatible storage, passed as a CONTAINER, which must have constructors: CONTAINER(const A &a) CONTAINER(const B &b) */ template class OR2C: public CONTAINER { public: OR2C() = default; // Delete copying OR2C(const OR2C & rhs) = delete; OR2C & operator=(const OR2C & rhs) = delete; // Initializing from itself OR2C(OR2C &&rhs) = default; OR2C & operator=(OR2C &&rhs) = default; // Initializing from components OR2C(A &&a) :CONTAINER(std::move(a)) { } OR2C(B &&b) :CONTAINER(std::move(b)) { } OR2C(CONTAINER && rhs) :CONTAINER(std::move(rhs)) { } static OR2C empty(const PARSER &parser) { CONTAINER tmp(CONTAINER::empty(parser)); DBUG_ASSERT((bool) tmp); return tmp; } OR2C & operator=(A &&rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR2C & operator=(B &&rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR2C(PARSER *p) :CONTAINER(A(p)) { if (CONTAINER::operator bool() || CONTAINER::operator=(B(p))) return; DBUG_ASSERT(!CONTAINER::operator bool()); } using Opt= OPT>; }; /* A rule consisting of a choice of three rules: rule ::= rule1 | rule2 | rule3 For the case when the three branches have incompatible storage */ template class OR3: public A, public B, public C { public: OR3() = default; // Delete copying OR3(const OR3 & rhs) = delete; OR3 & operator=(const OR3 & rhs) = delete; // Initializing from itself OR3(OR3 &&rhs) = default; OR3 & operator=(OR3 &&rhs) = default; // Initializing from components OR3(A &&a, B &&b, C &&c) :A(std::move(a)), B(std::move(b)), C(std::move(c)) { } OR3(PARSER *p) :A(p), B(A::operator bool() ? B() : B(p)), C(A::operator bool() || B::operator bool() ? C() : C(p)) { DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() || B::operator bool() || C::operator bool(); } static OR3 empty(const PARSER &p) { return OR3(A::empty(p), B::empty(p), C::empty(p)); } }; /* A rule consisting of a choice of three rules, e.g. rule ::= rule1 | rule2 | rule3 For the cases when the three branches have a compatible storage, passed as a CONTAINER, which must have constructors: CONTAINER(const A &a) CONTAINER(const B &b) CONTAINER(const C &c) */ template class OR3C: public CONTAINER { public: OR3C() = default; // Delete copying OR3C(const OR3C & rhs) = delete; OR3C & operator=(const OR3C & rhs) = delete; // Initializing from itself OR3C(OR3C &&rhs) = default; OR3C & operator=(OR3C &&rhs) = default; // Initializing from components OR3C(CONTAINER && rhs) :CONTAINER(std::move(rhs)) { } static OR3C empty(const PARSER &parser) { return CONTAINER::empty(parser); } OR3C(A &&a) :CONTAINER(std::move(a)) { } OR3C(B &&b) :CONTAINER(std::move(b)) { } OR3C(C &&c) :CONTAINER(std::move(c)) { } OR3C & operator=(A &&rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR3C & operator=(B &&rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR3C & operator=(C &&rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR3C(PARSER *p) :CONTAINER(A(p)) { if (CONTAINER::operator bool() || CONTAINER::operator=(B(p)) || CONTAINER::operator=(C(p))) return; DBUG_ASSERT(!CONTAINER::operator bool()); } using Opt= OPT>; }; /* A rule consisting of a choice of four rules: rule ::= rule1 | rule2 | rule3 | rule4 For the case when the four branches have incompatible storage */ template class OR4: public A, public B, public C, public D { public: OR4() = default; // Delete copying OR4(const OR4 & rhs) = delete; OR4 & operator=(const OR4 & rhs) = delete; // Initializing from itself OR4(OR4 &&rhs) = default; OR4 & operator=(OR4 &&rhs) = default; OR4(PARSER *p) :A(p), B(A::operator bool() ? B() : B(p)), C(A::operator bool() || B::operator bool() ? C() : C(p)), D(A::operator bool() || B::operator bool() || C::operator bool() ? D() : D(p)) { DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() || B::operator bool() || C::operator bool() || D::operator bool(); } }; /* A rule consisting of a choice of four rules, e.g. rule ::= rule1 | rule2 | rule3 | rule4 For the cases when the three branches have a compatible storage, passed as a CONTAINER, which must have constructors: CONTAINER(const A && a) CONTAINER(const B && b) CONTAINER(const C && c) CONTAINER(const D && d) */ template class OR4C: public CONTAINER { public: OR4C() = default; // Delete copying OR4C(const OR4C & rhs) = delete; OR4C & operator=(const OR4C & rhs) = delete; // Initializing from itself OR4C(OR4C && rhs) = default; OR4C & operator=(OR4C && rhs) = default; // Initializing from components OR4C(CONTAINER && rhs) :CONTAINER(std::move(rhs)) { } OR4C(A && a) :CONTAINER(std::move(a)) { } OR4C(B && b) :CONTAINER(std::move(b)) { } OR4C(C && c) :CONTAINER(std::move(c)) { } OR4C(D && d) :CONTAINER(std::move(d)) { } // Initializing from its components OR4C & operator=(CONTAINER && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR4C & operator=(A && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR4C & operator=(B && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR4C & operator=(C && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR4C & operator=(D && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR4C(PARSER *p) :CONTAINER(A(p)) { if (CONTAINER::operator bool() || CONTAINER::operator=(B(p)) || CONTAINER::operator=(C(p)) || CONTAINER::operator=(D(p))) return; DBUG_ASSERT(!CONTAINER::operator bool()); } using Opt= OPT>; }; /* A rule consisting of a choice of four rules, e.g. rule ::= rule1 | rule2 | rule3 | rule4 | rule5 For the cases when the three branches have a compatible storage, passed as a CONTAINER, which must have constructors: CONTAINER(const A && a) CONTAINER(const B && b) CONTAINER(const C && c) CONTAINER(const D && d) CONTAINER(const E && e) */ template class OR5C: public CONTAINER { public: OR5C() = default; // Delete copying OR5C(const OR5C & rhs) = delete; OR5C & operator=(const OR5C & rhs) = delete; // Initializing from itself OR5C(OR5C && rhs) = default; OR5C & operator=(OR5C && rhs) = default; // Initializing from its components OR5C(CONTAINER && rhs) :CONTAINER(std::move(rhs)) { } OR5C & operator=(CONTAINER && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } static OR5C empty(const PARSER &parser) { return CONTAINER::empty(parser); } static OR5C empty() { return CONTAINER::empty(); } OR5C(A && a) :CONTAINER(std::move(a)) { } OR5C(B && b) :CONTAINER(std::move(b)) { } OR5C(C && c) :CONTAINER(std::move(c)) { } OR5C(D && d) :CONTAINER(std::move(d)) { } OR5C(E && e) :CONTAINER(std::move(e)) { } OR5C & operator=(A && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR5C & operator=(B && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR5C & operator=(C && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR5C & operator=(D && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR5C & operator=(E && rhs) { CONTAINER::operator=(std::move(rhs)); return *this; } OR5C(PARSER *p) :CONTAINER(A(p)) { if (CONTAINER::operator bool() || CONTAINER::operator=(B(p)) || CONTAINER::operator=(C(p)) || CONTAINER::operator=(D(p)) || CONTAINER::operator=(E(p))) return; DBUG_ASSERT(!CONTAINER::operator bool()); } using Opt= OPT>; }; /* A rule consisting of a choice of seven rules: rule ::= rule1 | rule2 | rule3 | rule4 | rule5 | rule6 | rule7 */ template class OR7: public A, public B, public C, public D, public E, public F, public G { public: OR7() = default; // Delete copying OR7(const OR7 & rhs) = delete; OR7 & operator=(const OR7 & rhs) = delete; // Initializing from itself OR7(OR7 &&rhs) = default; OR7 & operator=(OR7 &&rhs) = default; // Other methods OR7(PARSER *p) :A(p), B(A::operator bool() ? B() : B(p)), C(A::operator bool() || B::operator bool() ? C() : C(p)), D(A::operator bool() || B::operator bool() || C::operator bool() ? D() : D(p)), E(A::operator bool() || B::operator bool() || C::operator bool() || D::operator bool() ? E() : E(p)), F(A::operator bool() || B::operator bool() || C::operator bool() || D::operator bool() || E::operator bool() ? F() : F(p)), G(A::operator bool() || B::operator bool() || C::operator bool() || D::operator bool() || E::operator bool() || F::operator bool() ? G() : G(p)) { DBUG_ASSERT(!operator bool() || !p->is_error()); } explicit operator bool() const { return A::operator bool() || B::operator bool() || C::operator bool() || D::operator bool() || E::operator bool() || F::operator bool() || G::operator bool(); } }; /* A list with at least MIN_COUNT elements (typlically 0 or 1), with or without a token separator between elements: list ::= element [ {, element }... ] // with a separator list ::= element [ element ... ] // without a separator Pass the null-token special purpose ID in SEP for a non-separated list, or a real token ID for a separated list. If MIN_COUNT is 0, then the list becomes optional, which corresponds to the following grammar: list ::= [ element [ {, element }... ] ] // with a separator list ::= [ element [ element ... ] ] // without a separator Template parameters: - PARSER - The main parser class - LIST_CONTAINER - The class where the list parsed data is accumulated to - ELEMENT_PARSER - The element parser - SEP - The ID of the separator token between elements. If the ID is eqoal to null_token().id(), then the list is not separated. See above. - MIN_COUNT - The mininum number of elements. Usually 1. 0 means that the list is optional: [ list ] */ template class LIST: public LIST_CONTAINER { protected: bool m_error; public: LIST() :m_error(true) { } // Delete copying LIST(const LIST & rhs) = delete; LIST & operator=(const LIST & rhs) = delete; // Initializing from its components /* This constructor is needed to initialize LIST from LIST_CONTAINER::empty() */ LIST(LIST_CONTAINER &&rhs) :LIST_CONTAINER(std::move(rhs)), m_error(false) { } // Initializing from itself LIST(LIST &&rhs) = default; LIST & operator=(LIST &&rhs) = default; static LIST empty(const PARSER &parser) { return LIST(LIST_CONTAINER::empty(parser)); } LIST(PARSER *p) :m_error(true) { // Determine if the caller wants a separated or a non-separated list const bool separated= SEP != PARSER::null_token().id(); for ( ; ; ) { ELEMENT_PARSER elem(p); if (!elem) { if (LIST_CONTAINER::count() == 0 || !separated) { /* Could not parse an element: 1. the very first element in an optional list: [ ELEM [,ELEM]...] 2. or non-first element in a non-separated list: ELEM [ELEM...] This state is OK, it's not a parse error, unless an error happened when parsing an ELEM subrule: */ m_error= p->is_error(); DBUG_ASSERT(!m_error || !operator bool()); #ifdef SIMPLE_PARSER_V2 if (!p->is_error()) { if (LIST_CONTAINER::count() == 0) { /* This is the case #1 described above. LIST_CONTAINER is currently in a "non parsed" state, its operator bool() would return false. Initialize LIST_CONTAINER to its "empty" value to make operator bool() return true, to make the caller aware that the list was parsed, just it was empty. */ LIST_CONTAINER::operator=(empty(*p)); } } #endif return; } // Could not get the next element after the separator p->set_syntax_error(); m_error= true; DBUG_ASSERT(!operator bool()); return; } if (LIST_CONTAINER::add(p, std::move(elem))) { p->set_fatal_error(); m_error= true; DBUG_ASSERT(!operator bool()); return; } if (separated) { if (!p->token(SEP)) { m_error= false; DBUG_ASSERT(operator bool()); return; } } } } explicit operator bool() const { return !m_error && LIST_CONTAINER::count() >= MIN_COUNT; } // A parser for an optional list using Opt= LIST; }; }; #endif // SIMPLE_PARSER_H