rexpr.cpp 6.1 KB

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