278 lines
7.2 KiB
C++
278 lines
7.2 KiB
C++
/*=============================================================================
|
|
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 <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
|
|
///////////////////////////////////////////////////////////////////////
|
|
struct error_handler
|
|
{
|
|
template <typename Iterator, typename Exception, typename Context>
|
|
x3::error_handler_result on_error(
|
|
Iterator& first, Iterator const& last
|
|
, Exception const& x, Context const& context)
|
|
{
|
|
auto& error_handler = x3::get<x3::error_handler_tag>(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<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, 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<client::ast::employee> 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<iterator_type>;
|
|
|
|
// 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<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();
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|