使用parser-ts的P.many解析器陷入无限循环,请求排查
parser-ts P.many无限挂起问题解决及令牌区分方案
问题原因
你的代码里S.spaces是可空解析器——它可以匹配0个空格并成功,且不消耗任何输入。当把包含它的everything传给P.many时,一旦遇到无法匹配Ch.alphanum的位置(比如hello 之后的[),S.spaces会反复匹配0个空格成功,导致P.many无限循环,永远无法终止。
修复无限挂起的代码
把S.spaces替换为非空的单个空格解析器Ch.space,确保每次匹配都消耗输入:
import * as P from 'parser-ts/Parser' import * as Ch from 'parser-ts/char' import * as S from 'parser-ts/string' import { pipe } from 'fp-ts/function' const everything = pipe( Ch.alphanum, P.alt(() => Ch.space) ); const input1 = `hello [123]`; const res = run(P.many(everything), input1); // 现在会正常返回匹配结果"hello " console.log(JSON.stringify(res, null, 2));
实现令牌区分的最终方案
要区分类似[123]的令牌和普通文本,需要优先匹配令牌,再匹配普通文本(且普通文本遇到令牌开头时停止):
import * as P from 'parser-ts/Parser' import * as Ch from 'parser-ts/char' import { pipe } from 'fp-ts/function' // 匹配[数字]格式的令牌 const bracketToken = pipe( Ch.char('['), P.chain(() => P.many(Ch.digit)), P.chain(digits => pipe( Ch.char(']'), P.map(() => `[${digits.join('')}]`) )) ); // 匹配普通文本(字母数字+空格,遇到[则停止) const normalText = pipe( P.many(pipe( Ch.alphanum, P.alt(() => Ch.space), // 预判下一个字符不是[,避免吞掉令牌开头 P.apFirst(P.notFollowedBy(Ch.char('['))) )), P.map(chars => chars.join('')) ); // 整体解析器:优先匹配令牌,否则匹配普通文本 const parser = P.alt(() => bracketToken, () => normalText); const input = `hello [123] world [456]`; const result = run(P.many(parser), input); // 输出结果:["hello ", "[123]", " world ", "[456]"] console.log(JSON.stringify(result, null, 2));
关键注意事项
- 禁止将可空解析器传入
P.many,这是parser-ts中常见的无限循环诱因。 - 令牌匹配要优先于普通文本匹配,避免普通文本误吞令牌的起始字符。
- 使用
P.notFollowedBy做预判,可以精准控制解析器的终止时机。
内容的提问来源于stack exchange,提问作者dark_ruby
相关产品推荐
相关产品推荐

