Sign in to follow this  
MaulingMonkey

Am I totally misusing/abusing boost::spirit?

Recommended Posts

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();
}

Share this post


Link to post
Share on other sites
Quote:
Original post by antareus
Looks okay to me, but I'm a spirit newbie. There is a definite learning curve involved with spirit.


Definately. I had some problems earlier on with key/value temp values not being both set (I think) at the time I tried to insert them into my map, resulting in a map with a single blank key, the corresponding value of which was the value of the last valid key/value pair of the file read... which I fixed either by switching the location of the statement, or more likely by using insert_a instead of map[key]=value (which one I'm not sure, I restarted the work on the parser from scratch when I couldn't get the 1st one to work)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this