• 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 annotate the AST with the iterator positions for access to the source
11 //  code when post processing. This example also shows how to "inject" client
12 //  data, using the "with" directive, that the handlers can access.
13 //
14 //  [ JDG May 9, 2007 ]
15 //  [ JDG May 13, 2015 ]    spirit X3
16 //  [ JDG Feb 22, 2018 ]    Parser annotations for spirit X3
17 //
18 //    I would like to thank Rainbowverse, llc (https://primeorbial.com/)
19 //    for sponsoring this work and donating it to the community.
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22 
23 #include <boost/config/warning_disable.hpp>
24 #include <boost/spirit/home/x3.hpp>
25 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
26 #include <boost/fusion/include/adapt_struct.hpp>
27 #include <boost/fusion/include/io.hpp>
28 
29 #include <iostream>
30 #include <string>
31 
32 namespace client { namespace ast
33 {
34     ///////////////////////////////////////////////////////////////////////////
35     //  Our AST (employee and person structs)
36     ///////////////////////////////////////////////////////////////////////////
37     namespace x3 = boost::spirit::x3;
38 
39     struct person : x3::position_tagged
40     {
personclient::ast::person41         person(
42             std::string const& first_name = ""
43           , std::string const& last_name = ""
44         )
45           : first_name(first_name)
46           , last_name(last_name)
47         {}
48 
49         std::string first_name, last_name;
50     };
51 
52     struct employee : x3::position_tagged
53     {
54         int age;
55         person who;
56         double salary;
57     };
58 
59     using boost::fusion::operator<<;
60 }}
61 
62 // We need to tell fusion about our employee struct
63 // to make it a first-class fusion citizen. This has to
64 // be in global scope.
65 
66 BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
67     first_name, last_name
68 )
69 
70 BOOST_FUSION_ADAPT_STRUCT(client::ast::employee,
71     age, who, salary
72 )
73 
74 namespace client
75 {
76     namespace parser
77     {
78         namespace x3 = boost::spirit::x3;
79         namespace ascii = boost::spirit::x3::ascii;
80 
81         ///////////////////////////////////////////////////////////////////////
82         //  Our annotation handler
83         ///////////////////////////////////////////////////////////////////////
84 
85         // tag used to get the position cache from the context
86         struct position_cache_tag;
87 
88         struct annotate_position
89         {
90             template <typename T, typename Iterator, typename Context>
on_successclient::parser::annotate_position91             inline void on_success(Iterator const& first, Iterator const& last
92             , T& ast, Context const& context)
93             {
94                 auto& position_cache = x3::get<position_cache_tag>(context).get();
95                 position_cache.annotate(ast, first, last);
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 : annotate_position {};
133         struct employee_class : annotate_position {};
134     }
135 }
136 
137 ///////////////////////////////////////////////////////////////////////////////
138 //  Main program
139 ///////////////////////////////////////////////////////////////////////////////
140 
141 ///////////////////////////////////////////////////////////////////////////////
142 // Our main parse entry point
143 ///////////////////////////////////////////////////////////////////////////////
144 
145 using iterator_type = std::string::const_iterator;
146 using position_cache = boost::spirit::x3::position_cache<std::vector<iterator_type>>;
147 
148 std::vector<client::ast::employee>
parse(std::string const & input,position_cache & positions)149 parse(std::string const& input, position_cache& positions)
150 {
151     using boost::spirit::x3::ascii::space;
152 
153     std::vector<client::ast::employee> ast;
154     iterator_type iter = input.begin();
155     iterator_type const end = input.end();
156 
157     using boost::spirit::x3::with;
158 
159     // Our parser
160     using client::parser::employees;
161     using client::parser::position_cache_tag;
162 
163     auto const parser =
164         // we pass our position_cache to the parser so we can access
165         // it later in our on_sucess handlers
166         with<position_cache_tag>(std::ref(positions))
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     return ast;
197 }
198 
199 // Sample input:
200 
201 std::string input = R"(
202 {
203     23,
204     "Amanda",
205     "Stefanski",
206     1000.99
207 },
208 {
209     35,
210     "Angie",
211     "Chilcote",
212     2000.99
213 },
214 {
215     43,
216     "Dannie",
217     "Dillinger",
218     3000.99
219 },
220 {
221     22,
222     "Dorene",
223     "Dole",
224     2500.99
225 },
226 {
227     38,
228     "Rossana",
229     "Rafferty",
230     5000.99
231 }
232 )";
233 
234 int
main()235 main()
236 {
237     position_cache positions{input.begin(), input.end()};
238     auto ast = parse(input, positions);
239 
240     // Get the source of the 2nd employee and print it
241     auto pos = positions.position_of(ast[1]); // zero based of course!
242     std::cout << "Here's the 2nd employee:" << std::endl;
243     std::cout << std::string(pos.begin(), pos.end()) << std::endl;
244     std::cout << "-------------------------\n";
245     return 0;
246 }
247