结合二元算子AST节点的Boost Spirit Qi语法
Boost Spirit Qi Grammar for Synthesizing associative Binary Operator AST nodes?
我试图解析形式为"1.0 + 2.0 + 3.0 +…"的表达式到AST.我有以下AST节点的二进制操作(完整的,最小的代码示例是在最后):
struct binop_t
{
expression_t lhs, rhs;
};
我想使用"BOOST_FUSION_ADAPT_STRUCT"宏来允许这个结构通过boost:spirit::qi::规则来合成:
BOOST_FUSION_ADAPT_STRUCT
(
client::ast::binop_t,
(client::ast::expression_t, lhs)
(client::ast::expression_t, rhs)
)
换句话说,二进制操作AST节点(binop_t)需要两个操作数——应该操作的左侧(lhs)和右侧(rhs)表达式。I am能够通过使用以下qi::grammar:
将形式为"1.0+(2.0+(3.0+4.0))"的表达式解析为此AST节点qi::rule<Iterator, ast::literal_t(), ascii::space_type> literal;
qi::rule<Iterator, ast::binop_t(), ascii::space_type> binop;
qi::rule<Iterator, ast::expression_t(), ascii::space_type> primary_expr;
qi::rule<Iterator, ast::expression_t(), ascii::space_type> expr;
expr = binop.alias();
binop = primary_expr > qi::lit('+') > primary_expr;
primary_expr = (qi::lit('(') > expr > qi::lit(')'))
| literal
;
literal = qi::double_;
然而,我正在努力理解如何修改这个语法,以便它可以解析这样的表达式没有使用括号(例如。"1 + 2 + 3 + 4 +……")。
我已经查看了"calc4.cpp"Boost Spirit示例,并注意到它仅使用以下AST节点进行二进制操作(如添加):
struct operation
{
optoken operator_;
operand operand_;
};
这个例子和我试图做的事情之间的区别是,这个例子定义了用于合成二进制操作节点的语法,纯粹是根据一组一元操作。一元操作列表被合成到一个称为"program"的AST节点:
struct program
{
operand first;
std::list<operation> rest;
};
在示例中使用以下语法综合了整个内容:
qi::rule<Iterator, ast::program(), ascii::space_type> expression;
qi::rule<Iterator, ast::program(), ascii::space_type> term;
qi::rule<Iterator, ast::operand(), ascii::space_type> factor;
expression =
term
>> *( (char_('+') >> term)
| (char_('-') >> term)
)
;
term =
factor
>> *( (char_('*') >> factor)
| (char_('/') >> factor)
)
;
factor =
uint_
| '(' >> expression >> ')'
| (char_('-') >> factor)
| (char_('+') >> factor)
;
在这个语法中,"表达式"规则产生一个"程序",它是一个操作列表。从"expression"的语法规则可以看出,它在语法中使用了Kleene星号:
*((char_('+') >> term)
这就是语法如何能够解析关联二进制操作链,例如"1+2+3+4+…"。该语法的属性是list,它与"program"AST节点的定义相匹配。计算器"eval"函数然后简单地遍历"program"中的操作列表,从左到右将这些操作应用于操作数:
int operator()(program const& x) const
{
int state = boost::apply_visitor(*this, x.first);
BOOST_FOREACH(operation const& oper, x.rest)
{
state = (*this)(oper, state);
}
return state;
}
我也看过"mini-c"Boost Spirit的例子,它有一个非常相似的AST设计,其中没有二进制操作符AST节点(只有一个接受单个操作数的"操作符"节点)。
下面是到目前为止我已经实现的程序的完整的最小代码示例。回顾一下,我的问题是:我如何修改这个程序,使它能够从"1+2+3+4+…"这样的表达式合成binop_t AST节点树?不带的输入文本中括号的用法:
#include <boost/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <iostream>
#include <string>
#include <exception>
using boost::variant;
using boost::recursive_wrapper;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
namespace client { namespace ast {
struct literal_t;
struct binop_t;
typedef variant< recursive_wrapper<literal_t>,
recursive_wrapper<binop_t>
> expression_t;
struct literal_t
{
double value;
};
struct binop_t
{
expression_t lhs, rhs;
};
}} // ns
BOOST_FUSION_ADAPT_STRUCT
(
client::ast::literal_t,
(double, value)
)
BOOST_FUSION_ADAPT_STRUCT
(
client::ast::binop_t,
(client::ast::expression_t, lhs)
(client::ast::expression_t, rhs)
)
namespace client {
template <typename Iterator>
struct grammar_t : qi::grammar<Iterator, ast::expression_t(), ascii::space_type>
{
qi::rule<Iterator, ast::literal_t(), ascii::space_type> literal;
qi::rule<Iterator, ast::binop_t(), ascii::space_type> binop;
qi::rule<Iterator, ast::expression_t(), ascii::space_type> primary_expr;
qi::rule<Iterator, ast::expression_t(), ascii::space_type> expr;
grammar_t() : grammar_t::base_type(expr)
{
expr = binop.alias();
binop = primary_expr > qi::lit('+') > primary_expr;
primary_expr = (qi::lit('(') > expr > qi::lit(')'))
| literal;
literal = qi::double_;
expr.name("expr");
binop.name("binop");
literal.name("literal");
qi::debug(expr);
qi::debug(binop);
qi::debug(literal);
}
};
} // ns
int main()
{
try
{
string input = "0.1 + 1.2 ";
std::string::const_iterator begin = input.begin();
std::string::const_iterator end = input.end();
typedef std::string::const_iterator iterator_type;
client::grammar_t<iterator_type> g;
client::ast::expression_t ast;
bool status;
status = qi::phrase_parse(begin, end, g, ascii::space, ast);
EXPECT_TRUE(status);
EXPECT_TRUE(begin == end);
} catch (std::exception& e)
{
cout << e.what() << endl;
}
}
freenode上##spirit IRC频道上的VeXocide解决了这个问题(http://codepad.org/wufmFufE)。答案是按如下方式修改语法:
expr = binop.alias();
binop = primary_expr >> qi::lit('+') >> (binop | primary_expr);
primary_expr = (qi::lit('(') >> expr >> qi::lit(')'))
| literal;
literal = qi::double_;
这个语法创建了一个正确的递归,能够合成我正在寻找的解析树。
对于遇到同样问题的人的提示:如果没有Spirit调试语句,如果提供左递归语法,Boost Spirit将由于堆栈溢出而导致Seg Fault。如果您打开调试语句,它将打印出"无限"数量的文本,告诉您解析器中出现了错误。
- Boost Graph Library,修复节点大小
- 有没有办法访问C++ Boost rtree 中的非叶节点
- Boost::p tree - 访问列表中包含的属性树节点
- 使用boost::property_tree编写CDATA XML节点
- 如何使用 Boost 为图形的节点着色
- 使用可移动节点创建有向图(使用 QT / Boost)
- 找到从节点到离它最远的节点的距离 BOOST
- 使用 boost::asio 和 std::wstring 解析网络节点
- Boost XML - 获取下一个节点
- 如何在boost::邻接列表中排列节点
- c++ Boost属性树通过属性限定符更新现有节点
- 结合二元算子AST节点的Boost Spirit Qi语法
- boost ptree:添加更多具有相同名称和属性的节点
- Boost属性树:移除嵌套节点
- Boost属性树xml解析没有这样的节点()
- 如何确定'std::map'创建的用于'boost::pool_allocator'的节点的大小(以跨平台方式)?
- 从 boost::labeled_graph 获取节点标签
- 使用 boost::variant 遍历树节点的模板
- 我如何使用json解析器的boost property_tree创建空数组节点
- 如何遍历一个图分支,从特定的节点与Boost.Graph