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的解析流程可正常执行:
- 匹配
FuncToken("func"); - 匹配
IdentToken("f"); - 匹配
LParToken("("); - 解析参数列表中的
IdentAst("x"); - 匹配
RParToken(")"); - 匹配
EqToken("="); - 解析表达式中的
IntNumber("3"); - 最终生成正确的
FunctionDefAST节点。
内容的提问来源于stack exchange,提问作者Jacob Bauer
相关产品推荐
相关产品推荐

