Skip to content

Instantly share code, notes, and snippets.

@eskil
Created May 13, 2016 21:12
Show Gist options
  • Save eskil/10614c61b9dd5823ed17da316f9cb834 to your computer and use it in GitHub Desktop.
Save eskil/10614c61b9dd5823ed17da316f9cb834 to your computer and use it in GitHub Desktop.
.cfg parse in boost spirit
//#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