• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2002-2018 Joel de Guzman
3 
4     Distributed under the Boost Software License, Version 1.0. (See accompanying
5     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 =============================================================================*/
7 ///////////////////////////////////////////////////////////////////////////////
8 //
9 //  Based on the employee parser (see employee.cpp), this example shows how
10 //  to implement error handling. This example also shows how to "inject" client
11 //  data, using the "with" directive, that the handlers can access.
12 //
13 //  [ JDG May 9, 2007 ]
14 //  [ JDG May 13, 2015 ]    spirit X3
15 //  [ JDG Feb 19, 2018 ]    Error handling for spirit X3
16 //
17 //    I would like to thank Rainbowverse, llc (https://primeorbial.com/)
18 //    for sponsoring this work and donating it to the community.
19 //
20 ///////////////////////////////////////////////////////////////////////////////
21 
22 #include <boost/config/warning_disable.hpp>
23 #include <boost/spirit/home/x3.hpp>
24 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
25 #include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
26 #include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
27 #include <boost/fusion/include/adapt_struct.hpp>
28 #include <boost/fusion/include/io.hpp>
29 
30 #include <iostream>
31 #include <string>
32 
33 namespace client { namespace ast
34 {
35     ///////////////////////////////////////////////////////////////////////////
36     //  Our AST (employee and person structs)
37     ///////////////////////////////////////////////////////////////////////////
38     namespace x3 = boost::spirit::x3;
39 
40     struct person : x3::position_tagged
41     {
personclient::ast::person42         person(
43             std::string const& first_name = ""
44           , std::string const& last_name = ""
45         )
46           : first_name(first_name)
47           , last_name(last_name)
48         {}
49 
50         std::string first_name, last_name;
51     };
52 
53     struct employee : x3::position_tagged
54     {
55         int age;
56         person who;
57         double salary;
58     };
59 
60     using boost::fusion::operator<<;
61 }}
62 
63 // We need to tell fusion about our employee struct
64 // to make it a first-class fusion citizen. This has to
65 // be in global scope.
66 
67 BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
68     first_name, last_name
69 )
70 
71 BOOST_FUSION_ADAPT_STRUCT(client::ast::employee,
72     age, who, salary
73 )
74 
75 namespace client
76 {
77     namespace parser
78     {
79         namespace x3 = boost::spirit::x3;
80         namespace ascii = boost::spirit::x3::ascii;
81 
82         ///////////////////////////////////////////////////////////////////////
83         //  Our error handler
84         ///////////////////////////////////////////////////////////////////////
85         struct error_handler
86         {
87             template <typename Iterator, typename Exception, typename Context>
on_errorclient::parser::error_handler88             x3::error_handler_result on_error(
89                 Iterator& first, Iterator const& last
90               , Exception const& x, Context const& context)
91             {
92                 auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
93                 std::string message = "Error! Expecting: " + x.which() + " here:";
94                 error_handler(x.where(), message);
95                 return x3::error_handler_result::fail;
96             }
97         };
98 
99         ///////////////////////////////////////////////////////////////////////
100         //  Our employee parser
101         ///////////////////////////////////////////////////////////////////////
102 
103         using x3::int_;
104         using x3::double_;
105         using x3::lexeme;
106         using ascii::char_;
107 
108         struct quoted_string_class;
109         struct person_class;
110         struct employee_class;
111 
112         x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string";
113         x3::rule<person_class, ast::person> const person = "person";
114         x3::rule<employee_class, ast::employee> const employee = "employee";
115 
116         auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"'];
117         auto const person_def = quoted_string > ',' > quoted_string;
118 
119         auto const employee_def =
120                 '{'
121             >   int_ > ','
122             >   person > ','
123             >   double_
124             >   '}'
125             ;
126 
127         auto const employees = employee >> *(',' >> employee);
128 
129         BOOST_SPIRIT_DEFINE(quoted_string, person, employee);
130 
131         struct quoted_string_class {};
132         struct person_class : x3::annotate_on_success {};
133         struct employee_class : error_handler, x3::annotate_on_success {};
134     }
135 }
136 
137 ///////////////////////////////////////////////////////////////////////////////
138 //  Main program
139 ///////////////////////////////////////////////////////////////////////////////
140 
141 ///////////////////////////////////////////////////////////////////////////////
142 // Our main parse entry point
143 ///////////////////////////////////////////////////////////////////////////////
144 
parse(std::string const & input)145 void parse(std::string const& input)
146 {
147     using boost::spirit::x3::ascii::space;
148     typedef std::string::const_iterator iterator_type;
149 
150     std::vector<client::ast::employee> ast;
151     iterator_type iter = input.begin();
152     iterator_type const end = input.end();
153 
154     using boost::spirit::x3::with;
155     using boost::spirit::x3::error_handler_tag;
156     using error_handler_type = boost::spirit::x3::error_handler<iterator_type>;
157 
158     // Our error handler
159     error_handler_type error_handler(iter, end, std::cerr);
160 
161     // Our parser
162     using client::parser::employees;
163     auto const parser =
164         // we pass our error handler to the parser so we can access
165         // it later in our on_error and on_sucess handlers
166         with<error_handler_tag>(std::ref(error_handler))
167         [
168             employees
169         ];
170 
171     bool r = phrase_parse(iter, end, parser, space, ast);
172 
173     if (r && iter == end)
174     {
175         std::cout << boost::fusion::tuple_open('[');
176         std::cout << boost::fusion::tuple_close(']');
177         std::cout << boost::fusion::tuple_delimiter(", ");
178 
179         std::cout << "-------------------------\n";
180         std::cout << "Parsing succeeded\n";
181 
182         for (auto const& emp : ast)
183         {
184             std::cout << "got: " << emp << std::endl;
185         }
186         std::cout << "\n-------------------------\n";
187 
188     }
189     else
190     {
191         std::cout << "-------------------------\n";
192         std::cout << "Parsing failed\n";
193         std::cout << "-------------------------\n";
194         ast.clear();
195     }
196 }
197 
198 // Good sample:
199 
200 std::string good_input = R"(
201 {
202     23,
203     "Amanda",
204     "Stefanski",
205     1000.99
206 },
207 {
208     35,
209     "Angie",
210     "Chilcote",
211     2000.99
212 },
213 {
214     43,
215     "Dannie",
216     "Dillinger",
217     3000.99
218 },
219 {
220     22,
221     "Dorene",
222     "Dole",
223     2500.99
224 },
225 {
226     38,
227     "Rossana",
228     "Rafferty",
229     5000.99
230 }
231 )";
232 
233 // Input sample with error:
234 
235 std::string bad_input = R"(
236 {
237     23,
238     "Amanda",
239     "Stefanski",
240     1000.99
241 },
242 {
243     35,
244     "Angie",
245     "Chilcote",
246     2000.99
247 },
248 {
249     43,
250     'I am not a person!'    <--- this should be a person
251     3000.99
252 },
253 {
254     22,
255     "Dorene",
256     "Dole",
257     2500.99
258 },
259 {
260     38,
261     "Rossana",
262     "Rafferty",
263     5000.99
264 }
265 )";
266 
267 int
main()268 main()
269 {
270     // Good input
271     parse(good_input);
272 
273    // Bad input
274     std::cout << "Now we have some errors" << std::endl;
275     parse(bad_input);
276     return 0;
277 }
278