Search code examples
c++parsingc++14boost-spiritboost-spirit-x3

How can I add conditional expectation points in spirit X3


I am currentl adding expectation points to my grammar in X3. Now I came accross an rule, which looks like this.

auto const id_string = +x3::char("A-Za-z0-9_);

auto const nested_identifier_def =
        x3::lexeme[
                *(id_string >> "::")
                >> *(id_string >> ".")
                >> id_string
        ];

I am wondering how I can add conditional expectation points to this rule. Like "if there is a "::" then there musst follow an id_string" or "when there is a . then there musst follow an id_string" and so on. How can I achieve such a behaviour for such a rule?


Solution

  • I'd write it exactly the way you intend it:

    auto const identifier 
        = lexeme [+char_("A-Za-z0-9_")];
    
    auto const qualified_id 
        = identifier >> *("::" > identifier);
    
    auto const simple_expression // only member expressions supported now
        = qualified_id >> *('.' > identifier);
    

    With a corresponding AST:

    namespace AST {
        using identifier = std::string;
    
        struct qualified_id : std::vector<identifier> { using std::vector<identifier>::vector; };
    
        struct simple_expression {
            qualified_id lhs;
            std::vector<identifier> rhs;
        };
    }
    

    LIVE DEMO

    Live On Coliru

    #include <iostream>
    #include <string>
    #include <vector>
    
    namespace AST {
        using identifier = std::string;
    
        struct qualified_id : std::vector<identifier> { using std::vector<identifier>::vector; };
    
        struct simple_expression {
            qualified_id lhs;
            std::vector<identifier> rhs;
        };
    }
    
    #include <boost/fusion/adapted.hpp>
    BOOST_FUSION_ADAPT_STRUCT(AST::simple_expression, lhs, rhs)
    
    #include <boost/spirit/home/x3.hpp>
    
    namespace Parser {
        using namespace boost::spirit::x3;
    
        auto const identifier 
            = rule<struct identifier_, AST::identifier> {}
            = lexeme [+char_("A-Za-z0-9_")];
    
        auto const qualified_id 
            = rule<struct qualified_id_, AST::qualified_id> {}
            = identifier >> *("::" > identifier);
    
        auto const simple_expression // only member expressions supported now
            = rule<struct simple_expression_, AST::simple_expression> {}
            = qualified_id >> *('.' > identifier);
    }
    
    int main() {
        using It = std::string::const_iterator;
    
        for (std::string const input : { "foo", "foo::bar", "foo.member", "foo::bar.member.subobject" }) {
            It f = input.begin(), l = input.end();
            AST::simple_expression data;
    
            bool ok = phrase_parse(f, l, Parser::simple_expression, boost::spirit::x3::space, data);
    
            if (ok) {
                std::cout << "Parse success: ";
                for (auto& el : data.lhs) std::cout << "::" << el;
                for (auto& el : data.rhs) std::cout << "." << el;
                std::cout << "\n";
            }
            else {
                std::cout << "Parse failure ('" << input << "')\n";
            }
    
            if (f != l)
                std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
        }
    }
    

    Prints

    Parse success: ::foo
    Parse success: ::foo::bar
    Parse success: ::foo.member
    Parse success: ::foo::bar.member.subobject