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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 23:10:31