binary_op_parser | ![]() |
![]() |
![]() |
![]() |
That's pretty much all there is to YAC. All the logic needed to write such a utility has been laid bare. However, before saying "good night" there is time for one last introduction: the functor_parser. The Functor Parser pages in the Spirit documentation don't really give much idea of the power of this tool. That's a shame because it can be used as the Spirit equivalent of a function call to refactor needlessly repetitive chunks of parser code.
First, return to the Spirit code used to parse a multiplication:
mult_expr
= expr_atom[ mult_expr.stk = arg1 ]
>> '*'
>> expr_atom[ mult_expr.stk += arg1,
push_back(mult_expr.stk,
/* function node pointing to "mult#2" */)
]
;
The code is pretty straightforward, but these rules all have a similar form and they all lead to a similar set of semantic actions:
bitwise_expr ::= equality_expr (('&' | '|' | '^') equality_expr)*
equality_expr ::= compare_expr (("==" | "!=") compare_expr)*
compare_expr ::= shift_expr (('<' | '>' | "<=" | ">=") shift_expr)*
shift_expr ::= add_expr (("<<" | ">>") add_expr)*
add_expr ::= mult_expr (('+' | '-') mult_expr)*
mult_expr ::= expr_atom (('*' | '/' | '%') expr_atom)*
Clearly, writing out mult_expr six times won't make the grammar any easier to read. (In fact, the mult_expr above is a simplification. The actual code is even more ugly.) It would be great if the grammar statement could be turned into this code:
bitwise_expr
= binary_op_p(equality_expr, bitwise_op)[bitwise_expr.stk = arg1];
equality_expr
= binary_op_p(compare_expr, equality_op)[equality_expr.stk = arg1];
compare_expr
= binary_op_p(shift_expr, compare_op)[compare_expr.stk = arg1];
shift_expr
= binary_op_p(add_expr, shift_op)[shift_expr.stk = arg1];
add_expr
= binary_op_p(mult_expr, add_op)[ add_expr.stk = arg1 ];
mult_expr
= binary_op_p(expr_atom, mult_op)[ mult_expr.stk = arg1 ];
And, indeed, thanks to the functor_parser that's exactly how it does appear.
The binary_op_p parser takes two arguments, the second of which is a symbol table containing pointers to the functions representing the appropriate set of operations ('+' | '-' etc.). That's just a YAC implentation detail. What is interesting is that binary_op_p is a fully-fledged parser whose 'result' (here the result is a stack variable) can be assigned to the parent rule's closure. The result is clear, easy to maintain code.
The binary_op_p has three components:
1. struct binary_op_parser is a class template that stores two parsers, created from the two rules passed to its constructor. It also defines a result_t typedef which in this instance is a stack. The parser is invoked through the class' operator() to fill a result variable and to also return the number of characters parsed to the invoking routine. That is, it's public interface is:
template <typename TermT, typename OpT>
struct binary_op_parser {
binary_op_parser(TermT const & term_, OpT const & op_table_);
typedef stack result_t;
template <typename ScannerT>
std::ptrdiff_t
operator()(ScannerT const & scan, result_t & result) const
};
The actual code is to be found here. Other code describing the approach is to be found in functor_parser.html in the spirit docs.
2. struct binary_op_parser_gen has a template operator() which generates a conformant Spirit parser from the two arguments passed to it. The return type of this operator() looks pretty hairy at first sight, but actually it's saying no more than that the rule and symbol table passed to the operator() must be converted to parsers if they are to be stored by the binary_op_parser class template that does the actual work:
template <typename TermT, typename OpT>
spirit::functor_parser<
binary_op_parser<
typename spirit::as_parser<TermT>::type,
typename spirit::as_parser<OpT>::type
>
>
operator()(TermT const & term, OpT const & op_table) const;
3. binary_op_p is a concrete instantiation of this binary_op_parser_gen struct. All of the Spirit parsers that we're now so familiar with are built in a similar way to binary_op_p.
![]() |
![]() |
![]() |
Copyright © 2004 Angus Leeming
Distributed under the Boost Software License,
Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt
)