mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
Merge branch 'Some-Tutorials' into develop
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <complex>
|
||||
|
||||
namespace client { namespace ast
|
||||
{
|
||||
|
||||
303
example/x3/error_handling.cpp
Normal file
303
example/x3/error_handling.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/*=============================================================================
|
||||
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 and annotation of the AST with the iterator
|
||||
// positions for access to the source code when post processing.
|
||||
//
|
||||
// [ 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 <boost/config/warning_disable.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
|
||||
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
|
||||
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/fusion/include/io.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
template <typename Iterator>
|
||||
using error_handler = x3::error_handler<Iterator>;
|
||||
|
||||
// tag used to get our error handler from the context
|
||||
using error_handler_tag = x3::error_handler_tag;
|
||||
|
||||
struct error_handler_base
|
||||
{
|
||||
template <typename Iterator, typename Exception, typename Context>
|
||||
x3::error_handler_result on_error(
|
||||
Iterator& first, Iterator const& last
|
||||
, Exception const& x, Context const& context)
|
||||
{
|
||||
std::string message = "Error! Expecting: " + x.which() + " here:";
|
||||
auto& error_handler = x3::get<error_handler_tag>(context).get();
|
||||
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<quoted_string_class, std::string> const quoted_string = "quoted_string";
|
||||
x3::rule<person_class, ast::person> const person = "person";
|
||||
x3::rule<employee_class, ast::employee> 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_base, x3::annotate_on_success {};
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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
|
||||
}
|
||||
)";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Our main parse entry point
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef std::string::const_iterator iterator_type;
|
||||
using position_cache = boost::spirit::x3::position_cache<std::vector<iterator_type>>;
|
||||
|
||||
// We return the AST as well as the position cache (for access to the source
|
||||
// when post processing).
|
||||
struct parse_result
|
||||
{
|
||||
std::vector<client::ast::employee> ast;
|
||||
position_cache positions;
|
||||
};
|
||||
|
||||
parse_result parse(std::string const& input)
|
||||
{
|
||||
using boost::spirit::x3::ascii::space;
|
||||
|
||||
std::vector<client::ast::employee> ast;
|
||||
iterator_type iter = input.begin();
|
||||
iterator_type const end = input.end();
|
||||
|
||||
using boost::spirit::x3::with;
|
||||
using error_handler_type = client::parser::error_handler<iterator_type>;
|
||||
using client::parser::error_handler_tag;
|
||||
|
||||
// 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 on in our on_error and on_sucess handlers
|
||||
with<error_handler_tag>(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();
|
||||
}
|
||||
return { ast, error_handler.get_position_cache() };
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
// Good input
|
||||
auto result = parse(good_input);
|
||||
|
||||
auto const& positions = result.positions;
|
||||
auto const& ast = result.ast;
|
||||
|
||||
// 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";
|
||||
|
||||
// Bad input
|
||||
std::cout << "Now we have some errors" << std::endl;
|
||||
parse(bad_input);
|
||||
return 0;
|
||||
}
|
||||
31
example/x3/minimal/ast.hpp
Normal file
31
example/x3/minimal/ast.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*=============================================================================
|
||||
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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_MINIMAL_AST_HPP)
|
||||
#define BOOST_SPIRIT_X3_MINIMAL_AST_HPP
|
||||
|
||||
#include <boost/fusion/include/io.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace client { namespace ast
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Our employee AST struct
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
struct employee
|
||||
{
|
||||
int age;
|
||||
std::string surname;
|
||||
std::string forename;
|
||||
double salary;
|
||||
};
|
||||
|
||||
using boost::fusion::operator<<;
|
||||
}}
|
||||
|
||||
#endif
|
||||
21
example/x3/minimal/ast_adapted.hpp
Normal file
21
example/x3/minimal/ast_adapted.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/*=============================================================================
|
||||
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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_MINIMAL_AST_ADAPTED_HPP)
|
||||
#define BOOST_SPIRIT_X3_MINIMAL_AST_ADAPTED_HPP
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include "ast.hpp"
|
||||
|
||||
// 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::employee,
|
||||
age, surname, forename, salary
|
||||
)
|
||||
|
||||
#endif
|
||||
20
example/x3/minimal/config.hpp
Normal file
20
example/x3/minimal/config.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_MINIMAL_CONFIG_HPP)
|
||||
#define BOOST_SPIRIT_X3_MINIMAL_CONFIG_HPP
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
namespace client { namespace parser
|
||||
{
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
using iterator_type = std::string::const_iterator;
|
||||
using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
|
||||
}}
|
||||
|
||||
#endif
|
||||
13
example/x3/minimal/employee.cpp
Normal file
13
example/x3/minimal/employee.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2001-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)
|
||||
=============================================================================*/
|
||||
#include "employee_def.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
namespace client { namespace parser
|
||||
{
|
||||
BOOST_SPIRIT_INSTANTIATE(employee_type, iterator_type, context_type);
|
||||
}}
|
||||
30
example/x3/minimal/employee.hpp
Normal file
30
example/x3/minimal/employee.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*=============================================================================
|
||||
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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_HPP)
|
||||
#define BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_HPP
|
||||
|
||||
#include <boost/config/warning_disable.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include "ast.hpp"
|
||||
|
||||
namespace client
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Our employee parser declaration
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace parser
|
||||
{
|
||||
namespace x3 = boost::spirit::x3;
|
||||
using employee_type = x3::rule<class employee, ast::employee>;
|
||||
BOOST_SPIRIT_DECLARE(employee_type);
|
||||
}
|
||||
|
||||
parser::employee_type employee();
|
||||
}
|
||||
|
||||
#endif
|
||||
56
example/x3/minimal/employee_def.hpp
Normal file
56
example/x3/minimal/employee_def.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*=============================================================================
|
||||
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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_DEF_HPP)
|
||||
#define BOOST_SPIRIT_X3_MINIMAL_EMPLOYEE_DEF_HPP
|
||||
|
||||
#include <boost/config/warning_disable.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include "ast.hpp"
|
||||
#include "ast_adapted.hpp"
|
||||
#include "employee.hpp"
|
||||
|
||||
namespace client
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Our employee parser definition
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace parser
|
||||
{
|
||||
namespace x3 = boost::spirit::x3;
|
||||
namespace ascii = boost::spirit::x3::ascii;
|
||||
|
||||
using x3::int_;
|
||||
using x3::lit;
|
||||
using x3::double_;
|
||||
using x3::lexeme;
|
||||
using ascii::char_;
|
||||
|
||||
x3::rule<class employee, ast::employee> const employee = "employee";
|
||||
|
||||
auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"'];
|
||||
|
||||
auto const employee_def =
|
||||
lit("employee")
|
||||
>> '{'
|
||||
>> int_ >> ','
|
||||
>> quoted_string >> ','
|
||||
>> quoted_string >> ','
|
||||
>> double_
|
||||
>> '}'
|
||||
;
|
||||
|
||||
BOOST_SPIRIT_DEFINE(employee);
|
||||
}
|
||||
|
||||
parser::employee_type employee()
|
||||
{
|
||||
return parser::employee;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
79
example/x3/minimal/main.cpp
Normal file
79
example/x3/minimal/main.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
/*=============================================================================
|
||||
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)
|
||||
=============================================================================*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This is the same employee parser (see employee.cpp) but structured to
|
||||
// allow separate compilation of the actual parser in its own definition
|
||||
// file (employee_def.hpp) and cpp file (employee.cpp). This main cpp file
|
||||
// sees only the header file (employee.hpp). This is a good example on how
|
||||
// parsers are structured in a C++ application.
|
||||
//
|
||||
// [ JDG May 9, 2007 ]
|
||||
// [ JDG May 13, 2015 ] spirit X3
|
||||
// [ JDG Feb 20, 2018 ] Minimal "best practice" example
|
||||
//
|
||||
// I would like to thank Rainbowverse, llc (https://primeorbial.com/)
|
||||
// for sponsoring this work and donating it to the community.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "ast.hpp"
|
||||
#include "ast_adapted.hpp"
|
||||
#include "employee.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int
|
||||
main()
|
||||
{
|
||||
std::cout << "/////////////////////////////////////////////////////////\n\n";
|
||||
std::cout << "\t\tAn employee parser for Spirit...\n\n";
|
||||
std::cout << "/////////////////////////////////////////////////////////\n\n";
|
||||
|
||||
std::cout
|
||||
<< "Give me an employee of the form :"
|
||||
<< "employee{age, \"surname\", \"forename\", salary } \n";
|
||||
std::cout << "Type [q or Q] to quit\n\n";
|
||||
|
||||
using boost::spirit::x3::ascii::space;
|
||||
using iterator_type = std::string::const_iterator;
|
||||
using client::employee;
|
||||
|
||||
std::string str;
|
||||
while (getline(std::cin, str))
|
||||
{
|
||||
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
|
||||
break;
|
||||
|
||||
client::ast::employee emp;
|
||||
iterator_type iter = str.begin();
|
||||
iterator_type const end = str.end();
|
||||
bool r = phrase_parse(iter, end, employee(), space, emp);
|
||||
|
||||
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";
|
||||
std::cout << "got: " << emp << std::endl;
|
||||
std::cout << "\n-------------------------\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "-------------------------\n";
|
||||
std::cout << "Parsing failed\n";
|
||||
std::cout << "-------------------------\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Bye... :-) \n\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <boost/range.hpp>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/core/enable_if.hpp>
|
||||
|
||||
namespace boost { namespace spirit { namespace x3
|
||||
{
|
||||
@@ -46,13 +47,16 @@ namespace boost { namespace spirit { namespace x3
|
||||
|
||||
// This will catch all nodes except those inheriting from position_tagged
|
||||
template <typename AST>
|
||||
boost::iterator_range<iterator_type>
|
||||
typename boost::enable_if_c<
|
||||
(!is_base_of<position_tagged, AST>::value)
|
||||
, boost::iterator_range<iterator_type>
|
||||
>::type
|
||||
position_of(AST const& ast) const
|
||||
{
|
||||
// returns an empty position
|
||||
return boost::iterator_range<iterator_type>();
|
||||
}
|
||||
|
||||
|
||||
// This will catch all nodes except those inheriting from position_tagged
|
||||
template <typename AST>
|
||||
void annotate(AST& ast, iterator_type first, iterator_type last, mpl::false_)
|
||||
|
||||
@@ -58,6 +58,11 @@ namespace boost { namespace spirit { namespace x3
|
||||
return pos_cache.position_of(pos);
|
||||
}
|
||||
|
||||
position_cache<std::vector<Iterator>> const& get_position_cache() const
|
||||
{
|
||||
return pos_cache;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void print_file_line(std::size_t line) const;
|
||||
|
||||
Reference in New Issue
Block a user