Boost.Spirit 使うたびにいっつもう〜う〜うなること.関数呼び出し式(より一般には postfix expression)を AST で表現するとき,何を関数呼び出し式に対する root node とするか.どうも以下のように関数呼び出し式の '(' (開きカッコ)にディレクティブを引っ掛けて root node 作るしか方策がないようだけれど,これがとてつもなく気に入らにゃい.う〜う〜う〜.他にきれいに root node 生成する方策にゃいのかにゃー.関数名を root node にすると expression_list が空のとき(引数のリストが空のとき)に identifier がそのまま上に上がっちゃうんだよにゃー(関数呼び出し構文に semantics があるのに対応する AST のノードが生成されない). epsilon に root_node_d を引っ掛けても identifier を root node としたときと同じことになるし.
#define BOOST_SPIRIT_DUMP_PARSETREE_AS_XML #include <string> #include <map> #include <iostream> #include <boost/spirit/core.hpp> #include <boost/spirit/utility/confix.hpp> #include <boost/spirit/tree/ast.hpp> #include <boost/spirit/tree/tree_to_xml.hpp> #include <boost/thread/once.hpp> namespace spirit = boost::spirit; class Grammar : public spirit::grammar< Grammar > { public: enum parser_ids { translation_unit_parser_id = 100 , statement_parser_id , primary_expression_parser_id , literal_parser_id , postfix_expression_parser_id , function_call_expression_parser_id , expression_parser_id , expression_list_parser_id , identifier_parser_id }; private: static void initializeRuleNameMap() //throw() { try{ rule_name_map_[translation_unit_parser_id] = "translation unit"; rule_name_map_[statement_parser_id] = "statement"; rule_name_map_[primary_expression_parser_id] = "primary expression"; rule_name_map_[literal_parser_id] = "literal"; rule_name_map_[postfix_expression_parser_id] = "postfix expression"; rule_name_map_[function_call_expression_parser_id] = "function call expression"; rule_name_map_[expression_parser_id] = "expression"; rule_name_map_[expression_list_parser_id] = "expression list"; rule_name_map_[identifier_parser_id] = "identifier"; } catch(...){ ; // Do nothing. } } public: Grammar() { boost::call_once( &initializeRuleNameMap , rule_name_map_initialized_once_ ); } public: template< class Scanner > class definition { public: definition( Grammar const &/*self*/ ) { using namespace boost::spirit; translation_unit = *statement ; statement = !expression >> no_node_d[ ch_p( ';' ) ] ; primary_expression = no_node_d[ ch_p( '(' ) ] >> expression >> no_node_d[ ch_p( ')' ) ] | literal ; literal = leaf_node_d[ lexeme_d[ !ch_p( '-' ) >> +digit_p ] ] ; postfix_expression = primary_expression | function_call_expression ; function_call_expression = identifier >> root_node_d[ ch_p( '(' ) ] // これがちょー気に入らにゃいっ!! >> !expression_list >> no_node_d[ ch_p( ')' ) ] ; expression = postfix_expression ; expression_list = expression >> *( root_node_d[ ch_p( ',' ) ] >> expression ) ; identifier = leaf_node_d[ lexeme_d[ ( ch_p( '_' ) | alpha_p ) >> *( ch_p( '_' ) | alnum_p ) ] ] ; } spirit::rule< Scanner , spirit::parser_tag< translation_unit_parser_id > > const &start() const { return translation_unit; } private: #define DECLARE_RULE( RULE_NAME ) \ spirit::rule< \ Scanner, spirit::parser_tag< RULE_NAME ## _parser_id > \ > RULE_NAME /**/ DECLARE_RULE( translation_unit ); DECLARE_RULE( statement ); DECLARE_RULE( primary_expression ); DECLARE_RULE( literal ); DECLARE_RULE( postfix_expression ); DECLARE_RULE( function_call_expression ); DECLARE_RULE( expression ); DECLARE_RULE( expression_list ); DECLARE_RULE( identifier ); #undef DECLARE_RULE }; // class definition static std::map< boost::spirit::parser_id, std::string > const & getRuleNameMap() { return rule_name_map_; } private: static boost::once_flag rule_name_map_initialized_once_; static std::map< spirit::parser_id, std::string > rule_name_map_; }; // Grammar boost::once_flag Grammar::rule_name_map_initialized_once_ = BOOST_ONCE_INIT; std::map< spirit::parser_id, std::string > Grammar::rule_name_map_ = std::map< spirit::parser_id, std::string >(); class SkipGrammar : public spirit::grammar< SkipGrammar > { public: template< class Scanner > class definition { public: definition( SkipGrammar const &/*self*/ ) { using namespace boost::spirit; skip_rule = space_p | eol_p | confix_p( ch_p( '#' ), *anychar_p, eol_p | end_p ) ; } spirit::rule< Scanner > const &start() const { return skip_rule; } private: spirit::rule< Scanner > skip_rule; }; // class definition }; // class SkipGrammar template< class T > T const &as_const( T const &x ) { return x; } int main() { std::string str( "cos( 0 ); # 1.0\n" "log( 2, sin( 0 ) ); # 0.0\n" "func(); # no arguments" ); Grammar g; SkipGrammar skip_g; spirit::tree_parse_info< std::string::const_iterator > info = spirit::ast_parse( as_const( str ).begin() , as_const( str ).end() , g , skip_g ); if( info.full ){ spirit::tree_to_xml( std::cout , info.trees , str , g.getRuleNameMap() ); } else{ std::cout << "failed to parse" << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }