Created
May 13, 2016 21:12
-
-
Save eskil/10614c61b9dd5823ed17da316f9cb834 to your computer and use it in GitHub Desktop.
.cfg parse in boost spirit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define BOOST_SPIRIT_DEBUG | |
#include <boost/spirit.hpp> | |
#include <boost/spirit/actor.hpp> | |
#include <string> | |
#include <map> | |
#include <algorithm> | |
using namespace std; | |
using namespace boost::spirit; | |
typedef pair<string, string> kv_t; | |
typedef vector<kv_t> kvs_t; | |
typedef map<string, kvs_t> cfg_map_t; | |
struct config_file_grammar : public grammar<config_file_grammar> { | |
config_file_grammar (cfg_map_t &cmap) | |
: config_map (cmap) | |
{ } | |
template <typename ScannerT> | |
struct definition { | |
definition(const config_file_grammar &self) | |
: cfg (self.config_map) | |
{ | |
odd_id = +(alnum_p | '_' | '-'); | |
comment = | |
comment_p ('#') | |
| comment_p (';'); | |
// By wrapping the assignment inside a lexeme directive, | |
// we can get initial space as well, ie. "key = 2" assigns | |
// " 2" to key. | |
val_assignment = lexeme_d[ch_p ('=') >> (*(anychar_p - '\n'))[assign_a (kv.second)]]; | |
section_decl = ('[' >> odd_id[assign_a (section)] >> ']'); | |
key_val_assignment = (odd_id[assign_a (kv.first)] >> val_assignment)[push_back_a (kvs, kv)]; | |
main_rule = | |
*(comment | |
| (section_decl | |
>> *(comment | |
| key_val_assignment | |
) | |
)[insert_at_a (cfg, section, kvs)][clear_a (kvs)] | |
); | |
// Just here in case you want to enable debug... | |
BOOST_SPIRIT_DEBUG_NODE (main_rule); | |
BOOST_SPIRIT_DEBUG_NODE (comment); | |
BOOST_SPIRIT_DEBUG_NODE (section_decl); | |
BOOST_SPIRIT_DEBUG_NODE (odd_id); | |
BOOST_SPIRIT_DEBUG_NODE (key_val_assignment); | |
BOOST_SPIRIT_DEBUG_NODE (val_assignment); | |
} | |
// Since rules are referred to by reference, they must live | |
// for the duration of anyone using them, hence declared in | |
// structure scope rather than insice definition. | |
rule<ScannerT> val_assignment, section_decl, comment; | |
rule<ScannerT> odd_id, key_val_assignment, main_rule; | |
cfg_map_t &cfg; | |
kvs_t kvs; | |
kv_t kv; | |
string section; | |
const rule<ScannerT>& start() const { return main_rule; } | |
}; | |
cfg_map_t &config_map; | |
}; | |
void print_cfg_kv (const kv_t &p) { | |
cout << p.first << "=\"" << p.second << '"' << endl; | |
} | |
void print_cfg_kvs (const kvs_t &l) { | |
for_each (l.begin (), l.end (), print_cfg_kv); | |
} | |
void print_cfg_section (const cfg_map_t::value_type &p) { | |
cout << '[' << p.first << ']' << endl; | |
print_cfg_kvs (p.second); | |
} | |
void print_cfg_map (const cfg_map_t &m) { | |
for_each (m.begin (), m.end (), print_cfg_section); | |
} | |
template<typename Iterator> | |
cfg_map_t parse_cfg (Iterator begin, Iterator end) { | |
cfg_map_t cfg; | |
config_file_grammar cfgfile (cfg); | |
BOOST_SPIRIT_DEBUG_NODE (cfgfile); | |
parse_info<Iterator> result = parse (begin, end, cfgfile, space_p); | |
if (result.full) { | |
cout << "Parse hit " << result.hit | |
<< " full " << result.full | |
<< " length " << result.length | |
<< endl; | |
} else { | |
typedef typename Iterator::difference_type diff_t; | |
string snip (std::min (distance (result.stop, end), (diff_t)20), ' '); | |
copy (result.stop, result.stop + snip.capacity (), snip.begin ()); | |
cout << "Parse failed around '" << snip << "'" << endl; | |
exit (1); | |
} | |
return cfg; | |
} | |
void self_test () { | |
string input = | |
"# Sample config file\n" | |
"[section1] \n" | |
"key=val\n" | |
"key1=this\n" | |
"key2=this is value for key 1 in section1\n" | |
"key3=2\n" | |
"\n" | |
"[ section2 ] # Second section\n" | |
" # This reall is the 2nd section....\n " | |
" # IT EATS DATA! \n" | |
"\n" | |
"\tkey1 = I have many spaces \n" | |
"## The next one doesn't have many spaces, but look, comments in the middle!\n" | |
"\tkey2 =2\n" | |
"\n" | |
"# I'm spent!\n" | |
"\t\t\n" | |
; | |
cfg_map_t cfg = parse_cfg (input.begin (), input.end ()); | |
if (!cfg.empty()) | |
print_cfg_map (cfg); | |
} | |
void parse_file (const string &file) { | |
cout << "Parsing " << file << "..." << endl; | |
file_iterator<> begin(file); | |
if (!begin) { | |
std::cout << "Unable to open '" << file << endl; | |
return ; | |
} | |
cfg_map_t cfg (parse_cfg (begin, begin.make_end ())); | |
if (!cfg.empty()) | |
print_cfg_map (cfg); | |
} | |
int main (int argc, char *argv[]) { | |
if (argc == 1) { | |
self_test (); | |
} else { | |
for_each (argv + 1, argv + argc, parse_file); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment