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

使用Spirit qi::success回调在规则匹配后设置字段

我明白你想通过qi::on_success回调在匹配_literal或_rule_name时设置Term的term_type字段。问题出在你没有正确为每个规则绑定对应的回调,而且当前的规则定义没有确保词法单元不受skipper干扰。下面是修正后的完整代码,以及关键修改的解释:

完整修正代码

// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;
namespace Ast {
enum class TermType { literal, rule_name };
struct Term {
    std::string data;
    TermType term_type;
};
using List = std::list<Term>;
using Expression = std::list<List>;
struct Rule {
    Term name; // lhs
    Expression rhs;
};
using Syntax = std::list<Rule>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Term, data)
BOOST_FUSION_ADAPT_STRUCT(Ast::Rule, name, rhs)

namespace Parser {
template<typename Iterator>
struct BNF : qi::grammar<Iterator, Ast::Syntax()> {
    BNF() : BNF::base_type(start) {
        using namespace qi;
        using namespace Ast;

        _blank = blank;
        _skipper = blank | (eol >> !skip(_blank.alias())[_rule]);
        start = skip(_skipper.alias())[_rule % +eol];
        _rule = _rule_name >> "::=" >> _expression;
        _expression = _list % '|';
        _list = +(_literal | _rule_name);

        // 用lexeme包裹词法单元,避免skipper干扰内部匹配
        _literal = lexeme[
            '"' >> *(_character - '"') >> '"' 
            | "'" >> *(_character - "'") >> "'"
        ];
        _rule_name = lexeme[
            '<' >> (alpha >> *(alnum | char_('-'))) >> '>'
        ];

        _character = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\\^_`{}~[-");

        // 为_literal绑定成功回调,设置term_type为literal
        qi::on_success(_literal, [](auto& ctx) {
            _val(ctx).term_type = TermType::literal;
        });

        // 为_rule_name绑定成功回调,设置term_type为rule_name
        qi::on_success(_rule_name, [](auto& ctx) {
            _val(ctx).term_type = TermType::rule_name;
        });

        BOOST_SPIRIT_DEBUG_NODES( (_rule)(_expression)(_list)(_literal) (_character) (_rule_name))
    }
private:
    using Skipper = qi::rule<Iterator>;
    Skipper _skipper, _blank;
    qi::rule<Iterator, Ast::Syntax()> start;
    qi::rule<Iterator, Ast::Rule(), Skipper> _rule;
    qi::rule<Iterator, Ast::Expression(), Skipper> _expression;
    qi::rule<Iterator, Ast::List(), Skipper> _list;
    // 词法单元规则
    qi::rule<Iterator, Ast::Term()> _literal;
    qi::rule<Iterator, Ast::Term()> _rule_name;
    qi::rule<Iterator, char()> _character;
};
}

int main() {
    Parser::BNF<std::string::const_iterator> const parser;
    std::string const input = R"(<code> ::= <letter><digit> | <letter><digit><code>
<letter> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i"
<digit> ::= "0" | "1" | "2" | "3" | "4"
)";
    auto it = input.begin(), itEnd = input.end();
    Ast::Syntax syntax;
    if (parse(it, itEnd, parser, syntax)) {
        for (auto &rule : syntax) {
            std::cout << rule.name.data << " ::= ";
            std::string sep;
            for (auto &list : rule.rhs) {
                std::cout << sep;
                for (auto &term: list) {
                    std::cout << term.data;
                    // 取消注释可以验证term_type是否正确设置
                    // std::cout << "(" << (term.term_type == Ast::TermType::literal ? "literal" : "rule_name") << ")";
                }
                sep = " | ";
            };
            std::cout << "\n";
        }
    } else {
        std::cout << "Failed\n";
    }
    if (it != itEnd)
        std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}

关键修改说明

  1. 给词法单元添加lexeme:
    _literal和_rule_name属于独立词法单元,用lexeme包裹后会临时禁用skipper,避免匹配过程中空白字符干扰引号、尖括号内部的内容解析。

  2. 绑定on_success回调:
    分别为两个规则设置lambda回调,在匹配成功后通过_val(ctx)获取当前规则返回的Ast::Term对象,直接修改其term_type字段,实现你想要的区分逻辑。

  3. 简化Fusion适配:
    你原来的BOOST_FUSION_ADAPT_STRUCT(Ast::Term, data)只适配data字段是合理的,因为term_type由我们手动设置,Spirit会自动将匹配到的字符串赋值给data。

可选替代方案(无需回调)

如果你觉得回调写法繁琐,也可以直接在规则中构造Ast::Term对象,同时赋值data和term_type:

// 需要额外包含boost/phoenix.hpp
#include <boost/phoenix.hpp>

// 替换原来的_literal、_rule_name定义和on_success代码
_literal = lexeme[
    ( '"' >> *(_character - '"') >> '"' | "'" >> *(_character - "'") >> "'" )
    [ _val = boost::phoenix::construct<Ast::Term>(_1, Ast::TermType::literal) ]
];
_rule_name = lexeme[
    ( '<' >> (alpha >> *(alnum | char_('-'))) >> '>' )
    [ _val = boost::phoenix::construct<Ast::Term>(_1, Ast::TermType::rule_name) ]
];

这种方式把赋值逻辑直接嵌入规则,更直观,不过需要依赖Boost Phoenix库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 08:44:08