1 /*=============================================================================
2 Copyright (c) 2001-2015 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 // A simple parser for X3 intended as a minimal starting point.
10 // 'rexpr' is a parser for a language resembling a minimal subset
11 // of json, but limited to a dictionary (composed of key=value pairs)
12 // where the value can itself be a string or a recursive dictionary.
13 //
14 // Example:
15 //
16 // {
17 // "color" = "blue"
18 // "size" = "29 cm."
19 // "position" = {
20 // "x" = "123"
21 // "y" = "456"
22 // }
23 // }
24 //
25 ///////////////////////////////////////////////////////////////////////////////
26
27 #include <boost/config/warning_disable.hpp>
28 #include <boost/spirit/home/x3.hpp>
29 #include <boost/spirit/home/x3/support/ast/variant.hpp>
30 #include <boost/fusion/include/adapt_struct.hpp>
31 #include <boost/fusion/include/std_pair.hpp>
32 #include <boost/fusion/include/io.hpp>
33
34 #include <iostream>
35 #include <fstream>
36 #include <string>
37 #include <map>
38
39 ///////////////////////////////////////////////////////////////////////////////
40 // Our AST
41 ///////////////////////////////////////////////////////////////////////////////
42 namespace client { namespace ast
43 {
44 namespace fusion = boost::fusion;
45 namespace x3 = boost::spirit::x3;
46
47 struct rexpr;
48
49 struct rexpr_value : x3::variant<
50 std::string
51 , x3::forward_ast<rexpr>
52 >
53 {
54 using base_type::base_type;
55 using base_type::operator=;
56 };
57
58 typedef std::map<std::string, rexpr_value> rexpr_map;
59 typedef std::pair<std::string, rexpr_value> rexpr_key_value;
60
61 struct rexpr
62 {
63 rexpr_map entries;
64 };
65 }}
66
67 // We need to tell fusion about our rexpr struct
68 // to make it a first-class fusion citizen
69 BOOST_FUSION_ADAPT_STRUCT(client::ast::rexpr,
70 entries
71 )
72
73 ///////////////////////////////////////////////////////////////////////////////
74 // AST processing
75 ///////////////////////////////////////////////////////////////////////////////
76 namespace client { namespace ast
77 {
78 ///////////////////////////////////////////////////////////////////////////
79 // Print out the rexpr tree
80 ///////////////////////////////////////////////////////////////////////////
81 int const tabsize = 4;
82
83 struct rexpr_printer
84 {
85 typedef void result_type;
86
rexpr_printerclient::ast::rexpr_printer87 rexpr_printer(int indent = 0)
88 : indent(indent) {}
89
operator ()client::ast::rexpr_printer90 void operator()(rexpr const& ast) const
91 {
92 std::cout << '{' << std::endl;
93 for (auto const& entry : ast.entries)
94 {
95 tab(indent+tabsize);
96 std::cout << '"' << entry.first << "\" = ";
97 boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second);
98 }
99 tab(indent);
100 std::cout << '}' << std::endl;
101 }
102
operator ()client::ast::rexpr_printer103 void operator()(std::string const& text) const
104 {
105 std::cout << '"' << text << '"' << std::endl;
106 }
107
tabclient::ast::rexpr_printer108 void tab(int spaces) const
109 {
110 for (int i = 0; i < spaces; ++i)
111 std::cout << ' ';
112 }
113
114 int indent;
115 };
116 }}
117
118 ///////////////////////////////////////////////////////////////////////////////
119 // Our rexpr grammar
120 ///////////////////////////////////////////////////////////////////////////////
121 namespace client { namespace parser
122 {
123 namespace x3 = boost::spirit::x3;
124 namespace ascii = boost::spirit::x3::ascii;
125
126 using x3::lit;
127 using x3::lexeme;
128
129 using ascii::char_;
130 using ascii::string;
131
132 x3::rule<class rexpr_value, ast::rexpr_value>
133 rexpr_value = "rexpr_value";
134
135 x3::rule<class rexpr, ast::rexpr>
136 rexpr = "rexpr";
137
138 x3::rule<class rexpr_key_value, ast::rexpr_key_value>
139 rexpr_key_value = "rexpr_key_value";
140
141 auto const quoted_string =
142 lexeme['"' >> *(char_ - '"') >> '"'];
143
144 auto const rexpr_value_def =
145 quoted_string | rexpr;
146
147 auto const rexpr_key_value_def =
148 quoted_string >> '=' >> rexpr_value;
149
150 auto const rexpr_def =
151 '{' >> *rexpr_key_value >> '}';
152
153 BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value);
154 }}
155
156 ///////////////////////////////////////////////////////////////////////////////
157 // Main program
158 ///////////////////////////////////////////////////////////////////////////////
main(int argc,char ** argv)159 int main(int argc, char **argv)
160 {
161 char const* filename;
162 if (argc > 1)
163 {
164 filename = argv[1];
165 }
166 else
167 {
168 std::cerr << "Error: No input file provided." << std::endl;
169 return 1;
170 }
171
172 std::ifstream in(filename, std::ios_base::in);
173
174 if (!in)
175 {
176 std::cerr << "Error: Could not open input file: "
177 << filename << std::endl;
178 return 1;
179 }
180
181 std::string storage; // We will read the contents here.
182 in.unsetf(std::ios::skipws); // No white space skipping!
183 std::copy(
184 std::istream_iterator<char>(in),
185 std::istream_iterator<char>(),
186 std::back_inserter(storage));
187
188 using client::parser::rexpr; // Our grammar
189 client::ast::rexpr ast; // Our tree
190
191 using boost::spirit::x3::ascii::space;
192 std::string::const_iterator iter = storage.begin();
193 std::string::const_iterator end = storage.end();
194 bool r = phrase_parse(iter, end, rexpr, space, ast);
195
196 if (r && iter == end)
197 {
198 std::cout << "-------------------------\n";
199 std::cout << "Parsing succeeded\n";
200 std::cout << "-------------------------\n";
201 client::ast::rexpr_printer printer;
202 printer(ast);
203 return 0;
204 }
205 else
206 {
207 std::string::const_iterator some = iter+30;
208 std::string context(iter, (some>end)?end:some);
209 std::cout << "-------------------------\n";
210 std::cout << "Parsing failed\n";
211 std::cout << "stopped at: \": " << context << "...\"\n";
212 std::cout << "-------------------------\n";
213 return 1;
214 }
215 }
216