Boost::Spirit:优化表达式解析器
Boost::Spirit : Optimizing an expression parser
我正在尝试写一个程序来解析和评估数学,文字和布尔表达式,例如:
- "(9/3)= = 3 + 3 * 2"将被解析为"(9/3)= =(3 +(3 * 2))",同时"false"
- "1+2/3"将解析为"1+(2/3)"并评估为"6"。
- "this +is+a+test"将被解析为"this+is+a+test"并计算为"thisisatest"
程序正确地解析和解决了我给它的内容,但是一旦我在表达式中放入括号,解析就会花费大量的时间。
我的工作是基于Sehe关于如何编写布尔语法解析器的令人印象深刻的详尽答案。在该示例之后,我添加了新的操作符(+、-、/、*、==、!=)。
解析器
// DEFINING TYPES
struct op_not {};
struct op_or {};
struct op_and {};
struct op_xor {};
struct op_equal {};
struct op_unequal {};
struct op_sum {};
struct op_difference {};
struct op_factor {};
struct op_division {};
typedef ustring var;
template <typename tag> struct binop;
template <typename tag> struct unop;
typedef boost::variant<var,
boost::recursive_wrapper<unop <op_not> >,
boost::recursive_wrapper<binop<op_equal> >,
boost::recursive_wrapper<binop<op_unequal> >,
boost::recursive_wrapper<binop<op_and> >,
boost::recursive_wrapper<binop<op_xor> >,
boost::recursive_wrapper<binop<op_or> >,
boost::recursive_wrapper<binop<op_difference> >,
boost::recursive_wrapper<binop<op_sum> >,
boost::recursive_wrapper<binop<op_factor> >,
boost::recursive_wrapper<binop<op_division> >
> expressionContainer;
template <typename tag> struct binop
{
explicit binop(const expressionContainer& l
, const expressionContainer& r)
: oper1(l), oper2(r) { }
expressionContainer oper1, oper2;
};
template <typename tag> struct unop
{
explicit unop(const expressionContainer& o) : oper1(o) { }
expressionContainer oper1;
};
// EXPRESSION PARSER
template <typename It, typename Skipper = boost::spirit::standard_wide::space_type>
struct parserExpression : qi::grammar<It, expressionContainer(), Skipper>
{
parserExpression() : parserExpression::base_type(expr_)
{
using namespace qi;
expr_ = or_.alias();
// Logical Operators
or_ = (xor_ >> orOperator_ >> or_) [_val = boost::phoenix::construct<Expression::binop<op_or >>(_1, _3)] | xor_[_val = _1];
xor_ = (and_ >> xorOperator_ >> xor_) [_val = boost::phoenix::construct<Expression::binop<op_xor>>(_1, _3)] | and_[_val = _1];
and_ = (equal_ >> andOperator_ >> and_) [_val = boost::phoenix::construct<Expression::binop<op_and>>(_1, _3)] | equal_[_val = _1];
equal_ = (unequal_ >> equalOperator_ >> equal_) [_val = boost::phoenix::construct<Expression::binop<op_equal>>(_1, _3)] | unequal_[_val = _1];
unequal_ = (factor_ >> unequalOperator_ >> unequal_) [_val = boost::phoenix::construct<Expression::binop<op_unequal>>(_1, _3)] | factor_[_val = _1];
// Numerical Operators
factor_ = (division_ >> factorOperator_ >> factor_) [_val = boost::phoenix::construct<Expression::binop<op_factor>>(_1, _3)] | division_[_val = _1];
division_ = (sum_ >> divisionOperator_ >> division_) [_val = boost::phoenix::construct<Expression::binop<op_division>>(_1, _3)] | sum_[_val = _1];
sum_ = (difference_ >> sumOperator_ >> sum_) [_val = boost::phoenix::construct<Expression::binop<op_sum>>(_1, _3)] | difference_[_val = _1];
difference_ = (not_ >> differenceOperator_ >> difference_) [_val = boost::phoenix::construct<Expression::binop<op_difference>>(_1, _3)] | not_[_val = _1];
// UNARY OPERATIONS
not_ = (notOperator_ > simple) [_val = boost::phoenix::construct<Expression::unop <op_not>>(_2)] | simple[_val = _1];
simple = (('(' > expr_ > ')') | var_);
var_ = qi::lexeme[+alnum];
notOperator_ = qi::char_('!');
andOperator_ = qi::string("&&");
orOperator_ = qi::string("||");
xorOperator_ = qi::char_("^");
equalOperator_ = qi::string("==");
unequalOperator_ = qi::string("!=");
sumOperator_ = qi::char_("+");
differenceOperator_ = qi::char_("-");
factorOperator_ = qi::char_("*");
divisionOperator_ = qi::char_("/");
/*BOOST_SPIRIT_DEBUG_NODE(expr_);
BOOST_SPIRIT_DEBUG_NODE(or_);
BOOST_SPIRIT_DEBUG_NODE(xor_);
BOOST_SPIRIT_DEBUG_NODE(and_);
BOOST_SPIRIT_DEBUG_NODE(not_);
BOOST_SPIRIT_DEBUG_NODE(simple);
BOOST_SPIRIT_DEBUG_NODE(var_);
BOOST_SPIRIT_DEBUG_NODE(notOperator_);
BOOST_SPIRIT_DEBUG_NODE(andOperator_);
BOOST_SPIRIT_DEBUG_NODE(orOperator_);
BOOST_SPIRIT_DEBUG_NODE(xorOperator_);
BOOST_SPIRIT_DEBUG_NODE(sumOperator_);
BOOST_SPIRIT_DEBUG_NODE(differenceOperator_);
BOOST_SPIRIT_DEBUG_NODE(factorOperator_);
BOOST_SPIRIT_DEBUG_NODE(divisionOperator_);*/
}
private:
qi::rule<It, var(), Skipper> var_;
qi::rule<It, expressionContainer(), Skipper> not_
, and_
, xor_
, or_
, equal_
, unequal_
, sum_
, difference_
, factor_
, division_
, simple
, expr_;
qi::rule<It, ustring(), Skipper> notOperator_
, andOperator_
, orOperator_
, xorOperator_
, equalOperator_
, unequalOperator_
, sumOperator_
, differenceOperator_
, factorOperator_
, divisionOperator_;
};
对于上面的代码,在我的计算机上(运行Intel I5 CPU):
- 解析"1 + 2 - 3 * 4/5 == 6 != 7 &&8 || 9 ^ 8 * 7/6 ^ 5 &&4 || 3 != 2 == 1"是瞬间的解析"(1)"大约需要200ms
Spirit的性能之前已经得到了证明,我有一个显而易见的问题:我可以改进什么?
您的问题是(1)
是使用该语法进行回溯的最坏情况。让我们研究一个简化的例子:
or_ = (and_ >> '|' >> or_) | and_;
and_ = (not_ >> '&' >> and_) | not_;
not_ = ('!' >> simple_) | simple_;
simple_ = ('(' >> or_ >> ')') | var_;
这里是一步一步的演练:
- 我们尝试
or_
- 我们试试
and_
- 我们试试
not_
- 我们尝试
'!'
,'!' >> simple_
失败 - 我们试试
simple
- 我们尝试
'('
,它匹配 - 我们试试
or_
- 我们试试
and_
- 我们试试
not_
- 我们尝试
'!'
,'!' >> simple_
失败 - 我们试试
simple
- 我们尝试
'('
,'(' >> or_ >> ')'
失败 - 我们尝试
var_
,它匹配
- 我们尝试
-
simple_
成功
- 我们尝试
-
not_
succeeded - 我们尝试
'&'
,not_ >> '&' >> and_
失败(之前的simple_
和not_
匹配被丢弃) - 我们尝试
not_
(唯一的一个)- 一切如初
-
not_
成功
- 我们试试
-
and_
成功 - 我们尝试
'|'
,and_ >> '|' >> or_
失败(and_
,not_
和simple_
匹配被丢弃) - 我们尝试
and_
(单独一个)- 一切如初
-
and_
成功
- 我们试试
-
or_
成功 - 我们尝试
')'
,'(' >> or_ >> ')'
成功
- 我们尝试
-
simple_
成功
- 我们尝试
-
not_
成功 - 我们尝试
'&'
,not_ >> '&' >> and_
失败(一切都被丢弃) - 我们尝试
not_
(单独一个)- 一切如初
-
not_
成功
- 我们试试
-
and_
成功 - 我们尝试
'|'
,and_ >> '|' >> or_
失败(一切都被丢弃) - 我们尝试
and_
(单独一个)- 一切如初
-
and_
成功
- 我们试试
-
or_
成功
这是只有两个二进制规则,你的情况更糟。
你可以这样写:
or_ = and_[_val=_1] >> -( '|' >> or_ )[_val=construct<binop<op_or> >(_val,_1)];
,尽管比以前更丑,但它不会丢弃任何匹配项。
一个问题,我不知道你是否注意到是,解析的结果是右关联的(意思是3-2-1
=> 3-(2-1)
)。我想应该是这样的:
or_ = and_[_val=_1] >> *( '|' >> and_)[_val=construct<binop<op_or> >(_val,_1)]; //note the `and_` instead of `or_` after '|'
可以解决这个问题,但是我还没有测试过。
也因为你安排规则的方式,你给了+
和-
比*
和/
更高的优先级。
试图解决这些问题(并删除语义动作)我已经提出了一个自定义指令,似乎工作,你会这样使用它:
or_ = fold<binop<op_or> >(xor_.alias())['|' >> xor_]; //sadly the `.alias()` is required
指令解析初始解析器(xor_.alias()
)并多次尝试主题。如果主题从未成功,则最终属性是初始解析器的属性。如果主题成功,则最终属性将为binop<op_or>(initial_attr,subject_attr)
/binop<op_or>(binop<op_or>(initial_attr,subject_attr1),subject_attr2)
/…
完整样本(在WandBox上运行)
custom_fold_directive.hpp
namespace custom
{
namespace tag
{
struct fold { BOOST_SPIRIT_IS_TAG() };
}
template <typename Exposed, typename Expr>
boost::spirit::stateful_tag_type<Expr, tag::fold, Exposed>
fold(Expr const& expr)
{
return boost::spirit::stateful_tag_type<Expr, tag::fold, Exposed>(expr);
}
}
namespace boost { namespace spirit
{
template <typename Expr, typename Exposed>
struct use_directive<qi::domain
, tag::stateful_tag<Expr, custom::tag::fold, Exposed> >
: mpl::true_ {};
}}
namespace custom
{
template <typename Exposed, typename InitialParser, typename RepeatingParser>
struct fold_directive
{
fold_directive(InitialParser const& initial, RepeatingParser const& repeating):initial(initial),repeating(repeating){}
template <typename Context, typename Iterator>
struct attribute
{
typedef typename boost::spirit::traits::attribute_of<InitialParser,Context,Iterator>::type type;//This works in this case but is not generic
};
template <typename Iterator, typename Context
, typename Skipper, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context& context, Skipper const& skipper, Attribute& attr_) const
{
Iterator start = first;
typename boost::spirit::traits::attribute_of<InitialParser,Context,Iterator>::type initial_attr;
if (!initial.parse(first, last, context, skipper, initial_attr))
{
first=start;
return false;
}
typename boost::spirit::traits::attribute_of<RepeatingParser,Context,Iterator>::type repeating_attr;
if(!repeating.parse(first, last, context, skipper, repeating_attr))
{
boost::spirit::traits::assign_to(initial_attr, attr_);
return true;
}
Exposed current_attr(initial_attr,repeating_attr);
while(repeating.parse(first, last, context, skipper, repeating_attr))
{
boost::spirit::traits::assign_to(Exposed(current_attr,repeating_attr),current_attr);
}
boost::spirit::traits::assign_to(current_attr,attr_);
return true;
}
template <typename Context>
boost::spirit::info what(Context& context) const
{
return boost::spirit::info("fold");
}
InitialParser initial;
RepeatingParser repeating;
};
}
namespace boost { namespace spirit { namespace qi
{
template <typename Expr, typename Exposed, typename Subject, typename Modifiers>
struct make_directive<
tag::stateful_tag<Expr, custom::tag::fold, Exposed>, Subject, Modifiers>
{
typedef custom::fold_directive<Exposed, Expr, Subject> result_type;
template <typename Terminal>
result_type operator()(Terminal const& term, Subject const& subject, Modifiers const&) const
{
typedef tag::stateful_tag<
Expr, custom::tag::fold, Exposed> tag_type;
using spirit::detail::get_stateful_data;
return result_type(get_stateful_data<tag_type>::call(term),subject);
}
};
}}}
main.cpp
//#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include "custom_fold_directive.hpp"
namespace qi = boost::spirit::qi;
// DEFINING TYPES
struct op_not {};
struct op_or {};
struct op_and {};
struct op_xor {};
struct op_equal {};
struct op_unequal {};
struct op_sum {};
struct op_difference {};
struct op_factor {};
struct op_division {};
namespace Expression{
typedef std::string var;
template <typename tag> struct binop;
template <typename tag> struct unop;
typedef boost::variant<var,
boost::recursive_wrapper<unop <op_not> >,
boost::recursive_wrapper<binop<op_equal> >,
boost::recursive_wrapper<binop<op_unequal> >,
boost::recursive_wrapper<binop<op_and> >,
boost::recursive_wrapper<binop<op_xor> >,
boost::recursive_wrapper<binop<op_or> >,
boost::recursive_wrapper<binop<op_difference> >,
boost::recursive_wrapper<binop<op_sum> >,
boost::recursive_wrapper<binop<op_factor> >,
boost::recursive_wrapper<binop<op_division> >
> expressionContainer;
template <typename tag> struct binop
{
explicit binop(const expressionContainer& l
, const expressionContainer& r)
: oper1(l), oper2(r) { }
expressionContainer oper1, oper2;
friend std::ostream& operator<<(std::ostream& os, const binop& val)
{
os << "(" << typeid(tag).name() << " " << val.oper1 << ", "<< val.oper2 << ")";
return os;
}
};
template <typename tag> struct unop
{
explicit unop(const expressionContainer& o) : oper1(o) { }
expressionContainer oper1;
friend std::ostream& operator<<(std::ostream& os, const unop& val)
{
os << "(" << typeid(tag).name() << " " << val.oper1 << ")";
return os;
}
};
}
// EXPRESSION PARSER
template <typename It, typename Skipper = boost::spirit::standard_wide::space_type>
struct parserExpression : qi::grammar<It, Expression::expressionContainer(), Skipper>
{
parserExpression() : parserExpression::base_type(expr_)
{
using namespace qi;
using namespace Expression;
using custom::fold;
expr_ = or_.alias();
// Logical Operators
or_ = fold<binop<op_or> >(xor_.alias())[orOperator_ >> xor_];
xor_ = fold<binop<op_xor> >(and_.alias())[xorOperator_ >> and_];
and_ = fold<binop<op_and> >(equal_.alias())[andOperator_ >> equal_];
equal_ = fold<binop<op_equal> >(unequal_.alias())[equalOperator_ >> unequal_];
unequal_ = fold<binop<op_unequal> >(sum_.alias())[unequalOperator_ >> sum_];
// Numerical Operators
sum_ = fold<binop<op_sum> >(difference_.alias())[sumOperator_ >> difference_];
difference_ = fold<binop<op_difference> >(factor_.alias())[differenceOperator_ >> factor_];
factor_ = fold<binop<op_factor> >(division_.alias())[factorOperator_ >> division_];
division_ = fold<binop<op_division> >(not_.alias())[divisionOperator_ >> not_];
// UNARY OPERATIONS
not_ = (notOperator_ > simple) [_val = boost::phoenix::construct<Expression::unop <op_not>>(_1)] | simple[_val = _1];
simple = (('(' > expr_ > ')') | var_);
var_ = qi::lexeme[+alnum];
notOperator_ = qi::char_('!');
andOperator_ = qi::string("&&");
orOperator_ = qi::string("||");
xorOperator_ = qi::char_("^");
equalOperator_ = qi::string("==");
unequalOperator_ = qi::string("!=");
sumOperator_ = qi::char_("+");
differenceOperator_ = qi::char_("-");
factorOperator_ = qi::char_("*");
divisionOperator_ = qi::char_("/");
BOOST_SPIRIT_DEBUG_NODES((expr_)(or_)(xor_)(and_)(equal_)(unequal_)(sum_)(difference_)(factor_)(division_)(simple)(notOperator_)
(andOperator_)(orOperator_)(xorOperator_)(equalOperator_)(unequalOperator_)(sumOperator_)(differenceOperator_)(factorOperator_)(divisionOperator_));
}
private:
qi::rule<It, Expression::var(), Skipper> var_;
qi::rule<It, Expression::expressionContainer(), Skipper> not_
, and_
, xor_
, or_
, equal_
, unequal_
, sum_
, difference_
, factor_
, division_
, simple
, expr_;
qi::rule<It, Skipper> notOperator_
, andOperator_
, orOperator_
, xorOperator_
, equalOperator_
, unequalOperator_
, sumOperator_
, differenceOperator_
, factorOperator_
, divisionOperator_;
};
void parse(const std::string& str)
{
std::string::const_iterator iter = str.begin(), end = str.end();
parserExpression<std::string::const_iterator,qi::space_type> parser;
Expression::expressionContainer expr;
bool result = qi::phrase_parse(iter,end,parser,qi::space, expr);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << str << " => " << expr << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
}
}
int main()
{
parse("(1)");
parse("3-2-1");
parse("a+b*c");
parse("a*b+c");
parse("(a+b)*c");
parse("a*b+c*(d+e)&&true||false");
}
相关文章:
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- Boost Spirit,获取迭代器内部语义动作
- 返回值优化:显式移动还是隐式
- 人脸跟踪arduino代码的优化
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 如何以优化的方式同时迭代两个间距不相等的数组
- boost::spirit::karma 替代生成器,带有 boost::variant 由字符串和字符串别名组成
- 小字符串优化(调试与发布模式)
- 浮点定向舍入和优化
- Visual Studio 调试优化如何工作?
- boost::spirit::x3 中的通用解析器生成器
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 线性优化目标函数中的绝对值
- GCC 会优化内联访问器吗?
- gcc 如何优化此循环?
- Boost::Spirit:优化表达式解析器