Handling logical operators for implementing a small query language
Olivier Trempe <[email protected]> Wed, 5 Dec 2018 16:03:22 -0500
| Newsgroups | gmane.comp.parsers.spirit.general |
|---|---|
| Message-ID | <CAM1151ysY82D19OkX6FaeOSerq4BHqwQX4Tgsus4HXiDvWGmnQ@mail.gmail.com> |
Hi,
I need to implement a query parser for a filter parameter used in a REST
query.
More specifically, I need to implement the query language defined at
https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#97-filtering
Based on the following examples
https://stackoverflow.com/questions/24219969/implementing-operator-precedence-with-boost-spirit
https://stackoverflow.com/questions/20387627/ast-and-operator-precedence-in-rule-definition
i came up with the following grammar
template< typename Iterator, typename Skipper >
struct CFilterQueryGrammar : qi::grammar< Iterator, Skipper, FilterQuery() >
{
CFilterQueryGrammar()
: CFilterQueryGrammar::base_type( start, "start" )
{
unaryOp.add( "not", UnaryOperator::not );
relationalOp.add( "gt", BinaryOperator::gt )
( "ge", BinaryOperator::ge )
( "lt", BinaryOperator::lt )
( "le", BinaryOperator::le );
equalityOp.add( "eq", BinaryOperator::eq )
( "ne", BinaryOperator::ne );
andOp.add( "and", UnaryOperator::and );
orOp.add( "or", UnaryOperator::or );
plainString = qi::lexeme[ +boost::spirit::ascii::char_(
"0-9a-zA-Z" ) ];
quotedString = qi::lexeme[ '"' >> +( boost::spirit::ascii::char_ -
'"' ) >> '"' ];
qi::real_parser< double, qi::strict_real_policies< double > >
strict_double;
name = quotedString | plainString;
value = quotedString | strict_double | qi::int_ | plainString;
relationalExpression = ( name >> relationalOp >> value )[ qi::_val
= phx::construct< BinaryOperation >( qi::_1, qi::_2, qi::_3 ) ];
equalityExpression = ( name >> equalityOp >> value )[ qi::_val =
phx::construct< BinaryOperation >( qi::_1, qi::_2, qi::_3 ) ];
andExpression = ( andOp >> expression )[ qi::_val =
phx::construct< UnaryOperation >( qi::_1, qi::_2 ) ];
orExpression = ( orOp >> expression )[ qi::_val =
phx::construct< UnaryOperation >( qi::_1, qi::_2 ) ];
binaryExpression = relationalExpression | equalityExpression;
unaryExpression = ( unaryOp >> expression )[ qi::_val =
phx::construct< UnaryOperation >( qi::_1, qi::_2 ) ];
logicalExpression = andExpression | orExpression | qi::eps;
expression = ( parenthesesExpression | unaryExpression |
binaryExpression ) >> logicalExpression;
parenthesesExpression = "(" >> expression >> ")";
start = qi::eps >> +expression;
BOOST_SPIRIT_DEBUG_NODES( ( start )( parenthesesExpression )(
expression )( binaryExpression )( orExpression )( andExpression )(
logicalExpression )( equalityExpression )( relationalExpression )(
unaryExpression )( value )( name )( quotedString )( plainString ) )
}
qi::symbols< char, UnaryOperator > unaryOp;
qi::symbols< char, BinaryOperator > relationalOp;
qi::symbols< char, BinaryOperator > equalityOp;
qi::symbols< char, UnaryOperator > andOp;
qi::symbols< char, UnaryOperator > orOp;
qi::rule< Iterator, Skipper, Expression() > unaryExpression;
qi::rule< Iterator, Skipper, Expression() > relationalExpression;
qi::rule< Iterator, Skipper, Expression() > equalityExpression;
qi::rule< Iterator, Skipper, Expression() > andExpression;
qi::rule< Iterator, Skipper, Expression() > orExpression;
qi::rule< Iterator, Skipper, Expression() > logicalExpression;
qi::rule< Iterator, Skipper, FilterQuery() > start;
qi::rule< Iterator, Skipper, Expression() > expression;
qi::rule< Iterator, Skipper, Expression() > parenthesesExpression;
qi::rule< Iterator, Skipper, Expression() > binaryExpression;
qi::rule< Iterator, Skipper, Expression() > rhsBinaryExpression;
qi::rule< Iterator, Skipper, std::string() > plainString;
qi::rule< Iterator, Skipper, std::string() > quotedString;
qi::rule< Iterator, Skipper, std::string() > name;
qi::rule< Iterator, Skipper, Literal() > value;
};
Using this grammar to parse a matching query is successful, except for one
thing. Because the line
expression = ( parenthesesExpression | unaryExpression | binaryExpression
) >> logicalExpression;
does not build an operation from both of its expression, only the
logicalExpression persists.
I would like to be able to extract the operator and expression from the
logicalExpression and inject them in a constructed BinaryOperation as the
operator and rhs expression.
Something like this:
expression = (( parenthesesExpression | unaryExpression | binaryExpression
) >> logicalExpression) [ qi::_val = phx::construct< BinaryOperation >(
qi::_1, qi::_2.op, qi::_2.rhs ) ];
I know this might not be the right way to achieve what I need to do.
I would appreciate some guidance to improve the grammar to handle logical
operators properly.
Note that I first defined my logical expressions as:
andExpression = expression >> andOp >> expression
orExpression = expression >> orOp >> expression
but I was quickly bitten by left recursion.
Thanks!
Olivier Trempe
_______________________________________________
Spirit-general mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/spirit-general