2
0
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:
djowel
2018-02-21 10:50:07 +08:00
parent f324a80d83
commit d98a785093
11 changed files with 564 additions and 3 deletions

View File

@@ -21,7 +21,6 @@
#include <iostream>
#include <string>
#include <complex>
namespace client { namespace ast
{

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

View 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

View 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

View 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

View 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);
}}

View 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

View 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

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