After finally getting my originally believed simple first use of boost::spirit to parse a simple config file format (inspired by an earlier post interpreting a more limited syntax in C, which I decided to do in boost for learning purpouses) I have finally squashed all my errors (including 3 pages worth that resulted from a single missing const function specification) and actually gotten my code to parse config files that have the format:
#leading blank lines AOK
#comments
#comments are allowed to have leading whitespace
key=value pairs
#blank lines allowed, including with just whitespaces:
keys_have_surrounding_whitespace_trimmed = same with values
#trailing blank lines AOK
But now that I've finally wrestled closures out of a couple of examples, I'd like to get feedback on better ways to write this same parser (USING BOOST::SPIRIT) since I've probably made many novice mistakes, and to be quite frank, I can barely understand the documentation OR the examples.
main.cc (yes, only one file, since I just wanted to get the thing working and compiling for the purpouse of understanding it - yes this code compiles and runs and it successfully parses all the examples I've thrown at it):
#include <boost/spirit.hpp>
#include <boost/format.hpp>
#include <boost/spirit/phoenix/binders.hpp>
#include <boost/lambda/bind.hpp>
#include <fstream>
#include <iostream>
#include <string>
#include <map>
#include <list>
using namespace boost::spirit;
using namespace boost;
using namespace phoenix;
using namespace std;
struct keyval_pair_closure : boost::spirit::closure<keyval_pair_closure,string,string>
{
member1 key;
member2 val;
};
struct string_closure : boost::spirit::closure<string_closure,string>
{
member1 val;
};
struct config_grammar : public grammar<config_grammar>
{
map<string,string> & tokens;
config_grammar( map<string,string> & tokens_ ) : tokens(tokens_) {}
void add_keyval(const string & key , const string & val ) const { tokens[key] = val; }
template < typename ScannerT > struct definition
{
definition(config_grammar const& self)
{
top
= *(eol_p)
>> !(any_line
>> *( +(eol_p) >> any_line)
)
>> *(eol_p) >> end_p
;
any_line
= comment_line
| keyval_line
| blank_line
;
comment_line
= *(blank_p)
>> '#'
>> *(anychar_p - eol_p)
;
blank_line
= +(blank_p)
;
identifier
= lexeme_d
[
( alpha_p | '_')
>> *( alnum_p | '_')
][identifier.val = construct_<std::string>(arg1, arg2)]
;
value
= lexeme_d
[
*( anychar_p - eol_p )
][value.val = construct_<std::string>(arg1, arg2)]
;
keyval_line
= *(blank_p)
>> identifier[keyval_line.key = arg1]
>> *(blank_p)
>> '='
>> *(blank_p)
>> value[keyval_line.val = arg1]
[bind(&config_grammar::add_keyval)(self,keyval_line.key,keyval_line.val)]
;
}
rule<ScannerT> top,comment_line,blank_line,any_line;
rule<ScannerT,keyval_pair_closure::context_t> keyval_line;
rule<ScannerT,string_closure::context_t> identifier,value;
rule<ScannerT> const& start() const { return top; }
};
};
config_grammar config_p( map<string,string> & output ) { return config_grammar( output ); }
bool parse_cfg_file( const string & filename , map<string,string> & output )
{
file_iterator<> first( filename.c_str() );
if (!first)
{
cout << "File does not exist" << endl;
return false;
}
file_iterator<> last = first.make_end();
if (!parse( first , last , config_p(output) ).full) return false;
return true;
}
int main ( int argc , char ** argv )
{
string filename = "C:\\eclipse\\workspace\\boostfun\\test.cfg";
if (argc >= 2) filename = argv[1];
map<string,string> keys;
keys["test"] = "123";
if (parse_cfg_file( filename , keys ))
{
cout << format("Parsed file: %1% OK") % filename << endl;
}
else
{
cout << format("Parse of file: %1% FAILED") % filename << endl;
}
for ( map<string,string>::iterator i = keys.begin() ; i != keys.end() ; ++i )
{
cout << format("Key: %1% Value: %2%") % i->first % i->second << endl;
}
cin.get();
}