Haskell Parsec十六进制字符串解析器不符合预期问题排查与修复
问题原因分析
你的解析器会在遇到非法字符时返回部分匹配结果,核心原因是Parsec的默认行为是匹配输入的前缀部分,不会自动检查整个输入是否被完全消耗。
拿第三次测试的#xcafexbeef来说,解析器的执行流程是:
- 成功匹配开头的
#x; - 用
many1 hexDigit匹配到cafe(都是合法十六进制字符),直到遇到第一个非十六进制字符x就停止匹配; - 直接返回解析结果,完全忽略了后面的
xbeef部分——因为你的解析规则没有要求必须读到输入的结尾。
修复方案
要让解析器在存在任何无效字符时直接判定不匹配,只需要在parseHex的末尾添加eof解析器,强制要求整个输入必须被完全解析,不能有剩余字符。
eof是Parsec提供的内置解析器,它会检查当前是否已经到达输入的末尾,如果还有未解析的字符,就会抛出解析错误。
修改后的完整代码如下:
module Main where import Control.Monad import Numeric import System.Environment import Text.ParserCombinators.Parsec hiding (spaces) parseHex :: Parser Integer parseHex = do string "#x" x <- many1 hexDigit eof -- 新增:确保输入被完全消耗,无剩余字符 return (fst (head (readHex x))) testHex :: String -> String testHex input = case parse parseHex "lisp" input of Left err -> "Does not match " ++ show err Right val -> "Matched " ++ show val -- 加个空格让输出更美观 main :: IO () main = do args <- getArgs putStrLn (testHex (head args))
测试验证
现在重新测试第三次用例,结果就符合预期了:
*Main> testHex "#xcafexbeef" "Does not match \"lisp\" (line 1, column 7):\nunexpected \"x\"\nexpecting end of input"
解析器因为输入未被完全消耗,直接返回错误,不再返回部分匹配的结果。
另外前两个测试用例依然正常工作:
*Main> testHex "#xcafebeef" "Matched 3405692655" *Main> testHex "#xnothx" "Does not match \"lisp\" (line 1, column 3):\nunexpected \"n\"\nexpecting hexadecimal digit"
内容的提问来源于stack exchange,提问作者Bhargav Kulkarni
相关产品推荐
相关产品推荐

