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

Parsec解析器公共前缀双分支场景下空格处理异常问题求解

问题根源

Parsec 的 <|> 选择运算符默认不支持消费输入后的自动回溯:只有当前一个分支完全没有消费任何输入就失败时,才会回滚输入指针尝试下一个分支;如果前一个分支已经消费了部分输入后失败,会直接抛出错误,不会进入后续分支。

你遇到的错误原因非常明确:
第一个分支首先执行 spaces 已经把输入开头的所有空格消费掉,输入指针移动到了第一个非空格字符的位置(也就是 " Bs" 中的大写 B)。接下来执行 lookAhead lower 匹配失败,此时第一个分支已经消费过输入,<|> 不会回滚指针,不会进入第二个分支,直接抛出匹配错误。哪怕你在第二个分支也加了 spaces 也没用,因为程序根本不会走到第二个分支的逻辑。

正确实现方案

方案1(推荐):公共前缀提取

前导空格是两个分支的公共匹配逻辑,直接提到选择分支外层统一处理,避免重复逻辑也不需要回溯:

testP :: Parser String
testP = do
    -- 先统一消费所有前导空格
    spaces
    -- 再在同一个输入位置执行两个分支的选择
    (do
        lookAhead lower
        a1 <- lower
        a2 <- lower
        return [a1,a2]
    ) <|> (do
        a1 <- upper
        a2 <- lower
        return [a1,a2]
    )

这个方案性能最优,没有额外的回溯开销。

方案2:显式开启回溯

如果你出于某种原因需要把空格逻辑写在每个分支内,可以给第一个分支套上 try 函数,强制分支失败时回滚输入指针:

testP :: Parser String
testP = (try $ do
            spaces
            lookAhead lower
            a1 <- lower
            a2 <- lower
            return [a1,a2]
        ) <|> (do
            spaces
            a1 <- upper
            a2 <- lower
            return [a1,a2]
        )

注意只有需要回溯的分支才需要加 try,过多使用 try 会降低解析器性能。

内容的提问来源于stack exchange,提问作者xsimplex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 05:24:03