微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

BOOST_SPIRIT_DEFINE 不明白

如何解决BOOST_SPIRIT_DEFINE 不明白

我正在尝试使用 boost spirit x3 编写一个表达式解析器。 我的新代码基于我多年前使用 Spirit 2.x (qi) 编写(并且运行良好)的旧代码

我的代码的核心是:

   //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);

我收到两个错误(Visual Studio 2019): 错误 C2338 BOOST_SPIRIT_DEFINE 未为此规则定义。 错误 C2039“解析”:不是“boost::spirit::x3::unused_type”的成员

为什么?

解决方法

BOOST_SPIRIT_DEFINE 用于将静态规则标签与定义(即该规则的解析函数模板的实例化)相关联。

好消息是这通常是不必要的,并且可以在没有任何宏的情况下内嵌定义规则。

一般来说,使用 x3::rule 的原因如下:

  1. 当规则使用递归时。递归调用的规则需要有未定义的规则(未初始化的 rule<> 对象)来引用(很像前向声明)。

  2. 强制暴露的属性类型(根据我的经验,在 X3 中这种需要比在 Qi 中更常见:Understanding the List Operator (%) in Boost.Spirit,或例如 boost::spirit::x3 attribute compatibility rules,intuition or code?)。>

  3. 当您想在翻译单元之间传播规则定义时(即具有外部定义)。请注意,这还要求您了解需要支持的上下文和迭代器类型,以便您可以适当地实例化。

  4. 受益于内置规则调试 (#define BOOST_SPIRIT_X3_DEBUG)。 这是我知道使用 BOOST_SPIRIT_DEFINE 系列宏的唯一原因

    这通常会导致难以诊断的链接器错误,因此在大多数情况下我建议不要这样做:X3 解析器在实践中编译速度足够快,我可以负担得起将它们保存在单个翻译单元中


您的样品

只有 expression 被递归使用。其余的只需对它们重新排序即可:

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)
}

看到它Live On Compiler Explorer打印:

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

很明显,您的 factor 规则应该恢复正常:

auto factor         = x3::double_ | group;

看到它Live On Compiler Explorer打印:

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

奖励:属性

添加属性传播将突出我在上面 2. 下的意思:

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;
    };
}

这是最简单的方法。您的规则对于从语义操作中构建 ast 非常有效¹:

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)
}

看到它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";
    }
}

打印

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

和调试输出:

<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>

¹这里我没有提到my usual screed,因为在这种语法中使用自动传播往往会导致语法中的大量回溯,从而使其效率低下

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。