Superpower Parser如何解决子解析器部分匹配引发的组合解析失败问题?
问题根因
解析失败的核心来自 Superpower 解析器的零长度失败设计规则:
- 所有循环类组合器(如
Many()、Repeat())判断“是否可以正常终止循环”的唯一标准是:子解析器在当前输入位置没有消费任何字符就返回失败 - 一旦子解析器已经消费了至少1个字符后才返回失败,组合器会直接把错误冒泡,中断整个解析流程
代码走到文件最后一行的单独#时,流程完全踩中这个规则:
- 外层
record.Many()循环尝试匹配下一个record - 进入
propertyList逻辑,第一步就匹配成功吃到了#字符 - 接下来需要匹配至少1位数字作为属性ID,但
#后面没有任何内容,匹配失败 - 由于
propertyList已经消费了1个字符(那个#),失败属于“非零长度失败”,Superpower直接抛出解析错误,根本走不到最外层原本预留的匹配结束#的逻辑。
修复方案
1. 为propertyList增加匹配回退能力
用Try()完整包裹propertyList的解析逻辑,实现“匹配中途失败时自动回退解析位置到开始状态”的能力,让循环逻辑可以正常识别“当前位置不是合法属性行”的判断:
public static TextParser<TagProperies> propertyList = ( from _ in hash from id in Character.Digit.AtLeastOnce() from endOfId in pipe from name in text from endOfName in pipe from otherItems in itemList select new TagProperies(Convert.ToInt32(new string(id)), name, otherItems) ).Try();
若遇到静态字段初始化顺序导致的依赖未赋值问题,可使用
Superpower.Parse.Ref(() => 上述解析逻辑)做延迟引用。
加上Try()后,走到最后那个单独#的流程会发生变化:
- 尝试匹配
propertyList:吃到#后发现没有数字,匹配失败 - 因为有
Try()机制,解析位置自动回退到#之前的位置,对上层Many()来说,这次尝试完全没有消费输入,属于正常的循环终止条件,不会触发错误冒泡。
2. 优化外层文件解析器的结束匹配逻辑
原有的结束逻辑为匹配单独的#作为文件终止标记,可增加对可选换行的兼容,避免文件结尾格式差异导致的解析失败:
public static TextParser<TagRecord[]> TagRecordsFileParser = from _ in firstLine from records in record.Many() from end in hash.IgnoreThen(eol.Optional()) select records;
修复后效果
- 完整解析文件中所有合法的
propertyList开头的record行 - 走到最后单独的
#位置时,循环尝试匹配record,依靠Try()的回退机制正常终止循环 - 外层逻辑匹配到这个单独的
#作为文件结束标记,可选匹配后面的换行,整个解析流程顺利完成
最后一行的单独#因为内容长度过短,永远不会被其他子解析器(如需要匹配至少1位字母的heading、需要管道分隔符的itemList)误匹配,不需要额外增加特殊判断逻辑。
内容的提问来源于stack exchange,提问作者TheColonel26
相关产品推荐
相关产品推荐

