/*============================================================================= Copyright (c) 2002-2018 Joel de Guzman 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) =============================================================================*/ /////////////////////////////////////////////////////////////////////////////// // // Based on the employee parser (see employee.cpp), this example shows how // to implement error handling. This example also shows how to "inject" client // data, using the "with" directive, that the handlers can access. // // [ JDG May 9, 2007 ] // [ JDG May 13, 2015 ] spirit X3 // [ JDG Feb 19, 2018 ] Error handling for spirit X3 // // I would like to thank Rainbowverse, llc (https://primeorbial.com/) // for sponsoring this work and donating it to the community. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include namespace client { namespace ast { /////////////////////////////////////////////////////////////////////////// // Our AST (employee and person structs) /////////////////////////////////////////////////////////////////////////// namespace x3 = boost::spirit::x3; struct person : x3::position_tagged { person( std::string const& first_name = "" , std::string const& last_name = "" ) : first_name(first_name) , last_name(last_name) {} std::string first_name, last_name; }; struct employee : x3::position_tagged { int age; person who; double salary; }; using boost::fusion::operator<<; }} // We need to tell fusion about our employee struct // to make it a first-class fusion citizen. This has to // be in global scope. BOOST_FUSION_ADAPT_STRUCT(client::ast::person, first_name, last_name ) BOOST_FUSION_ADAPT_STRUCT(client::ast::employee, age, who, salary ) namespace client { namespace parser { namespace x3 = boost::spirit::x3; namespace ascii = boost::spirit::x3::ascii; /////////////////////////////////////////////////////////////////////// // Our error handler /////////////////////////////////////////////////////////////////////// struct error_handler { template x3::error_handler_result on_error( Iterator& first, Iterator const& last , Exception const& x, Context const& context) { auto& error_handler = x3::get(context).get(); std::string message = "Error! Expecting: " + x.which() + " here:"; error_handler(x.where(), message); return x3::error_handler_result::fail; } }; /////////////////////////////////////////////////////////////////////// // Our employee parser /////////////////////////////////////////////////////////////////////// using x3::int_; using x3::double_; using x3::lexeme; using ascii::char_; struct quoted_string_class; struct person_class; struct employee_class; x3::rule const quoted_string = "quoted_string"; x3::rule const person = "person"; x3::rule const employee = "employee"; auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"']; auto const person_def = quoted_string > ',' > quoted_string; auto const employee_def = '{' > int_ > ',' > person > ',' > double_ > '}' ; auto const employees = employee >> *(',' >> employee); BOOST_SPIRIT_DEFINE(quoted_string, person, employee); struct quoted_string_class {}; struct person_class : x3::annotate_on_success {}; struct employee_class : error_handler, x3::annotate_on_success {}; } } /////////////////////////////////////////////////////////////////////////////// // Main program /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Our main parse entry point /////////////////////////////////////////////////////////////////////////////// void parse(std::string const& input) { using boost::spirit::x3::ascii::space; typedef std::string::const_iterator iterator_type; std::vector ast; iterator_type iter = input.begin(); iterator_type const end = input.end(); using boost::spirit::x3::with; using boost::spirit::x3::error_handler_tag; using error_handler_type = boost::spirit::x3::error_handler; // Our error handler error_handler_type error_handler(iter, end, std::cerr); // Our parser using client::parser::employees; auto const parser = // we pass our error handler to the parser so we can access // it later in our on_error and on_sucess handlers with(std::ref(error_handler)) [ employees ]; bool r = phrase_parse(iter, end, parser, space, ast); if (r && iter == end) { std::cout << boost::fusion::tuple_open('['); std::cout << boost::fusion::tuple_close(']'); std::cout << boost::fusion::tuple_delimiter(", "); std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; for (auto const& emp : ast) { std::cout << "got: " << emp << std::endl; } std::cout << "\n-------------------------\n"; } else { std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "-------------------------\n"; ast.clear(); } } // Good sample: std::string good_input = R"( { 23, "Amanda", "Stefanski", 1000.99 }, { 35, "Angie", "Chilcote", 2000.99 }, { 43, "Dannie", "Dillinger", 3000.99 }, { 22, "Dorene", "Dole", 2500.99 }, { 38, "Rossana", "Rafferty", 5000.99 } )"; // Input sample with error: std::string bad_input = R"( { 23, "Amanda", "Stefanski", 1000.99 }, { 35, "Angie", "Chilcote", 2000.99 }, { 43, 'I am not a person!' <--- this should be a person 3000.99 }, { 22, "Dorene", "Dole", 2500.99 }, { 38, "Rossana", "Rafferty", 5000.99 } )"; int main() { // Good input parse(good_input); // Bad input std::cout << "Now we have some errors" << std::endl; parse(bad_input); return 0; }