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

Haskell Parsec解析器异常:输入意外结束问题排查

Haskell Parsec解析器错误排查

问题场景

原本可正常运行的Haskell Parsec解析器,在为非终结符添加位置信息、新增parseLexeme(用于忽略终结符首尾空白)后,输入func f(x) = 3时抛出错误:

(line 1, column 14):
unexpected end of input
expecting white space, "." or digit

核心错误点及修正

1. parseRealNum逻辑错误

原代码中实数解析存在两个问题:

  • 错误使用parseLexeme包裹内部解析步骤,导致解析整数部分后会强制消耗空白,而实数的整数部分与小数点之间不允许有空白;
  • 拼接实数字符串时遗漏了整数部分与小数点,且返回值错误使用小数部分而非完整实数。

修正后的parseRealNum:

parseRealNum :: CalcLangLexer Token
parseRealNum = parseLexeme $ do
               startPos <- getPosition
               integerPart <- many1 parseDigit
               char '.'  -- 实数的小数点与前后数字无空白,直接解析
               fractionalPart <- many1 parseDigit  -- 小数部分至少一个数字
               let num = integerPart ++ "." ++ fractionalPart
               return (RealNum num startPos)

2. parseTimes返回值错误

原代码中解析乘法符号*时,错误返回Minus类型的Token,会导致后续解析逻辑混乱。

修正后的parseTimes:

parseTimes :: CalcLangLexer Token
parseTimes = parseLexeme $ do
             startPosition <- getPosition
             char '*' >> return (Times startPosition)

3. 终结符解析器的parseLexeme误用

原代码中parseIdent、parseIntNum等终结符解析器,在内部步骤重复使用parseLexeme,导致不必要的空白消耗,破坏了标识符、整数的连续解析逻辑。正确的做法是将整个终结符的核心解析逻辑包裹在parseLexeme中,统一处理首尾空白。

修正后的parseIdent:

parseIdent :: CalcLangLexer Token
parseIdent = parseLexeme $ do
             startPos <- getPosition
             firstChar <- letter
             restChars <- many alphaNum
             let ident = firstChar : restChars
             return (Ident ident startPos)

修正后的parseIntNum:

parseIntNum :: CalcLangLexer Token
parseIntNum = parseLexeme $ do
              startPos <- getPosition
              result <- many1 parseDigit
              return (IntNum result startPos)

4. parseDot的parseLexeme误用

原代码中parseDot使用parseLexeme包裹,导致实数中的小数点会强制消耗前后空白,不符合实数的语法规则(如3.14合法,3 . 14不合法)。应改为直接解析字符.,仅在单独作为Dot Token时使用parseLexeme。

修正后的parseDot与parsePeriod:

parseDot :: CalcLangLexer Char
parseDot = char '.'

parsePeriod :: CalcLangLexer Token
parsePeriod = parseLexeme $ do
              start <- getPosition
              parseDot >> return (Dot start)

修正后的解析流程验证

修正后,输入func f(x) = 3的解析流程可正常执行:

  1. 匹配Func Token("func");
  2. 匹配Ident Token("f");
  3. 匹配LPar Token("(");
  4. 解析参数列表中的IdentAst("x");
  5. 匹配RPar Token(")");
  6. 匹配Eq Token("=");
  7. 解析表达式中的IntNumber("3");
  8. 最终生成正确的FunctionDef AST节点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 12:45:53