/*============================================================================= 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 annotate the AST with the iterator positions for access to the source // code when post processing. 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 22, 2018 ] Parser annotations 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 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 annotation handler /////////////////////////////////////////////////////////////////////// // tag used to get the position cache from the context struct position_cache_tag; struct annotate_position { template inline void on_success(Iterator const& first, Iterator const& last , T& ast, Context const& context) { auto& position_cache = x3::get(context).get(); position_cache.annotate(ast, first, last); } }; /////////////////////////////////////////////////////////////////////// // 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 : annotate_position {}; struct employee_class : annotate_position {}; } } /////////////////////////////////////////////////////////////////////////////// // Main program /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Our main parse entry point /////////////////////////////////////////////////////////////////////////////// using iterator_type = std::string::const_iterator; using position_cache = boost::spirit::x3::position_cache>; std::vector parse(std::string const& input, position_cache& positions) { using boost::spirit::x3::ascii::space; std::vector ast; iterator_type iter = input.begin(); iterator_type const end = input.end(); using boost::spirit::x3::with; // Our parser using client::parser::employees; using client::parser::position_cache_tag; auto const parser = // we pass our position_cache to the parser so we can access // it later in our on_sucess handlers with(std::ref(positions)) [ 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(); } return ast; } // Sample input: std::string 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 } )"; int main() { position_cache positions{input.begin(), input.end()}; auto ast = parse(input, positions); // Get the source of the 2nd employee and print it auto pos = positions.position_of(ast[1]); // zero based of course! std::cout << "Here's the 2nd employee:" << std::endl; std::cout << std::string(pos.begin(), pos.end()) << std::endl; std::cout << "-------------------------\n"; return 0; }