c++parsingboost

How can I pass a parameter to a parser in Boost.Parser?


Please note that I am asking about Boost.Parser, not the older Boost.Spirit.

I have a reference to a symbols object that I want to use in a parsing rule. I think I want to use a parameter for that, but apparently the example of parameters in the tutorial is too complicated for me.

Here is the code I tried, which gives a compile error on the toprule_def line.

#include <boost/parser/parser.hpp>

namespace bp = ::boost::parser;

bp::rule< struct toprule, double > toprule = "toprule";
auto const toprule_def = bp::double_ >> bp::_p<0>;
BOOST_PARSER_DEFINE_RULES( toprule );

static void ParamTest( const bp::symbols<double>& syms )
{
    auto result = bp::parse( "10.3", toprule.with( syms ), bp::ws );
}

In comparison, if I wanted to do something like this in Spirit::Qi, I could create a grammar object with my symbol table reference as a member variable, and then use that symbol table in rules inside the grammar. Whereas Boost.Parser does not have grammars, and as far as I can tell, rules must be written in global scope.


Solution

  • You are not misunderstanding parameters, you're looking for lazy rule references.

    Just like X3, Boost Parser doesn't currently seem to have support for that, whereas Qi did. I tackled this previously for X3 (Boost spirit x3 - lazy parser, see https://github.com/boostorg/spirit/issues/530).

    However, Boost Parser doesn't advertise an x3::any_parser equivalent, and I cannot quickly find an example of a custom parser directive either. The quickest thing I can think of is when you can tokenize the input independently, so you can defer the symbol lookup to a semantic action instead:

     token [ lookup_sym(bp::_p<0>) ]
    

    Here's a proof-of-concept:

    Live On Coliru

    #include <boost/parser/parser.hpp>
    
    namespace bp = boost::parser;
    using namespace bp::literals;
    
    
    inline auto token      = bp::lexeme[+(bp::char_ - bp::blank - bp::punct)];
    auto        lookup_sym = [](auto& ctx) {
        auto const&      syms = bp::_p<0>(ctx); // Get the symbols from the parser context
    
        if (auto opt = syms.find(ctx, _attr(ctx))) {
            _val(ctx) = *opt; // Assign the value associated with the token
        } else
            _pass(ctx) = false; // If not found, fail the parse
    };
    auto copy = [](auto& ctx) { _val(ctx) = _attr(ctx); };
    
    bp::rule<struct toprule, double> toprule     = "toprule";
    static inline auto const         toprule_def = bp::double_[copy] | token[lookup_sym];
    BOOST_PARSER_DEFINE_RULES(toprule)
    
    static void ParamTest(bp::symbols<double> const& syms) {
        for (std::string const input : { "42e9", "-inf", "abc", "xyz" }) {
            std::cout << "Parsed " << std::setw(6) << quoted(input);
    
            if (auto result = bp::parse(input, toprule.with(std::cref(syms)), bp::ws))
                std::cout << " -> value: " << result.value() << std::endl;
            else
                std::cerr << " FAILED" << std::endl;
        }
    }
    
    int main() {
        std::cout << " -------- " << std::endl;
        ParamTest(bp::symbols<double> {{"abc", 10.3}});
    
        std::cout << " -------- " << std::endl;
        ParamTest(bp::symbols<double> {{"xyz", 10.3}});
    }
    

    Printing

     --------
    Parsed "42e9" -> value: 4.2e+10
    Parsed "-inf" -> value: -inf
    Parsed  "abc" -> value: 10.3
    Parsed  "xyz" FAILED
     --------
    Parsed "42e9" -> value: 4.2e+10
    Parsed "-inf" -> value: -inf
    Parsed  "abc" FAILED
    Parsed  "xyz" -> value: 10.3