Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

11.3. Missing closing parens

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>;

copy-paste friendly version

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>;

copy-paste friendly version

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).


PrevUpHomeNext