Boost::精神表达分析器
Boost::Spirit Expression Parser
我的boost::spirit解析器有另一个问题。
template<typename Iterator>
struct expression: qi::grammar<Iterator, ast::expression(), ascii::space_type> {
expression() :
expression::base_type(expr) {
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];
binop = (expr >> '+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1,_2)]
| (expr >> '-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1,_2)]
| (expr >> '*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1,_2)]
| (expr >> '/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1,_2)] ;
expr %= number | varname | binop;
}
qi::rule<Iterator, ast::expression(), ascii::space_type> expr;
qi::rule<Iterator, ast::expression(), ascii::space_type> binop;
qi::rule<Iterator, std::string(), ascii::space_type> varname;
qi::rule<Iterator, double(), ascii::space_type> number;
};
这是我的解析器。它可以很好地解析"3.1415"
和"var"
,但是当我试图解析"1+2"
时,它告诉我parse failed
。然后我尝试将binop
规则更改为
binop = expr >>
(('+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1, _2)]
| ('-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1, _2)]
| ('*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1, _2)]
| ('/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1, _2)]);
但是现在它当然不能构建AST,因为_1
和_2
的设置不同。我只见过_r1
这样的东西,但作为一个boost-Newbie,我不太能理解boost::phoenix
和boost::spirit
是如何相互作用的。
如何解决这个问题?
我不太清楚你想达到什么目的。最重要的是,你不担心运算符的结合性吗?我将展示基于右递归的简单答案——这将导致左结合运算符被解析。
对于可见问题的直接答案是使用fusion::vector2<char, ast::expression>
-这并不是很有趣,特别是在Phoenix lambda语义动作中。(我将在下面展示它是什么样子的)。
与此同时,我认为你应该仔细阅读Spirit文档
-
旧 Spirit文档中的
- (消除左递归);虽然语法不再适用,但Spirit仍然生成LL递归下降解析器,因此左递归背后的概念仍然适用。下面的代码显示了对Spirit Qi 的应用这里的
- : Qi示例包含三个
calculator
示例,它们应该提示您为什么操作符结合性很重要,以及如何表达捕获二元操作符结合性的语法。显然,它还展示了如何支持括号表达式来覆盖默认的求值顺序。
代码:
我有三个版本的代码可以工作,解析输入如下:
std::string input("1/2+3-4*5");
进入ast::expression
组,如(使用BOOstrongPIRIT_DEBUG):
<expr>
....
<success></success>
<attributes>[[1, [2, [3, [4, 5]]]]]</attributes>
</expr>
代码的链接在这里:
- step_ # 1 _reduce_semantic_actions.cpp
- step_ # 2 _drop_rule.cpp
- step_ # 0 _vector2.cpp
步骤1:减少语义动作
首先,我要去掉每个操作符的解析表达式;这将导致过多的回溯1。而且,正如您所发现的,它使语法难以维护。因此,这里有一个更简单的变体,它使用一个函数来执行语义动作:
<子>1检查使用BOOstrongPIRIT_DEBUG! 子>
static ast::expression make_binop(char discriminant,
const ast::expression& left, const ast::expression& right)
{
switch(discriminant)
{
case '+': return ast::binary_op<ast::add>(left, right);
case '-': return ast::binary_op<ast::sub>(left, right);
case '/': return ast::binary_op<ast::div>(left, right);
case '*': return ast::binary_op<ast::mul>(left, right);
}
throw std::runtime_error("unreachable in make_binop");
}
// rules:
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];
simple = varname | number;
binop = (simple >> char_("-+*/") >> expr)
[ _val = phx::bind(make_binop, qi::_2, qi::_1, qi::_3) ];
expr = binop | simple;
步骤2:删除冗余规则,使用 _val
如您所见,这有可能降低复杂性。现在这只是一个小步骤,删除binop中间(它已经变得相当冗余):
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];
simple = varname | number;
expr = simple [ _val = _1 ]
> *(char_("-+*/") > expr)
[ _val = phx::bind(make_binop, qi::_1, _val, qi::_2) ]
> eoi;
如你所见,
- 在
expr
规则中,_val
惰性占位符被用作一个伪局部变量,用于累积binops。跨规则,您必须使用qi::locals<ast::expression>
来实现这种方法。(这是你关于_r1
的问题)。 - 现在有明确的期望点,使语法更健壮
-
expr
规则不再需要是一个自动规则(expr =
代替expr %=
)
步骤0:直接摔跤融合类型
最后,为了有趣和血腥,让我展示如何处理建议的代码,以及_1,_2等的移位绑定:
static ast::expression make_binop(
const ast::expression& left,
const boost::fusion::vector2<char, ast::expression>& op_right)
{
switch(boost::fusion::get<0>(op_right))
{
case '+': return ast::binary_op<ast::add>(left, boost::fusion::get<1>(op_right));
case '-': return ast::binary_op<ast::sub>(left, boost::fusion::get<1>(op_right));
case '/': return ast::binary_op<ast::div>(left, boost::fusion::get<1>(op_right));
case '*': return ast::binary_op<ast::mul>(left, boost::fusion::get<1>(op_right));
}
throw std::runtime_error("unreachable in make_op");
}
// rules:
expression::base_type(expr) {
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];
simple = varname | number;
binop %= (simple >> (char_("-+*/") > expr))
[ _val = phx::bind(make_binop, qi::_1, qi::_2) ]; // note _2!!!
expr %= binop | simple;
如您所见,用这种方式编写make_binop
函数并没有那么有趣! 相关文章:
- 理解boost::asio-async_read在无需读取内容时的行为
- boost::进程间消息队列引发错误
- 如何运行位于boost/libs/python/example/tutorial目录中的hello.cpp和Jamfil
- cmake如何在fedora工作站中找到boost静态库包
- CMake项目Boost库错误:Boost/config/compiler/gcc.hpp:165:10:致命错误:cs
- Boost Graph Library,修复节点大小
- 什么是"#include <boost/functional/hash.hpp> "?
- 基于boost的程序的静态链接——zlib问题
- C++:如何在CLion IDE中安装Boost
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何在boost beast http请求中设置http头
- Boost Spirit,获取迭代器内部语义动作
- boost::当词法分析器令牌> 10 时出现精神编译错误
- 编译错误,带有boost::spirit语法分析器
- Boost Spirit字符分析器
- Boost Spirit自动分析器在一个双元组中失败
- Boost Spirit(经典):内联语法分析器,除了跳过注释之外还能工作
- Boost::精神表达分析器
- 我是否需要词法分析器来消除关键字的歧义?(boost::精神)
- 我无法在 Boost Spirit 的词法分析器功能中获得语义操作来编译