BOOST_SPIRIT_DEFINE not understand



我正在尝试用boost spirit x3编写一个表达式解析器。我的新代码基于几年前用Spirit2.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 BOOstrongPIRIT_DEFINE。错误C2039"parse":不是"boost::spirit::x3::unused_type"的成员

为什么?

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

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

通常,使用x3::rule:的原因如下

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

  2. 强制暴露的属性类型(在我的经验中,X3中对这一点的需求比Qi中更常见:理解Boost中的列表运算符(%(。Spirit,或例如boost::Spirit::x3属性兼容性规则、直觉或代码?(。

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

查看实时编译器资源管理器打印:

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

查看实时编译器资源管理器打印:

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

查看实时编译器资源管理器:

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>

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

最新更新