Home | Libraries | People | FAQ | More |
Missing closing parens are common errors. Let's see how our parsers report them:
> exp_parser20::apply<BOOST_METAPARSE_STRING("(1+2")>::type << compilation error >> ..... x__________________PARSING_FAILED__________________x<1, 5, unpaired<1, 1, literal_expected<')'>>> .... << compilation error >>
The parser could detect that there is a missing paren and the error report points to the open paren which is not closed. This looks great, but we are not done yet. Let's try a slightly more complex input:
> exp_parser20::apply<BOOST_METAPARSE_STRING("0+(1+2")>::type mpl_::integral_c<int, 0>
This is getting strange now. We parse the +
<mult_exp>
elements using foldl_start_with_parser
(see the
definition of plus_exp3
).
foldl_start_with_parser
parses the input as long as it can and stops when it fails to parse it.
In the above input, it parses 0
as the initial element and then it tries to parse the first + <mult_exp>
element. But parsing the <mult_exp>
part fails because of the missing closing
paren. So foldl_start_with_parser
stops and ignores this failing part of the input.
The result of the above is that we parse only the 0
part of the input, ignore the "garbage" at the end and assume
that the value of the expression is 0
.
This could be fixed by using entire_input
. Our parser would
reject the input (because of the "garbage" at the end), but the
error message would not be useful. So we take a different approach.
When foldl_start_with_parser
stops, we should check if there is an extra broken +
<mult_exp>
there or not. When there is, we should
report what is wrong with that broken +
<mult_exp>
(eg. a missing closing paren). Metaparse
provides fail_at_first_char_expected
to implement such validations. fail_at_first_char_expected
<parser>
checks how parser
fails to parse the input: when it fails right at the first character,
fail_at_first_char_expected
assumes that there is no garbage and accepts the input. When parser
consumes characters from the input
before failing, fail_at_first_char_expected
assumes
that there is a broken expression and propagates the error. It can be used
the following way:
> #include <boost/metaparse/fail_at_first_char_expected.hpp> > #include <boost/metaparse/first_of.hpp> > struct plus_exp4 : \ ...> first_of< \ ...> foldl_start_with_parser< \ ...> sequence<one_of<plus_token, minus_token>, mult_exp6>, \ ...> mult_exp6, \ ...> boost::mpl::quote2<binary_op> \ ...> >, \ ...> fail_at_first_char_expected< \ ...> sequence<one_of<plus_token, minus_token>, mult_exp6> \ ...> > \ ...> > {}; > using exp_parser21 = build_parser<plus_exp4>;
first_of
is similar to middle_of
,
but keeps the result of the first element, not the middle one. We use it
to keep the "real" result (the result of foldl_start_with_parser
) and to
throw the dummy result coming from fail_at_first_char_expected
away
when there is no broken expression at the end. first_of
propagates any error
coming from fail_at_first_char_expected
.
Let's try this new expression parser out with a missing closing paren:
> exp_parser21::apply<BOOST_METAPARSE_STRING("0+(1+2")>::type << compilation error >> ..... x__________________PARSING_FAILED__________________x<1, 7, unpaired<1, 3, literal_expected<')'>>> .... << compilation error >>
This works as expected now: it tells us that there is a missing paren and it points us the open paren which is not closed.
Our parser provides useful error messages for missing closing parens,
however, the implementation of the parser (plus_exp4
)
is long and repetitive: it contains the parser for the repeated element
(sequence
<
one_of
<plus_token,
minus_token>,
mult_exp6>
)
twice, and that is not ideal.
plus_exp4
uses foldl_start_with_parser
to implement repetition. Metaparse provides foldl_reject_incomplete_start_with_parser
which does the same we did with first_of
, foldl_start_with_parser
and
fail_at_first_char_expected
together:
> #include <boost/metaparse/foldl_reject_incomplete_start_with_parser.hpp> > struct plus_exp5 : \ ...> foldl_reject_incomplete_start_with_parser< \ ...> sequence<one_of<plus_token, minus_token>, mult_exp6>, \ ...> mult_exp6, \ ...> boost::mpl::quote2<binary_op> \ ...> > {}; > using exp_parser22 = build_parser<plus_exp5>;
It parses the input using sequence
<
one_of
<plus_token,
minus_token>,
mult_exp6>
)
repeatedly. When it fails, foldl_reject_incomplete_start_with_parser
checks if it consumed any character before failing (the same as what
fail_at_first_char_expected
does), and if yes, then foldl_reject_incomplete_start_with_parser
fails.
This makes the implementation of the repetition with advanced error reporting simpler. Let's try it out:
> exp_parser22::apply<BOOST_METAPARSE_STRING("0+(1+2")>::type << compilation error >> ..... x__________________PARSING_FAILED__________________x<1, 7, unpaired<1, 3, literal_expected<')'>>> .... << compilation error >>
Note that other folding parsers have their f
versions as well (eg. foldr_reject_incomplete
, foldl_reject_incomplete1
,
etc).