mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
It is better to manage warnings on our side to know what warnings we need to fix or suppress, and the only thing that header does is disabling deprecation warnings on MSVC and ICC which we would prefer to not show to users.
277 lines
7.2 KiB
C++
277 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/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;
|
|
}
|