如何基于Dart的Petit Parser实现带例外的分隔符终止消费
用Petit Parser实现支持单引号转义的分隔符感知词法分析器
你的基础实现仅处理了分隔符的直接否定匹配,未考虑单引号内的内容会屏蔽分隔符,也没处理单引号的转义逻辑。要实现需求,需拆分出单引号段的解析逻辑,确保单引号内的分隔符不触发终止,同时支持转义单引号。
完整实现代码
import 'package:petitparser/petitparser.dart'; Parser<String> fpLexer(List<String> delimiters) { // 优先匹配更长的分隔符,避免短分隔符提前截断长分隔符 final delimiterChoice = delimiters .sorted((a, b) => b.length.compareTo(a.length)) .map(string) .toChoiceParser(); // 匹配转义单引号:\' final escapeSingleQuote = (char(r'\') & char("'")).map((pair) => pair.join()); // 匹配单引号内的内容:允许转义单引号或非单引号字符 final singleQuotedContent = (escapeSingleQuote | char("'").neg()) .star() .map((chars) => chars.join()); // 匹配完整的单引号段:'...'(包含内部转义) final singleQuoted = (char("'") & singleQuotedContent & char("'")) .map((parts) => '${parts[0]}${parts[1]}${parts[2]}'); // 匹配普通字符:既不是分隔符也不是单引号的单个字符 final normalChar = (delimiterChoice | char("'")) .neg() .map((char) => char.toString()); // 匹配可消费的内容:由普通字符段或单引号段组成,重复直到遇到分隔符 final content = (normalChar.plus() | singleQuoted) .star() .map((segments) => segments.join()); return content; }
代码逻辑说明
- 分隔符优先级处理:先对分隔符按长度降序排序,确保长分隔符(比如
then)比短分隔符(比如单个字符)先被匹配,避免出现then被拆分为the+n的错误匹配。 - 转义单引号解析:单独匹配
\',确保单引号内的转义单引号被视为普通内容,不会提前终止单引号段。 - 单引号段解析:匹配完整的单引号包裹内容,内部允许转义单引号或任意非单引号字符,整个单引号段会被当作普通内容消费,不会触发分隔符检查。
- 普通字符解析:匹配既不是分隔符也不是单引号的字符,确保这些字符被正常消费,直到遇到单引号或分隔符。
- 内容组合:重复匹配普通字符段或单引号段,直到遇到第一个有效分隔符(此时无法继续匹配,解析停止)。
测试示例验证
对于输入字符串:
'<a href=\"http://www.w3.org/1999/xhtml\">then</a>' some "Link"
指定分隔符为["\"", "then"]时,解析器会消费到"Link"前的",最终得到的内容是:
'<a href=\"http://www.w3.org/1999/xhtml\">then</a>' some
原因是:
- 单引号内的转义双引号
\"和单词then都属于单引号段的一部分,不会被视为有效分隔符。 - 第一个触发终止的有效分隔符是
"Link"前的未被包裹的"。
内容的提问来源于stack exchange,提问作者Grey
相关产品推荐
相关产品推荐

