Quelle simple_parser.h
Sprache: C
#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 < PARSER_Impl , Bar , Baz > {
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 PARSER, class AParent, class A>
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 PARSER, class AB, class A, class B>
class OR_CONTAINER2: public AB
{
using SELF= OR_CONTAINER2;
static_assert(std::is_base_of_v<A, AB>, "AB must derive from A" );
static_assert(std::is_base_of_v<B, AB>, "AB must derive from B" );
static_assert(std::negation_v<std::is_copy_constructible<AB>>,
"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 PARSER, class ABC, class A, class B, class C>
class OR_CONTAINER3: public ABC
{
using SELF= OR_CONTAINER3;
static_assert(std::is_base_of_v<A, ABC>, "ABC must derive from A" );
static_assert(std::is_base_of_v<B, ABC>, "ABC must derive from B" );
static_assert(std::is_base_of_v<C, ABC>, "ABC must derive from C" );
static_assert(std::negation_v<std::is_copy_constructible<ABC>>,
"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 PARSER, class ABCD, class A, class B, class C, class D>
class OR_CONTAINER4: public ABCD
{
using SELF= OR_CONTAINER4;
static_assert(std::is_base_of_v<A, ABCD>, "ABCD must derive from A" );
static_assert(std::is_base_of_v<B, ABCD>, "ABCD must derive from B" );
static_assert(std::is_base_of_v<C, ABCD>, "ABCD must derive from C" );
static_assert(std::is_base_of_v<C, ABCD>, "ABCD must derive from D" );
static_assert(std::negation_v<std::is_copy_constructible<ABCD>>,
"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 PARSER, class RULE_PARSER>
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 PARSER, typename PARSER::TokenID tid>
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<PARSER, TokenParser>;
};
/*
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 PARSER, class COND>
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<PARSER, TokenChoice>;
};
template <class PARSER, typename PARSER::TokenID a,
typename PARSER::TokenID b>
class TokenChoiceCond2
{
public :
static bool allowed_token_id(typename PARSER::TokenID id)
{ return id == a || id == b; }
};
template <class PARSER, typename PARSER::TokenID a,
typename PARSER::TokenID b,
typename PARSER::TokenID c>
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 PARSER, class A, class B>
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<PARSER, AND2<PARSER, A, B>>;
};
/*
A rule consisting of three other rules in a row :
rule : : = rule1 rule2 rule3
*/
template <class PARSER, class A, class B, class C>
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 PARSER, class A, class B, class C, class D>
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 PARSER, class A, class B>
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 PARSER, class CONTAINER, class A, class B>
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<PARSER, OR2C<PARSER, CONTAINER, A, B>>;
};
/*
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 PARSER, class A, class B, class C>
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 PARSER, class CONTAINER, class A, class B, class C>
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<PARSER, OR3C<PARSER, CONTAINER, A, B, C>>;
};
/*
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 PARSER, class A, class B, class C, class D>
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 PARSER, class CONTAINER, class A, class B, class C, class D>
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<PARSER, OR4C<PARSER, CONTAINER, A, B, C, D>>;
};
/*
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 PARSER, class CONTAINER,
class A, class B, class C, class D, class E>
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<PARSER, OR5C<PARSER, CONTAINER, A, B, C, D, E>>;
};
/*
A rule consisting of a choice of seven rules :
rule : : = rule1 | rule2 | rule3 | rule4 | rule5 | rule6 | rule7
*/
template <class PARSER, class A, class B, class C, class D, class E, class F,
class G>
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 PARSER,
class LIST_CONTAINER, class ELEMENT_PARSER,
typename PARSER::TokenID SEP, size_t MIN_COUNT>
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<PARSER, LIST_CONTAINER,
ELEMENT_PARSER, SEP, 0 /*no elements is OK*/>;
};
/*
Generic container for parser list elements that handles memory allocation .
This template provides a reusable container for LIST < > parsers , replacing
the need for individual * _ list_container classes with custom add ( ) methods .
ELEM_TYPE : The type of elements stored in the list
PARSER : The parser class ( must provide thd ( ) method for memory allocation )
Elements are allocated on the THD ' s memory pool and moved into place ,
ensuring proper lifetime management within the parser ' s memory context .
*/
template <typename ELEM_TYPE, typename PARSER>
class List_container : public List<ELEM_TYPE>
{
public :
bool add(PARSER *p, ELEM_TYPE &&elem)
{
// Allocate memory for the new list element
ELEM_TYPE *new_elem= (ELEM_TYPE*) p->thd()->alloc(sizeof (ELEM_TYPE));
if (!new_elem)
return true ;
// Move-construct the new element in the preallocated memory
::new (new_elem) ELEM_TYPE(std::move(elem));
return List<ELEM_TYPE>::push_back(new_elem, p->thd()->mem_root);
}
size_t count() const { return List<ELEM_TYPE>::elements; }
};
};
#endif // SIMPLE_PARSER_H
Messung V0.5 in Prozent C=92 H=100 G=95
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland
2026-10-09
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1867298