Boost Spirit X3 repeat指令解析非法输入未按预期失败问题
问题根因
你当前的实现存在两个核心逻辑错误,导致解析行为不符合预期:
- 未强制匹配到输入末尾:X3的
parse接口默认只要从起始位置匹配到合法前缀就返回成功,不会检查是否存在剩余未解析内容。这是超长输入、下划线结尾输入被误判为成功的直接原因——解析器只消费了前缀合法部分,直接丢弃了后面的非法内容。repeat指令本身行为符合设计,它的上下限限制的是重复段的匹配次数,你之前设置的0-5次重复本身符合6个数字上限的要求,只是因为没有末尾检查,匹配到重复上限后剩余的非法内容被忽略了。 - 规则逻辑未从语法层面杜绝下划线结尾:原重复段逻辑没有强制要求下划线出现后必须跟随数字直到规则结束,结合无末尾检查的问题,下划线结尾的输入自然会漏判。
正确实现
首先明确两个必要修正点:
- 所有待匹配的完整规则末尾追加
x3::eoi(输入结束匹配符),只要存在未被消费的剩余输入,直接判定解析失败 - 严格对齐EBNF定义写integer的重复逻辑:每轮重复固定为「可选下划线 + 必须出现的数字」,从语法层面保证下划线不可能出现在字面量末尾,同时repeat的上限直接对应允许的最大数字个数减1(因为第一个数字已经在repeat外匹配)
- 用
x3::raw包裹整个integer规则,即可直接拿到包含下划线的完整原始字面量字符串/迭代器范围,完全满足后续AST存储需求。
对应可直接运行的实现代码:
#include <boost/spirit/home/x3.hpp> #include <iostream> #include <string_view> namespace x3 = boost::spirit::x3; using namespace std::string_view_literals; int main() { constexpr int max_integer_digits = 6; // 配置允许的最大数字个数 for (std::string_view const s : { "0"sv, "10"sv, "1_0"sv, "012345"sv, // 合法用例 "0123456"sv, "1_2_3_4_5_6_7_8_9_0"sv, // 非法:数字个数超限 "1_2_3_4_5_6_"sv, "_0123_456"sv, ""sv // 非法:格式错误 }) { auto const digit = x3::char_("0-9"); std::string parsed_literal; // 严格对齐EBNF: integer ::= digit { [underline] digit } auto const integer_rule = x3::raw[ digit >> x3::repeat(0, max_integer_digits - 1)[ -('_' >> digit) >> digit // 每轮:可选下划线,必须跟数字 ] ] >> x3::eoi; // 强制消费全部输入 bool parse_ok = x3::parse(s.begin(), s.end(), integer_rule, parsed_literal); std::cout << s << " -> " << parsed_literal << " (" << std::boolalpha << parse_ok << ")\n"; } }
运行后输出完全符合预期:
0 -> 0 (true) 10 -> 10 (true) 1_0 -> 1_0 (true) 012345 -> 012345 (true) 0123456 -> 012345 (false) 1_2_3_4_5_6_7_8_9_0 -> 1_2_3_4_5_6 (false) 1_2_3_4_5_6_ -> 1_2_3_4_5_6 (false) _0123_456 -> (false) -> (false)
其他问题说明
- 关于
BOOST_SPIRIT_X3_DEBUG无输出:该宏仅对通过BOOST_SPIRIT_DECLARE/BOOST_SPIRIT_DEFINE宏定义的命名空间作用域规则生效,直接在parse调用中写临时组合的规则表达式不会触发调试输出,将integer规则拆分为独立的全局规则即可正常打印调试日志。 - 如果需要限制的是字面量总长度(包含下划线)而非数字个数,不需要手动写迭代器计数,直接给
raw匹配结果加语义检查即可,示例:constexpr size_t max_total_len = 12; // 包含下划线的总长度上限 auto const check_length = [](auto& ctx) { if (x3::_attr(ctx).size() > max_total_len) { x3::_pass(ctx) = false; } }; auto const integer_rule = x3::raw[/* 核心integer匹配逻辑 */][check_length] >> x3::eoi;
内容的提问来源于stack exchange,提问作者Olx
相关产品推荐
相关产品推荐

