用 Haskell 写的解析器未按预期工作。

5

我在尝试使用Haskell的parsec库。我试图将形如"#x[0-9A-Fa-f]*"的十六进制字符串解析为整数。这是我认为可以工作的代码:

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
  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))

然后我尝试在Haskell的repl中测试testHex函数:

GHCi, version 8.6.5: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( src/Main.hs, interpreted )
Ok, one module loaded.
*Main> testHex "#xcafebeef"
"Matched3405692655"
*Main> testHex "#xnothx"
"Does not match \"lisp\" (line 1, column 3):\nunexpected \"n\"\nexpecting hexadecimal digit"
*Main> testHex "#xcafexbeef"
"Matched51966"

第一次和第二次尝试都按预期工作。但在第三次尝试中,该字符串匹配到无效字符为止。我不想让解析器这样做,而是在字符串中任何数字不是有效字符串时不匹配。为什么会发生这种情况,我该如何修复它?

谢谢!


3
通常这样的解析器将会被嵌入到一个更大的解析器中,比如说在十六进制数后面期望有加号、括号或分号等字符。在这种情况下,你确实希望十六进制数的解析器能够成功,并在第一个无效字符处停止解析,以便让包含它的整个解析器对其后的内容进行处理。 - Daniel Wagner
@DanielWagner,这确实很有道理。谢谢! - Bhargav Kulkarni
1个回答

6

你需要在最后放置eof

parseHex :: Parser Integer
parseHex = do
  string "#x"
  x <- many1 hexDigit
  eof
  return (fst (head (readHex x)))

或者,您可以在使用时将其与eof组合,如果您想在其他地方重用parseHex

testHex :: String -> String
testHex input = case parse (parseHex <* eof) "lisp" input of
  Left err -> "Does not match " ++ show err
  Right val -> "Matched" ++ show val

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接