Nov 4
Boost Spirit III (Adding Error Handling)
This article expands the example from the previous two, here and here, to add more specific error information.
As always the full code from the example is available here here.
The Problem
In our previous example an error simply resulted in a generic error message and a printout of the location at which the error occurred.
Ideally we should provide some indication of what we were expecting to get.
The Error Enumeration
The first step is to define an enumeration containing the error types that may be encountered. In this case I have wrapped the enumeration in a struct so that:
- The enumeration values are not stuck into the global namespace
- I can add a static method for converting a value to an error message
:
struct error{ enum _error{ NONE, UNTERMINATED_QUOTE, MISSING_OPEN_BRACKET, MISSING_CLOSE_BRACKET, MISSING_RECORD_NAME, MISSING_EQUAL, MISSING_PROPERTY_VALUE }; static const char *message(_rec_parse_error e){ ... } }; typedef error::_error error_t;
Defining Assertions
To define where these errors can occur in the grammar you will need to define an assertion for each error. You can define them anywhere, but since they require a constructor argument I did not make them member variables like a rules, instead I defined them on the stack in the definition struct constructor:
struct record_grammar:public grammar<record_grammar>{ ... template<class ScannerT> struct definition{ ... typedef assertion<error_t> my_assertion_t; definition(const record_grammar &self){ my_assertion_t expect_quote(error::UNTERMINATED_QUOTE); my_assertion_t expect_open_bracket(error::MISSING_OPEN_BRACKET); my_assertion_t expect_close_bracket(error::MISSING_CLOSE_BRACKET); my_assertion_t expect_name(error::MISSING_RECORD_NAME); my_assertion_t expect_equal(error::MISSING_EQUAL); my_assertion_t expect_property_value(error::MISSING_PROPERTY_VALUE); ... } ... }; ... };
Attaching Assertions to the Grammar
Once you’ve defined your assertions you use them to wrap portions of the grammar that, if absent, will cause the error to occur. For instance, in order to specify that a quoted string must be terminated by a closing quote:
string_literal = lexeme_d[ ch_p('"') >> *( (anychar_p - ch_p('"') ) | str_p("\\\"") ) >> expect_quote( ch_p('"') ) ];
Processing Errors Using Guard
In order to actually catch and handle an error, you use the guard class. In theory you can have more than one, but in this case a top-level catch all seems appropriate:
definition(const record_grammar &self){ ... guard<error_t> my_guard; ... record = my_guard( ... )[handle_error(self.error)]; };
The Guard Callback
An annoying part of the error mechanism is that the error callback must take ScannerT as an argument. Since this is a template argument that means the callback must be templatized which in turn means it cannot be a function2 object.
So instead I defined the following functor which will translate the error into a callback taking :
void (error_t, const char *)
typedef function2<void, error_t, const char *> error_cb; ... template<class ScannerT> struct definition{ ... struct handle_error{ handle_error(error_cb &cb):cb(cb){ }; error_status<> operator()(ScannerT const &scan, parser_error<error_t, const char*> error) const{ cb(error.descriptor, error.where); return error_status<>(); } error_cb &cb; }; ... }
Now we can use this functor as follows:
struct record_grammar:public grammar<record_grammar>{ ... template<class ScannerT> struct definition{ definition(const record_grammar &self){ ... record = my_guard( ... )[handle_error(self.error)]; ... } ... }; ... };
Closing Notes
One thing I wanted to get into in this post was recovering from parse errors. Most grammar parsers allow you to continue after a parse error and spirit appears to support at least the concept.
Unfortunately, although the error_status<> struct has the option to specify a retry, the documentation doesn’t provide any information on how to specify where the parser should pick back up or in what state.
Anyway, If I figure it out I’ll update the article.
Michael Smit is a software engineer in Seattle, Washington who works for amazonThe Author
1 Comment so far
Leave a comment
[...] information on improving the error messages for your grammar take a look at the next article. The AuthorMichael Smit is a software engineer in Maryland who currently develops software for [...]