BOOST_SPIRIT_DEFINE not understand

我们两清 提交于 2021-02-10 18:21:41

问题


I'm trying to write an expression parser with boost spirit x3. I based my new code on old code that I written years ago (and worked well) with Spirit 2.x (qi).

The core of my code is:

   //Make new rule(s) for expression
    auto term = factor >> *(('*' >> factor) | ('/' >> factor));
    auto expression = term >> *(('+' >> term) | ('-' >> term));
    auto group = '(' >> expression >> ')';
    auto factor = lexeme["double_"] | group;

     
    string s="12.4 + 3.2";

    auto first = s.begin();
    auto last = s.end();
    bool r = x3::phrase_parse(
        first,
        last,
        //  Begin grammar
        expression,
        //  End grammar
        x3::space);

I obtain two errors (Visual Studio 2019): Error C2338 BOOST_SPIRIT_DEFINE undefined for this rule. Error C2039 'parse': is not a member of 'boost::spirit::x3::unused_type'

Why?


回答1:


BOOST_SPIRIT_DEFINE is used to associate a static rule-tag with a definition (i.e. an instantiation of the parse function template for that rule).

The good news is that this is frequently unnecessary, and rules can be defined in-line without any macros.

In general, the following reasons exist to use x3::rule:

  1. When rules use recursion. The rule(s) that are invoked recursively need to have the undefined rule (uninitialized rule<> object) to refer to (much like a forward declaration).

  2. To coerce the exposed attribute type (in my exeperience the need for this is a bit more common in X3 than it was in Qi: Understanding the List Operator (%) in Boost.Spirit, or e.g. boost::spirit::x3 attribute compatibility rules, intuition or code?).

  3. When you want to spread your rule definitions across translation units (i.e. have external definitions). Note this also requires you to know the context and iterator type(s) you need to support so you can instantiate appropriately.

  4. To benefit from the builtin rule debugging (#define BOOST_SPIRIT_X3_DEBUG). This is the only reason I know off to use the BOOST_SPIRIT_DEFINE family of macros

    This frequently gives rise to hard-to diagnose linker errors so I recommend against this in most cases: X3 parsers compile fast enough in practice that I can afford to keep them in a single translation unit


Your Sample

Only expression is used recursively. For the rest it suffices to simply reorder them:

namespace parser {
    x3::rule<struct expression_> expression{"expression"};
   
    auto group          = '(' >> expression >> ')';
    auto factor         = x3::lexeme["double_"] | group;
    auto term           = factor >> *(('*' >> factor) | ('/' >> factor));
    auto expression_def = term >> *(('+' >> term) | ('-' >> term));

    BOOST_SPIRIT_DEFINE(expression)
}

See it Live On Compiler Explorer printing:

<expression>
  <try>12.4 + 3.2</try>
  <fail/>
</expression>
------ 12.4 + 3.2
r: false
remaining input: '12.4 + 3.2'

It is clear that your factor rule should be back to normal:

auto factor         = x3::double_ | group;

See it Live On Compiler Explorer printing:

<expression>
  <try>12.4 + 3.2</try>
  <success></success>
</expression>
------ 12.4 + 3.2
r: true
remaining input: ''

BONUS: Attributes

Adding attribute propagation will highlight what I meant under 2. above:

namespace Ast {
    struct binop;

    using expression = boost::make_recursive_variant<
        double,
        boost::recursive_wrapper<binop>,
        boost::recursive_variant_
    >::type;

    struct binop {
        char op;
        expression lhs, rhs;
    };
}

That's the simplest thing that could work. Your rules are pretty efficient for constructing the ast from semantic actions¹:

namespace parser {
    x3::rule<struct expression_, Ast::expression> expression{"expression"};

    auto assign = [](auto& ctx) { _val(ctx) = _attr(ctx); };
    auto make_binop = [](auto& ctx) {
        using boost::fusion::at_c;
        auto& op = at_c<0>(_attr(ctx));
        auto& rhs = at_c<1>(_attr(ctx));
        _val(ctx) = Ast::binop { op, _val(ctx), rhs };
    };
   
    auto group   
        = x3::rule<struct group_, Ast::expression> {"group"}
        = '(' >> expression >> ')';

    auto factor
        = x3::rule<struct factor_, Ast::expression> {"factor"}
        = x3::double_ | group;

    auto term
        = x3::rule<struct term_, Ast::expression> {"term"}
        = factor [assign] >> *(x3::char_("*/") >> factor) [make_binop];

    auto expression_def
        = term [assign] >> *(x3::char_("-+") >> term) [make_binop];

    BOOST_SPIRIT_DEFINE(expression)
}

See it Live On Compiler Explorer:

int main() {
    for (std::string const s : {
            "12.4 + 3.2",
        })
    {
        auto f = s.begin(), l = s.end();
        Ast::expression e;
        bool r = x3::phrase_parse(f, l, parser::expression, x3::space, e);

        std::cout
            << "------ " << s << "\n"
            << "r: " << std::boolalpha << r << "\n";

        if (r)
            std::cout << "e: " << e << "\n";

        if (f!=l)
            std::cout << "remaining input: '" << std::string(f,l) << "'\n";
    }
}

Printing

------ 12.4 + 3.2
r: true
e: (12.4 + 3.2)

And the debug output:

<expression>
  <try>12.4 + 3.2</try>
  <term>
    <try>12.4 + 3.2</try>
    <factor>
      <try>12.4 + 3.2</try>
      <success> + 3.2</success>
      <attributes>12.4</attributes>
    </factor>
    <success> + 3.2</success>
    <attributes>12.4</attributes>
  </term>
  <term>
    <try> 3.2</try>
    <factor>
      <try> 3.2</try>
      <success></success>
      <attributes>3.2</attributes>
    </factor>
    <success></success>
    <attributes>3.2</attributes>
  </term>
  <success></success>
  <attributes>(12.4 + 3.2)</attributes>
</expression>

¹ Here I don't mention my usual screed because using automatic propagation in this kind of grammar tends to lead to a lot of backtracking in the grammar, which makes it inefficient



来源:https://stackoverflow.com/questions/65566480/boost-spirit-define-not-understand

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!