You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Boost Spirit Qi在OR条件下尝试所有规则以解析命令?

解决Boost Spirit Qi解析分支回溯问题

问题描述

需要解析以下SendCmd命令模板:

SendCmd SomeCommand Left_Side = "Some Value";
SendCmd AnotherCmd "Some Literal" = Some_Value;
SendCmd AnotherCmd "Some Literal" = Some_Value "Other Literal" = "Something";
SendCmd SomeCommand Just_object_name;

现有代码可成功解析后三种模板,但第一种解析失败。核心问题出在send_cmd_规则的(object_ | gen_asgns_)分支:解析第一个测试用例的Left_Side = "Some Value"时,object_成功匹配Left_Side后,后续无法匹配分号,但Spirit未回溯尝试gen_asgns_分支;调换分支顺序则会导致第四个测试用例失败,使用qi::hold[object_]也无效。

原核心规则代码:

send_cmd_ = no_case["sendcmd"] >> (literal_ | object_) >> (object_ | gen_asgns_)
    >> ';'
    ;

运行结果:

Case #1
SendCmd SomeCommand Left_Side = "Some Value"; [INVALID]
Case #2
SendCmd AnotherCmd "Some Literal" = Some_Value; [Success]
Case #3
SendCmd AnotherCmd "Some Literal" = Some_Value "Other Literal" = "Something"; [Success]
Case #4
SendCmd SomeCommand Just_object_name; [Success]

问题分析

Boost Spirit Qi基于PEG(Parsing Expression Grammar),PEG的分支选择是优先匹配第一个成功的分支,一旦某个分支的前缀部分匹配成功,即使后续规则失败,也不会回溯尝试下一个分支。

在原规则中:

  • 当解析第一个测试用例的Left_Side = "Some Value"时,object_先匹配了Left_Side,此时分支已消耗部分输入,后续的';'无法匹配,整个send_cmd_规则失败,不会尝试gen_asgns_分支。
  • 若调换分支顺序为(gen_asgns_ | object_),gen_asgns_是*gen_asgn_(零或多个赋值),会优先匹配零个赋值,导致第四个测试用例的Just_object_name无法被匹配,最终因无法匹配';'而失败。

解决方案

通过预判后续输入来明确分支匹配条件:让object_分支仅在匹配后紧跟分号时才生效,否则尝试gen_asgns_分支。使用qi::lit结合&(正向预测先行断言)实现这一逻辑。

修改后的send_cmd_规则如下:

send_cmd_ = no_case["sendcmd"] >> (literal_ | object_) 
    >> (object_ >> &lit(';') | gen_asgns_)
    >> ';'
    ;

完整修改后的核心代码

namespace client {
    template <typename Itr> struct DML : qi::grammar<Itr, Ast::SendCmd()> {
        DML() : DML::base_type(start) {
            using namespace qi;

            start = skip(space)[send_cmd_];
            ident_ = raw[alpha >> *(alnum | '_')];
            number_ = double_;

            literal_ = '"' > *('\'' >> char_ | ~char_('"')) > '"';
            object_ = ident_ >> *('[' >> ident_ >> ']');
            gen_asgn_ = (literal_ | object_) >> '=' >> (literal_ | number_ | object_);
            gen_asgns_ = *gen_asgn_;

            // 修改后的send_cmd_规则,添加正向预测断言
            send_cmd_ = no_case["sendcmd"] >> (literal_ | object_) 
                >> (object_ >> &lit(';') | gen_asgns_)
                >> ';'
                ;

            BOOST_SPIRIT_DEBUG_NODES(
                (ident_)(literal_)(number_)(object_)(gen_asgn_)(send_cmd_)
            )
        }

    private:
        qi::rule<Itr, Ast::SendCmd()> start;
        using Skipper = qi::space_type;
        qi::rule<Itr, Ast::Literal()>       literal_;
        qi::rule<Itr, Ast::Number()>        number_;
        qi::rule<Itr, Ast::Ident()>         ident_;
        qi::rule<Itr, Ast::GenericAssignment(), Skipper>    gen_asgn_;
        qi::rule<Itr, Ast::GenAssignments(), Skipper>           gen_asgns_;
        qi::rule<Itr, Ast::SendCmd(), Skipper>                      send_cmd_;
        qi::rule<Itr, Ast::Object(), Skipper>                           object_;
    };
}

修改后运行结果

Case #1
SendCmd SomeCommand Left_Side = "Some Value"; [Success]
Case #2
SendCmd AnotherCmd "Some Literal" = Some_Value; [Success]
Case #3
SendCmd AnotherCmd "Some Literal" = Some_Value "Other Literal" = "Something"; [Success]
Case #4
SendCmd SomeCommand Just_object_name; [Success]

内容的提问来源于stack exchange,提问作者Dylan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:02:33