【问题标题】:BOOST_SPIRIT_DEFINE not understandBOOST_SPIRIT_DEFINE 不明白
【发布时间】:2021-04-10 11:17:36
【问题描述】:

我正在尝试使用 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 'parse': is not a member of 'boost::spirit::x3::unused_type'

为什么?

【问题讨论】:

    标签: c++ visual-studio c++11 boost boost-spirit-x3


    【解决方案1】:

    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,因为在这种语法中使用自动传播往往会导致语法中的大量回溯,从而导致效率低下

    【讨论】:

    猜你喜欢
    • 2015-04-21
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-11
    • 2021-05-13
    • 2020-08-20
    • 2011-02-17
    相关资源
    最近更新 更多