Home | Libraries | People | FAQ | More |
Note | |
---|---|
Note that you can find everything that has been included and defined so far here. |
foldl
applies a parser repeatedly and iterates over the parsing results from
left to right. (This is where the l
in the name comes from). Metaparse provides another folding parser combinator,
foldr
.
It applies a parser on the input as well but it iterates from right
to left over the results.
Similarly to foldl_start_with_parser
,
Metaparse provides foldr_start_with_parser
as well.
A major difference between the two (foldl_start_with_parser
and foldr_start_with-parser
)
solutions is that while foldl_start_with_parser
treats
the first number as a special one, foldr_start_with_parser
treats
the last number as a special one. This might sound
strange, but think about it: if you want to summarise the elements from
right to left, your starting value should be the last element, not the
first one, as the first one is the one you visit last.
Due to the above difference foldr_start_with_parser
is not
a drop-in replacement of foldl_start_with_parser
. While
the list of values foldl
was iterating over is "8
",
"/ 4
",
"/ 2
",
the list of values foldlr
has to iterate over is "2
",
"4 /
",
"8 /
".
This means that the function we use to "add"
a new value to the already evaluated part of the expression (this has been
binary_op
so far) has to
be prepared for taking the next operator and operand in a reverse order
(eg. by taking "4 /
"
instead of "/ 4
").
We write another metafunction for this
purpose:
> template <class S, class Item> \ ...> struct reverse_binary_op : \ ...> eval_binary_op< \ ...> typename boost::mpl::at_c<Item, 0>::type, \ ...> boost::mpl::at_c<Item, 1>::type::value, \ ...> S \ ...> > \ ...> {};
There are multiple differences between binary_op
and reverse_binary_op
:
Item
argument,
which is a vector
is
expected to be [operator, operand]
in binary_op
and [operand, operator]
in reverse_binary_op
.
eval_binary_op
to evaluate the subexpression, but binary_op
treats S
, the value
representing the already evaluated part of the expression as the left
operand, while reverse_binary_op
treats it as the right operand. This is because in the first case we
are going from left to right while in the second case we are going
from right to left.
We need to include foldr_start_with_parser
:
> #include <boost/metaparse/foldr_start_with_parser.hpp>
We can rewrite mult_exp
using foldr_start_with_parser
:
> using mult_exp3 = \ ...> foldr_start_with_parser< \ ...> sequence<int_token, one_of<times_token, divides_token>>, /* The parser applied repeatedly */ \ ...> int_token, /* The parser parsing the last number */ \ ...> boost::mpl::quote2<reverse_binary_op> /* The function called for every result */ \ ...> /* of applying the above parser */ \ ...> >;
It is almost the same as mult_exp2
,
but ...
<number>
<operator>
elements instead of <operator> <number>
elements (what mult_exp2
did).
reverse_binary_op
instead of binary_op
as the function that is called for every result of applying the above
parser.
We can create a new version of exp_parser
that uses mult_exp3
instead
of mult_exp2
:
> using exp_parser17 = \ ...> build_parser< \ ...> foldl_start_with_parser< \ ...> sequence<one_of<plus_token, minus_token>, mult_exp3>, \ ...> mult_exp3, \ ...> boost::mpl::quote2<binary_op> \ ...> > \ ...> >;
The only difference between exp_parser17
and the previous version, exp_parser16
is that it uses the updated version of mult_exp
.
Let's try this parser out:
> exp_parser17::apply<BOOST_METAPARSE_STRING("8 / 4 / 2")>::type mpl_::integral_c<int, 4>
This version of the parser gives the other possible
result. The one you get when division is right associative, which means
that the above expression is evaluated as 8
/ (4 / 2)
. Here is a diagram showing how the foldr_start_with_parser
-based
solution works:
To make it easier to compare the two solutions, here is a diagram showing the two approaches side-by-side:
As we have seen, the associativity of the operators can be controlled by choosing between folding solutions. The folding solutions going from left to right implement left associativity, while the solutions going from right to left implement right associativity.
Note | |
---|---|
Note that folding solutions going from left to right is implemented in a more efficient way than folding from right to left. Therefore when both solutions can be used you should prefer folding from left to right. |