Search code examples
boostboost-spirit-x3

how to avoid ambiguity of grammar when using boost spirit rule?


I was using boost.spirit.x3 to build a grammar for a script language, and I found an example in the boost which called cal9,and I need somehing like that but it lack the if-else statement parsing. so I added the following code to the statement definition on top of the calc9's statement_def.cpp. but when parsing the if_statement, the parser will recognize the if_statement as an assignment statement,which is not what I expected.Did I do something wrong? The code below is my modified version,you can compare to the original cal9 version


namespace client { namespace parser
{
    using x3::raw;
    using x3::lexeme;
    using namespace x3::ascii;


    struct statement_list_class;
    struct variable_declaration_class;
    struct assignment_class;
    struct variable_class;
    struct if_statement_class;

    typedef x3::rule<statement_list_class, ast::statement_list> statement_list_type;
    typedef x3::rule<variable_declaration_class, ast::variable_declaration> variable_declaration_type;
    typedef x3::rule<assignment_class, ast::assignment> assignment_type;
    typedef x3::rule<variable_class, ast::variable> variable_type;
    typedef x3::rule<if_statement_class, ast::if_statement> if_statement_type;

    statement_type const statement("statement");
    statement_list_type const statement_list("statement_list");
    variable_declaration_type const variable_declaration("variable_declaration");
    if_statement_type const if_statement("if_statement");
    assignment_type const assignment("assignment");
    variable_type const variable("variable");

    // Import the expression rule
    namespace { auto const& expression = client::expression(); }

    auto const statement_list_def =
        +(variable_declaration | assignment | if_statement )
        ;

    auto const variable_declaration_def =
            lexeme["var" >> !(alnum | '_')] // make sure we have whole words
        >   assignment
        ;
/////below is what I added//////
    auto const if_statement_def =
        lexeme["if" >> !(alnum | '_')] // make sure we have whole words
        > '('
        > expression
        > ')'
        > '{'
        > statement
        > '}'
        > -(lexeme["else" >> !(alnum | '_')] > '{' > statement > '}')

        ;
/////
    auto const assignment_def =
            variable
        >   '='
        >   expression
        >   ';'
        ;

 

    auto const variable_def = identifier;
    auto const statement_def = statement_list;

    BOOST_SPIRIT_DEFINE(
        statement
      , statement_list
      , variable_declaration
      , assignment
      , if_statement
      , variable
    );

    struct statement_class : error_handler_base, x3::annotate_on_success {};
    struct assignment_class : x3::annotate_on_success {};
    struct variable_class : x3::annotate_on_success {};
    struct if_statement_class : x3::annotate_on_success {};
}}

namespace client
{
    parser::statement_type const& statement()
    {
        return parser::statement;
    }
}


Solution

  • The simplest thing you can try is reordering the branches, changing

    auto const statement_list_def =
        +(variable_declaration | assignment | if_statement )
        ;
    

    into

    auto const statement_list_def =
        +(variable_declaration | if_statement | assignment )
        ;