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