BOOST_SPIRIT_DEFINE不明白 [英] BOOST_SPIRIT_DEFINE not understand

查看:61
本文介绍了BOOST_SPIRIT_DEFINE不明白的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Boost Spirit x3编写一个表达式解析器.我将新代码基于几年前用Spirit 2.x(qi)编写的旧代码(并能很好地工作).

我的代码的核心是:

 //为表达式创建新规则自动项=因子>>*((('*'>>因子)|('/'>>因子)));自动表达=术语>>*((('+'>>项)|('-'>>项)));auto group ='('>>表达式>>')';自动因子= lexeme ["double_"] |团体;字符串s ="12.4 + 3.2";自动优先= s.begin();auto last = s.end();bool r = x3 :: phrase_parse(第一的,最后的,//开始语法表达,//结束语法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中更为常见: boost :: spirit :: x3属性兼容规则,直觉或代码?).).).>

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

  4. 要从内置规则调试中受益( #define BOOST_SPIRIT_X3_DEBUG ).这是我了解使用 BOOST_SPIRIT_DEFINE 宏系列的唯一原因

    这经常会引起难以诊断的链接器错误,因此在大多数情况下,我建议您这样做:X3解析器在实践中的编译速度足够快,我有能力将它们保存在单个翻译单元中


您的样本

expression 被递归使用.对于其余的内容,只需对它们重新排序就可以了:

 命名空间解析器{x3 :: rule< struct expression_>表达式{表达式"};auto group ='('>>表达式>>')';自动因子= x3 :: lexeme ["double_"] |团体;自动项=因子>>*((('*'>>因子)|('/'>>因子)));自动expression_def =术语>>*((('+'>>项)|('-'>>项)));BOOST_SPIRIT_DEFINE(表达式)} 

查看它 Live on Compiler Explorer 打印:

 < expression>< try> 12.4 + 3.2</try><失败/></表达式>------ 12.4 + 3.2r:错误其余输入:"12.4 + 3.2" 

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

  auto factor = x3 :: double_ |团体; 

查看它 在Compiler Explorer上实时运行 :

 < expression>< try> 12.4 + 3.2</try><成功>/成功</表达式>------ 12.4 + 3.2r:是的其余输入:" 

奖励:属性

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

 命名空间Ast {struct binop;使用表达式= boost :: make_recursive_variant<双倍的,boost :: recursive_wrapper< binop> ;,boost :: recursive_variant_> :: type;struct binop {char op;表达式lhs,rhs;};} 

这是最简单的方法.您的规则对于根据语义动作构造ast非常有效¹:

 命名空间解析器{x3 :: rule< struct expression_,Ast :: expression>表达式{表达式"};自动分配= [](自动和ctx){_val(ctx)= _attr(ctx);};自动make_binop = [](自动ctx){使用boost :: fusion :: at_c;自动&op = at_c <0>(_ attr(ctx));自动&rhs = at_c 1(_attr(ctx));_val(ctx)= Ast :: binop {op,_val(ctx),rhs};};汽车集团= x3 :: rule< struct group_,Ast :: expression>{"group"}='('>>表达式>>')';自动因素= x3 :: rule< struct factor_,Ast :: expression>{"factor"}= x3 :: double_ |团体;自动词= x3 :: rule< struct term_,Ast :: expression>{条款"}=系数[assign]>*(x3 :: char _("*/")>>系数)[make_binop];自动expression_def=项[assign]>>*(x3 :: char _(-+")>>项)[make_binop];BOOST_SPIRIT_DEFINE(表达式)} 

查看它 在编译器资源管理器中实时运行 :

  int main(){对于(std :: string const s:{"12.4 + 3.2",}){自动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";如果(r)std :: cout<<" e:"<<e<<"\ n";如果(f!= l)std :: cout<<剩余输入:'"<<std :: string(f,l)<<'\ n";}} 

打印

  ------ 12.4 + 3.2r:是的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><成功>+ 3.2</success><属性> 12.4</属性></factor><成功>+ 3.2</success><属性> 12.4</属性></term>< term>< try>3.2</try>< factor>< try>3.2</try><成功>/成功<属性> 3.2</属性></factor><成功>/成功< attributes> 3.2</attributes></term><成功>/成功< attributes>(12.4 + 3.2)</attributes></表达式> 


¹这里我没有提及我通常的熨平板,因为使用了自动在这种语法中传播 往往会导致语法中的大量回溯,从而使其效率低下

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?

解决方案

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

这篇关于BOOST_SPIRIT_DEFINE不明白的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆