如何在boost::spirit中实现带优先级且支持短路求值的逻辑表达式解析
如何在boost::spirit中实现带优先级且支持短路求值的逻辑表达式解析
嘿,我正好之前在Boost.Spirit里折腾过类似的逻辑表达式解析问题,你的情况我太懂了——优先级搞反、短路求值失效,这俩是新手用Spirit处理逻辑表达式时最容易踩的坑。我来给你拆解解决办法:
首先得解决运算符优先级的问题。你现在把||和&&放在同一个规则层级里,Spirit会默认按左到右的顺序解析,这就导致1 || 0 && 0被当成(1 || 0) && 0,结果自然是0,完全不符合&&优先级高于||的预期。
正确的做法是像处理算术表达式那样分层定义规则,把高优先级的运算符放在底层规则里:
- 最底层是「原子表达式」:处理单个数字(比如
uint_)或者括号包裹的完整表达式 - 中间层是「逻辑与表达式」:由原子表达式通过
&&连接,确保&&先被解析 - 最上层是「逻辑或表达式」:由逻辑与表达式通过
||连接,这样||的优先级就低于&&了
接下来是短路求值的实现。Spirit默认会贪婪地解析所有内容,要实现短路(比如||左边为true时跳过右边,&&左边为false时跳过右边),得用qi::eps结合语义动作里的当前值,动态控制是否继续解析后续的操作数。
给你看具体的代码实现示例:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <string> namespace qi = boost::spirit::qi; namespace phoenix = boost::phoenix; template <typename Iterator> struct LogicalGrammar : qi::grammar<Iterator, bool()> { LogicalGrammar() : LogicalGrammar::base_type(expr) { // 原子表达式:数字转bool(非0为true),或括号包裹的表达式 atom = qi::uint_ [qi::_val = (qi::_1 != 0)] | '(' >> expr >> ')' ; // 逻辑与表达式:实现短路——当前值为false时,跳过后续&&和原子 logical_and = atom [qi::_val = qi::_1] >> *( qi::eps(qi::_val) // 只有当前结果为true,才继续解析下一个&& >> "&&" >> atom [qi::_val = qi::_val && qi::_1] ) ; // 逻辑或表达式:实现短路——当前值为true时,跳过后续||和逻辑与 logical_or = logical_and [qi::_val = qi::_1] >> *( qi::eps(!qi::_val) // 只有当前结果为false,才继续解析下一个|| >> "||" >> logical_and [qi::_val = qi::_val || qi::_1] ) ; // 顶层表达式指向逻辑或 expr = logical_or; } qi::rule<Iterator, bool()> atom, logical_and, logical_or, expr; }; // 测试用例 int main() { std::string test1 = "1 || 0 && 0"; LogicalGrammar<std::string::iterator> grammar; bool result; auto it = test1.begin(); bool success = qi::parse(it, test1.end(), grammar, result); if (success && it == test1.end()) { // 正确结果应该是1(true) std::cout << "Result: " << std::boolalpha << result << std::endl; } return 0; }
我来给你解释这段代码的关键:
- 分层规则保证了
&&先被解析,1 || 0 && 0会被正确处理成1 || (0 && 0),结果为true qi::eps(qi::_val)在逻辑与规则里,只有当前结果为true时,才会尝试解析后面的&&和原子表达式;如果当前是false,eps会让解析失败,直接跳过后续部分,实现&&的短路qi::eps(!qi::_val)在逻辑或规则里,只有当前结果为false时,才会继续解析后面的||和逻辑与表达式;如果当前是true,直接跳过,实现||的短路
最后再提一句你原来的规则问题:把所有运算符塞在同一个规则里,不仅优先级混乱,而且无法控制解析的停止时机,自然做不到短路。分层+动态解析控制才是正确的路子。
内容来源于stack exchange
相关产品推荐
相关产品推荐

