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

Idiomatic complete matching with post skipping


What is the most idiomatic way to do post-skipping? More specific I want to ensure there is no "non-skippable" (garbage) characters in my input after matching my top rule.

auto const blankOrComment
    = ascii::space
    | x3::lexeme ['#' >> *(x3::char_ - x3::eol) >> -x3::eol ]
    ;

auto const program = rule<AstProgram>("program")
    = *(as<AstDefinition> (definition > ";"))
    ;

auto const programEntry = x3::skip(blankOrComment) [program];

One idea, I consider quite ugly was to do a separate parse call for the blankOrComment afterwards, if the main iterator position is not the end iterator. The current better idea I have is to change the root rule:

auto const programEntry = x3::skip(blankOrComment) [program >> x3::omit[*blankOrComment]];

Is there a more idiomatic way?


Solution

  • The simplest hack is to tack on >> eps: Live On Coliru

    Note I'd strive to make the skipper more self-descriptive:

    auto const skipper
        = space
        | '#' >> *(char_ - eol) >> (eol|eoi) 
        ;
    

    Likewise you can make that postskip hack more self-descriptive:

        auto const post_skip = eps;
        auto const program = "program" >> post_skip;
    

    Live On Coliru

    #include <iostream>
    #define BOOST_SPIRIT_X3_DEBUG
    #include <boost/spirit/home/x3.hpp>
    
    namespace Parser {
        namespace x3 = boost::spirit::x3;
    
        namespace rules {
            using namespace x3;
    
            auto const skipper
                = space
                | '#' >> *(char_ - eol) >> (eol|eoi) 
                ;
    
            auto const post_skip = eps;
            auto const program = "program" >> post_skip;
    
        }
    
        auto const programEntry = x3::skip(rules::skipper) [rules::program];
    }
    
    int main() {
        using It = std::string::const_iterator;
        for (std::string const input : {
                "",
                " program ",
                "#hello\n program # comment\n",
        }) {
            It f = input.begin(), l = input.end();
    
            if(parse(f, l, Parser::programEntry)) {
                std::cout << "Parse success\n";
            } else {
                std::cout << "Parse failed\n";
            }
    
            std::cout << "Remaining: '" << std::string(f,l) << "'\n";
        }
    }
    

    Prints

    Parse failed
    Remaining: ''
    Parse success
    Remaining: ''
    Parse success
    Remaining: ''