config_file_grammar.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Config Class
  2. // Author: Charles Gruenwald III
  3. // #define BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT 2
  4. #include "config_file.hpp"
  5. // #define BOOST_SPIRIT_DEBUG // define this for debug output
  6. #include <boost/version.hpp>
  7. #if (BOOST_VERSION==103500)
  8. # include <boost/spirit/core.hpp>
  9. # include <boost/spirit/tree/parse_tree.hpp>
  10. # include <boost/spirit/tree/ast.hpp>
  11. # include <boost/spirit/utility/confix.hpp>
  12. # include <boost/spirit/utility/escape_char.hpp>
  13. # include <boost/spirit/utility/chset.hpp>
  14. #else
  15. # include <boost/spirit/include/classic_core.hpp>
  16. # include <boost/spirit/include/classic_parse_tree.hpp>
  17. # include <boost/spirit/include/classic_ast.hpp>
  18. # include <boost/spirit/include/classic_confix.hpp>
  19. # include <boost/spirit/include/classic_escape_char.hpp>
  20. # include <boost/spirit/include/classic_chset.hpp>
  21. #endif
  22. #include <iostream>
  23. /*! \file config_file_grammar.hpp
  24. * This file represents the parser element of the bl::config::ConfigFile interface.
  25. * It uses boost:spirit to create an AST of the loaded config text, then simply
  26. * walks that tree adding the appropriate sections and keys to the ConfigFile class.
  27. */
  28. namespace config
  29. {
  30. #if (BOOST_VERSION==103500)
  31. using namespace boost::spirit;
  32. #else
  33. using namespace boost::spirit::classic;
  34. #endif
  35. enum RuleID { defaultID, sectionID, keyNameID, keyValueID, keySeparatorID, keyValueArrayID, keyValueSpanID, keyID, sectionNameID, stringID, configID };
  36. // Like int_p, but support up to SInt64
  37. typedef int_parser<SInt64, 10, 1, -1> long_parser_t;
  38. static long_parser_t long_p;
  39. struct config_parser : public grammar<config_parser> {
  40. // A utility function for tagging each node with an 'ID' for identification
  41. // when we walk the parse tree.
  42. struct Name {
  43. Name(RuleID e_) : e(e_) { }
  44. template<typename Node, typename It>
  45. void operator()(Node& n, It, It) const {
  46. n.value.value(e);
  47. }
  48. RuleID e;
  49. };
  50. // The actual node structure in the tree, we simply need an id in this structure
  51. // as the strings are provided by the container class provided by spirit.
  52. struct NodeValue {
  53. RuleID e;
  54. NodeValue() : e((RuleID)0) {}
  55. NodeValue(RuleID e_) : e(e_) {}
  56. };
  57. void show_match(const char *begin, const char*end)
  58. {
  59. std::cout << "Matched: " << String(begin, end-begin) << std::endl;
  60. }
  61. typedef node_iter_data_factory<NodeValue> factory_t;
  62. typedef const char* iterator_t;
  63. template <typename ScannerT>
  64. struct definition {
  65. definition(config_parser const& self) {
  66. //Grammar Definition
  67. // Complex rules
  68. r_file = r_config_node >> end_p;
  69. r_config_node = r_config;
  70. r_config = *r_section_node ;
  71. r_section_node = access_node_d[ r_section ][Name(sectionNameID)];
  72. r_section = root_node_d[r_section_name_node] >> *r_key_node ;
  73. r_key_node = access_node_d[ r_key ][Name(keyID)];
  74. r_key = r_key_name
  75. >> lexeme_d[ no_node_d[ *r_key_array ] >> no_node_d[ *space_p ] >> root_node_d[ ch_p('=') ] >> no_node_d[ *space_p ] ]
  76. >> !r_value ;
  77. r_key_name = access_node_d[ !r_string ][Name(keyNameID)];
  78. r_key_array = ch_p('[') >> ch_p(']');
  79. r_value_array = access_node_d[ r_value_array2 ][Name(keyValueArrayID)];
  80. r_value_array2 = r_value_single_or_empty >> +(access_node_d[ ch_p(',') ][Name(keySeparatorID)] >> r_value_single_or_empty);
  81. r_value_span = access_node_d[ r_value_long >> root_node_d[ ch_p('-') ] >> r_value_long ][Name(keyValueSpanID)];
  82. r_value_long = access_node_d[ long_p ][Name(keyValueID)];
  83. r_value_single = access_node_d[ r_string ][Name(keyValueID)];
  84. r_value_single_or_empty = r_value_single | !r_value_single;
  85. r_value = r_value_array | r_value_span | r_value_single;
  86. // Terminals
  87. // Note, the lexeme directive tells the parser to act character-by-character (don't skip spaces),
  88. // the token_node_d says to make 1 node out of the matched results (instead of 1 node per character)
  89. // inner_node directive states to throw away each of the '[' and ']'
  90. // finally config_p looks for matching [ with ]
  91. r_section_name_node = access_node_d[ r_section_name ][Name(sectionNameID)] ;
  92. //HACK:
  93. //
  94. //This is a total hack below. Because we are using Boost 1.34, we cannot use the gen_pt_node_d
  95. //in certain places (notably the one commented below). The gen_pt_node_d function allows us to
  96. //generate an AST node, even if there is no data contained within the node. This happens when we
  97. //get a section that doesn't have a name in it (i.e. []). In Boost 1.35 we can use the following
  98. //gent_pt_node_d with the inner_node_d to eliminate the braces at parse-time. Instead until then
  99. //we have to leave the braces in due to null-node-culling and strip them at the time we walk the
  100. //tree.
  101. r_section_name = lexeme_d[ token_node_d[ confix_p('[', (*lex_escape_ch_p), ']') ] ] ;
  102. // Strings and names may either be "quoted" or not.
  103. r_string =
  104. (
  105. ( no_node_d[ *space_p ] >> lexeme_d[ token_node_d[ inner_node_d[ confix_p('"', *lex_escape_ch_p, '"') ] ] ] >> no_node_d[ *space_p ] )
  106. |
  107. ( lexeme_d[ token_node_d[ +(alnum_p | (punct_p - ch_p('=') - ch_p(',') - ch_p('[') - ch_p(']') ) ) ] ] )
  108. )
  109. ;
  110. }
  111. rule<ScannerT> r_file, r_config, r_config_node, r_section, r_key, r_key_name, r_key_array, r_value, r_value_long, r_value_single, r_value_single_or_empty, r_value_array, r_value_array2, r_value_span, r_key_node, r_section_node, r_section_name, r_section_name_node, r_section_name_node_node, r_string;
  112. rule<ScannerT> const&
  113. start() const { return r_file; }
  114. };
  115. };
  116. typedef tree_parse_info<config_parser::iterator_t, config_parser::factory_t> parse_info_t;
  117. typedef tree_match<config_parser::iterator_t, config_parser::factory_t> tree_match_t;
  118. typedef tree_match_t::node_t node_t;
  119. typedef tree_match_t::const_tree_iterator tree_iter_t;
  120. } //namespaces