Boost X3迁移:移除identity lambda以传递属性至值
从Boost Qi迁移到X3:移除属性复制的Lambda表达式
问题描述
我正在将代码从Boost Qi迁移至Boost X3。现有一个非终结符规则expression,其对应AST结构为ast::Expression。示例中expression包含两种情况:
value:用于创建新的ast::Expression对象'( expression )':支持括号语法,需要将内部expression的结果直接传递给父规则
在Boost Qi中,我通过%=运算符即可自动完成属性复制,但在X3中不得不使用lambda表达式e2e手动复制属性。请问如何移除这个e2e?
复现代码
#include <iostream> #include <tuple> #include <string> #include <variant> #include <vector> #include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3/support/ast/variant.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include <boost/fusion/adapted/std_tuple.hpp> namespace x3 = boost::spirit::x3; namespace ast { enum class Cmd : int { const_val = 42 }; using data_t = x3::variant<int, double>; struct Expression { Cmd _cmd; std::string _args; }; }//ast BOOST_FUSION_ADAPT_STRUCT(ast::Expression, _cmd, _args) inline std::ostream& operator << (std::ostream& os, const ast::Expression& val) { os << "{" << (int)val._cmd << ':' << val._args << "}"; return os; } namespace client { namespace x3 = boost::spirit::x3; x3::rule<class expression, ast::Expression> const expression("expression_stmt"); x3::rule<class value, ast::data_t> const value("value_stmt"); auto const value_def = x3::int_ | x3::double_ ; // 从常量构造expression auto val_to_expr = [](auto& ctx) { auto& a1 = _attr(ctx); _val(ctx) = ast::Expression{ ast::Cmd::const_val, std::to_string(boost::get<int>(a1)) }; }; // 手动复制属性到结果 auto e2e = [](auto& ctx) { auto& a1 = _attr(ctx); _val(ctx) = std::get<1>(a1); }; auto const expression_def = value[val_to_expr] // !!!! 为什么需要e2e来复制属性? | ('(' > expression > ')') [e2e] ; BOOST_SPIRIT_DEFINE(expression, value); }//ns:client auto parse_text(const std::string& s) { namespace x3 = boost::spirit::x3; auto iter = s.cbegin(); auto end_iter = s.cend(); ast::Expression res; x3::ascii::space_type space; bool success = x3::phrase_parse(iter, end_iter, client::expression, space, res); if (success && iter == end_iter) { std::cout << "Success\n"; } else { std::cout << "Parse failure\n{"; while (iter != end_iter) std::cout << *iter++; std::cout << "}\n"; } return res; } int main() { auto test = [](const std::string& s) { std::cout << "parsing " << s << '\n'; auto res = parse_text(s); std::cout << "'" << res << "'\n"; }; test("123"); test("(123)"); }
解决方案
问题根源
在X3中,'( expression )'是三个解析器的组合:'('、expression、')',其属性是一个三元tuple(包含左括号字符、expression的属性、右括号字符),而外层expression规则的属性是ast::Expression,类型不匹配,因此无法自动赋值,必须用lambda手动提取tuple中的有效属性。
移除e2e的方法
使用x3::omit忽略括号的属性,让整个分支的属性直接等于内部expression的属性,这样X3就能自动完成属性匹配,无需手动复制:
修改expression_def部分:
auto const expression_def = value[val_to_expr] | (x3::omit['('] > expression > x3::omit[')']) ;
更简洁的写法
X3中,当解析器的属性被忽略时,>运算符会自动跳过无属性的解析器,因此也可以写成:
auto const expression_def = value[val_to_expr] | (x3::omit['('] >> expression >> x3::omit[')']) ;
修改后,括号部分不再产生额外属性,整个分支的属性与expression规则的属性完全一致,X3会自动完成属性赋值,e2e lambda就可以彻底移除了。
内容的提问来源于stack exchange,提问作者Dewfy
相关产品推荐
相关产品推荐

