mariadb/sql/simple_parser.h
Alexander Barkov 706afabc8a MDEV-20022 sql_mode="oracle" does not support TO_NUMBER() function
This patch implements an Oracle style function to_number()
with the following signatures:

- to_number(number_or_string_subject)
- to_number(string_subject, string_format)

The function is implemented in sql/item_numconvfunc.cc.

The function returns the DOUBLE data type for
all signatures and input data types.

The format parser understands the following components:

- Digits: 0, 9
- Hex digits: X
- Group separators: comma (,) and G
- Decimal delimiers: period (.) and D
- Approximate number signature: EEEE
- Currency/numeric flags: $ and B
- Currency signatures: C, L, U
- Sign signatures: S, MI, PR
- Special format signatures: V, TM, TM9, TME
- Format flag: FM

Note, the parser was implemented assuming that we'll also have the
oppostite function to_char() soon for numeric input.
So it parser all known components.
However, the function to_number() does not support:
- Formats V, TM, TM9, TME. to_number() returns NULL if the format
  string has these components.
  These componens are supported only by to_char() in Oracle.

Features not inclided into this patch:
- The ON CONVERSION ERROR clause
- The third parameter (nlsparam)
- Internationalized components: G, D, C, L, U.
These features will be implemented later, under terms of MDEV-36978.

Notable changes in the other files:

- item_func.h: adding Item_handled_func::Handler_double

- simple_parser.h: adding a number of *CONTAINER* templates
  They help to save on duplicate code when creating classes
  suitable for passing into parsing templates such as OPT, OR2C, OR3C, etc

- simple_parser.h: Adding parsing templates OR4C and OR5C

- simple_parser.h: Moving the template "OPT" towars the beginning of the file
  Rule parsing templates TOKEN, TokenChoice, AND2, OR2C, OR3C, OR4C, OR5C, LIST
  now provide a sub-class Opt, to parse its optional rule.

- simple_parser.h: Adding "explicit" to all "operator bool" definitions

- Renaming Parser_templates::TOKEN to Parser_templates::TokenParser

- Adding "explicit" to all "operator bool()" templates/classes,
  to avoid hidden implicit conversion (to int, void*, etc).

- Renaming the LIST template parameter ELEMENT to ELEMENT_PARSER,
  to make it clearer what it is for.

- Renaming the OPT template parameter RULE to RULE_PARSER,
  to make it clearer what it is for.
2025-08-18 11:23:54 +04:00

1193 lines
34 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.
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*/>;
};
};
#endif // SIMPLE_PARSER_H