mariadb/sql/simple_parser.h
Alexander Barkov aa6091be9c Test
2025-06-09 22:01:18 +04:00

1380 lines
36 KiB
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.
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.
*/
/*
TODO:
Let's change all rule constructor to have a "const Parser &p" parameter.
This is 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
CONTAINER1 is needed to derive the storage from another container,
but to make a new distinct type.
For example, in item_numfunc.cc, an Approximate_container and
Approximate_tail_container reuse the same low level storage,
but logically they are two differenc types:
- Approximate_container stores a good approximate number,
it can start only with digits '0', '9' and flags '$' and 'B'.
- Approximate_tail_container stores its tail - it can additionally
start with a group character (, or G).
*/
template<class PARSER, class A>
class CONTAINER1: public A
{
using SELF= CONTAINER1;
using A::A;
public:
// Initialization on parse error
CONTAINER1() :A() { }
// Initialization from its components
CONTAINER1(A && rhs) :A(std::move(rhs)) { }
// Initialization from itself
CONTAINER1(SELF && rhs) :A(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(rhs));
return *this;
}
// Generating empty values
static SELF empty(const PARSER &p) { return SELF(A::empty(p)); }
static SELF empty() { return SELF(A::empty()); }
// Boolean
using A::operator bool;
};
template<class PARSER, class AParent, class A>
class CONTAINER1P: public A
{
using SELF= CONTAINER1P;
using A::A;
public:
// Initialization from its components
CONTAINER1P(A && rhs) :A(std::move(rhs)) { }
// Initialization from itself
CONTAINER1P(SELF && rhs) :A(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
AParent::operator=(std::move(rhs));
return *this;
}
// Generating empty values
static SELF empty(const PARSER &p)
{
return A(AParent::Container::empty(p));
}
static SELF empty()
{
return A(AParent::Container::empty());
}
// Boolean
using A::operator bool;
};
// 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;
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::empty())
{ }
OR_CONTAINER2(B && rhs)
:AB(A::empty(),
std::move(rhs))
{ }
// Initialization from itself
OR_CONTAINER2(SELF && rhs)
:AB(std::move(static_cast<A&&>(rhs)),
std::move(static_cast<B&&>(rhs)))
{ }
// Assignment from itself
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
return *this;
}
// Generating empty values
explicit OR_CONTAINER2(AB && rhs) :AB(std::move(rhs)) { }
static SELF empty(const PARSER &p)
{
return SELF(AB(A::empty(p), B::empty(p)));
}
static SELF empty()
{
return SELF(AB(A::empty(), B::empty()));
}
// The rest
operator bool() const
{
return A::operator bool() && B::operator bool();
}
};
// Make a single container from two other containers suitable for ORxC
template<class PARSER, class AB, class A, class B>
class OR_CONTAINER2P: public AB
{
using SELF= OR_CONTAINER2P;
public:
using AB::AB;
// Initialization on parse error
OR_CONTAINER2P()
:AB(A(), B())
{ }
// Initialization from its components
OR_CONTAINER2P(A && rhs)
:AB(std::move(rhs),
B::Container::empty())
{ }
OR_CONTAINER2P(B && rhs)
:AB(A::Container::empty(),
std::move(rhs))
{ }
// Initialization from itself
OR_CONTAINER2P(SELF && rhs)
:AB(std::move(static_cast<A&&>(rhs)),
std::move(static_cast<B&&>(rhs)))
{ }
// Assignment from itself
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
return *this;
}
// Generating empty values
explicit OR_CONTAINER2P(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()));
}
// The rest
//operator bool() const
//{
// return A::operator bool() && B::operator bool();
//}
};
// 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;
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::empty(),
C::empty())
{ }
OR_CONTAINER3(B && rhs)
:ABC(A::empty(),
std::move(rhs),
C::empty())
{ }
OR_CONTAINER3(C && rhs)
:ABC(A::empty(),
B::empty(),
std::move(rhs))
{ }
// Initialization from itself
explicit OR_CONTAINER3(ABC && rhs) :ABC(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
return *this;
}
// Gerating empty values
OR_CONTAINER3(SELF && rhs) :ABC(std::move(rhs)) { }
static SELF empty(const PARSER &p)
{
return SELF(ABC(A::empty(p), B::empty(p), C::empty(p)));
}
static SELF empty()
{
return SELF(ABC(A::empty(), B::empty(), C::empty()));
}
// Boolean
operator bool() const
{
return A::operator bool() && B::operator bool() && C::operator bool();
}
};
template<class PARSER, class ABC, class A, class B, class C>
class OR_CONTAINER3P: public ABC
{
using SELF= OR_CONTAINER3P;
public:
using ABC::ABC;
// Initialization on parse error
OR_CONTAINER3P()
:ABC(A(), B(), C())
{ }
// Initialization from its components
OR_CONTAINER3P(A && rhs)
:ABC(std::move(rhs),
B::Container::empty(),
C::Container::empty())
{ }
OR_CONTAINER3P(B && rhs)
:ABC(A::Container::empty(),
std::move(rhs),
C::Container::empty())
{ }
OR_CONTAINER3P(C && rhs)
:ABC(A::Container::empty(),
B::Container::empty(),
std::move(rhs))
{ }
// Initialization from itself
explicit OR_CONTAINER3P(ABC && rhs) :ABC(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
return *this;
}
// Gerating empty values
OR_CONTAINER3P(SELF && rhs) :ABC(std::move(rhs)) { }
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()));
}
// Boolean
//operator bool() const
//{
// return A::operator bool() && B::operator bool() && C::operator bool();
//}
};
// 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;
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::empty(),
C::empty(),
D::empty())
{ }
OR_CONTAINER4(B && rhs)
:ABCD(A::empty(),
std::move(rhs),
C::empty(),
D::empty())
{ }
OR_CONTAINER4(C && rhs)
:ABCD(A::empty(),
B::empty(),
std::move(rhs),
D::empty())
{ }
// Initializing from itself
OR_CONTAINER4(SELF && rhs) :ABCD(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
D::operator=(std::move(static_cast<D&&>(rhs)));
return *this;
}
// Generating empty values
explicit OR_CONTAINER4(ABCD && rhs) :ABCD(std::move(rhs)) { }
static SELF empty(const PARSER &p)
{
return SELF(ABCD(A::empty(p), B::empty(p), C::empty(p), D::empty(p)));
}
static SELF empty()
{
return SELF(ABCD(A::empty(), B::empty(), C::empty(), D::empty()));
}
// Boolean
operator bool() const
{
return A::operator bool() &&
B::operator bool() &&
C::operator bool() &&
D::operator bool();
}
};
// 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_CONTAINER4P: public ABCD
{
using SELF= OR_CONTAINER4P;
public:
using ABCD::ABCD;
// Initialization on parse error
OR_CONTAINER4P()
:ABCD(A(), B(), C(), D())
{ }
// Initialization from its components
OR_CONTAINER4P(A && rhs)
:ABCD(std::move(rhs),
B::Container::empty(),
C::Container::empty(),
D::Container::empty())
{ }
OR_CONTAINER4P(B && rhs)
:ABCD(A::Container::empty(),
std::move(rhs),
C::Container::empty(),
D::Container::empty())
{ }
OR_CONTAINER4P(C && rhs)
:ABCD(A::Container::empty(),
B::Container::empty(),
std::move(rhs),
D::Container::empty())
{ }
OR_CONTAINER4P(D && rhs)
:ABCD(A::Container::empty(),
B::Container::empty(),
C::Container::empty(),
std::move(rhs))
{ }
// Initializing from itself
OR_CONTAINER4P(SELF && rhs) :ABCD(std::move(rhs)) { }
SELF & operator=(SELF && rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
D::operator=(std::move(static_cast<D&&>(rhs)));
return *this;
}
// Generating empty values
explicit OR_CONTAINER4P(ABCD && rhs) :ABCD(std::move(rhs)) { }
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()));
}
// Boolean
//operator bool() const
//{
// return A::operator bool() &&
// B::operator bool() &&
// C::operator bool() &&
// D::operator bool();
//}
};
// Templates to parse common rule sequences
/*
An optional rule:
opt_rule ::= [ rule ]
*/
template<class PARSER, class RULE>
class OPT: public RULE
{
public:
OPT()
{ }
#ifdef SIMPLE_PARSER_V2
OPT(OPT && rhs)
:RULE(std::move(rhs))
{ }
OPT & operator=(OPT && rhs)
{
RULE::operator=(std::move(rhs));
return *this;
}
#endif
explicit OPT(RULE && rhs)
:RULE(std::move(rhs))
{ }
OPT(PARSER *p)
:RULE(p)
{
if (!RULE::operator bool() && !p->is_error())
{
RULE::operator=(RULE::empty(*p));
DBUG_ASSERT(RULE::operator bool());
}
else if (p->is_error() && RULE::operator bool())
{
#ifdef SIMPLE_PARSER_V2
/*
RULE is responsible to implement operator=
in the way that it frees all allocated memory.
*/
RULE::operator=(RULE());
DBUG_ASSERT(!RULE::operator bool());
#endif
}
}
static OPT empty(const PARSER &parser)
{
return OPT(RULE::empty(parser));
}
static OPT empty()
{
return OPT(RULE::empty());
}
};
/*
A rule consisting of a single token, e.g.:
rule ::= @
rule ::= IDENT
*/
template<class PARSER, typename PARSER::TokenID tid>
class TOKEN: public PARSER::Token
{
public:
TOKEN()
{ }
#ifdef SIMPLE_PARSER_V2
TOKEN(TOKEN && rhs)
:PARSER::Token(std::move(rhs))
{ }
TOKEN & operator=(TOKEN && rhs)
{
PARSER::Token::operator=(std::move(rhs));
return *this;
}
#endif
TOKEN(const class PARSER::Token &tok)
:PARSER::Token(tok)
{ }
explicit TOKEN(class PARSER::Token &&tok)
:PARSER::Token(std::move(tok))
{ }
TOKEN(PARSER *p)
:PARSER::Token(p->token(tid))
{ }
static TOKEN empty(const PARSER &p)
{
return TOKEN(p.empty_token());
}
static TOKEN empty()
{
return TOKEN(PARSER::Token::empty());
}
using Opt= OPT<PARSER, TOKEN>;
};
/*
A rule consisting of a choice of multiple tokens
rule ::= TOK1 | TOK2 | TOK3
*/
template<class PARSER, class COND>
class TokenChoice: public PARSER::Token
{
public:
TokenChoice()
{ }
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()
:A(), B()
{ }
AND2(AND2 && rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs)))
{ }
AND2(A &&a, B &&b)
:A(std::move(a)), B(std::move(b))
{ }
AND2 & operator=(AND2 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
return *this;
}
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());
}
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()
:A(), B(), C()
{ }
AND3(AND3 && rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs))),
C(std::move(static_cast<C&&>(rhs)))
{ }
AND3(A &&a, B &&b, C &&c)
:A(std::move(a)), B(std::move(b)), C(std::move(c))
{ }
AND3 & operator=(AND3 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
return *this;
}
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());
}
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()
:A(), B(), C(), D()
{ }
AND4(AND4 && rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs))),
C(std::move(static_cast<C&&>(rhs))),
D(std::move(static_cast<D&&>(rhs)))
{ }
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 & operator=(AND4 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
D::operator=(std::move(static_cast<D&&>(rhs)));
return *this;
}
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());
}
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()
{ }
OR2(OR2 &&rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs)))
{ }
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 & operator=(OR2 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
return *this;
}
OR2(PARSER *p)
:A(p), B(A::operator bool() ? B() :B(p))
{
DBUG_ASSERT(!operator bool() || !p->is_error());
}
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()
{ }
OR2C(A &&a)
:CONTAINER(std::move(a))
{ }
OR2C(B &&b)
:CONTAINER(std::move(b))
{ }
OR2C(OR2C &&rhs)
:CONTAINER(std::move(rhs))
{ }
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=(OR2C &&rhs)
{
CONTAINER::operator=(std::move(rhs));
return *this;
}
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()
{ }
OR3(OR3 &&rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs))),
C(std::move(static_cast<C&&>(rhs)))
{ }
OR3(A &&a, B &&b, C &&c)
:A(std::move(a)), B(std::move(b)), C(std::move(c))
{ }
OR3 & operator=(OR3 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
return *this;
}
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());
}
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()
{ }
OR3C(OR3C &&rhs)
:CONTAINER(std::move(rhs))
{ }
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=(OR3C &&rhs)
{
CONTAINER::operator=(std::move(rhs));
return *this;
}
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()
{ }
OR4(OR4 &&rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs))),
C(std::move(static_cast<C&&>(rhs))),
D(std::move(static_cast<D&&>(rhs)))
{ }
OR4 & operator=(OR4 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
D::operator=(std::move(static_cast<D&&>(rhs)));
return *this;
}
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());
}
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()
{ }
OR4C(OR4C && rhs) :CONTAINER(std::move(rhs)) { }
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)) { }
OR4C & operator=(OR4C && rhs)
{
CONTAINER::operator=(std::move(rhs));
return *this;
}
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()
{ }
OR5C(OR5C && rhs) :CONTAINER(std::move(rhs)) { }
OR5C(CONTAINER && rhs) :CONTAINER(std::move(rhs)) { }
OR5C & operator=(OR5C && rhs)
{
CONTAINER::operator=(std::move(rhs));
return *this;
}
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()
{ }
OR7(OR7 &&rhs)
:A(std::move(static_cast<A&&>(rhs))),
B(std::move(static_cast<B&&>(rhs))),
C(std::move(static_cast<C&&>(rhs))),
D(std::move(static_cast<D&&>(rhs))),
E(std::move(static_cast<E&&>(rhs))),
F(std::move(static_cast<F&&>(rhs))),
G(std::move(static_cast<G&&>(rhs)))
{ }
OR7 & operator=(OR7 &&rhs)
{
A::operator=(std::move(static_cast<A&&>(rhs)));
B::operator=(std::move(static_cast<B&&>(rhs)));
C::operator=(std::move(static_cast<C&&>(rhs)));
D::operator=(std::move(static_cast<D&&>(rhs)));
E::operator=(std::move(static_cast<E&&>(rhs)));
F::operator=(std::move(static_cast<F&&>(rhs)));
G::operator=(std::move(static_cast<G&&>(rhs)));
return *this;
}
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());
}
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<class PARSER,
class LIST_CONTAINER, class ELEMENT,
typename PARSER::TokenID SEP, size_t MIN_COUNT>
class LIST: public LIST_CONTAINER
{
protected:
bool m_error;
public:
LIST()
:m_error(true)
{ }
LIST(LIST_CONTAINER &&rhs)
:LIST_CONTAINER(std::move(rhs)),
m_error(false)
{ }
LIST(LIST &&rhs)
:LIST_CONTAINER(std::move(rhs)),
m_error(rhs.m_error)
{ }
LIST & operator=(LIST &&rhs)
{
LIST_CONTAINER::operator=(std::move(rhs));
m_error= rhs.m_error;
return *this;
}
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 elem(p);
if (!elem)
{
if (LIST_CONTAINER::count() == 0 || !separated)
{
/*
Could not get the very first element,
or not-first element in a non-separated list.
*/
m_error= p->is_error();
DBUG_ASSERT(!m_error || !operator bool());
#ifdef SIMPLE_PARSER_V2
if (!p->is_error())
{
if (LIST_CONTAINER::count() == 0)
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;
}
}
}
}
operator bool() const
{
return !m_error && LIST_CONTAINER::count() >= MIN_COUNT;
}
// A parser for an optional list
using Opt= LIST<PARSER, LIST_CONTAINER, ELEMENT, SEP, 0/*no elements*/>;
};
};
#endif // SIMPLE_PARSER_H