haskell 秒差距尝试:应尝试转到下一个选项

bihw5rsg  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(179)

下面是一个例子:

import Control.Applicative ((<|>))
import Text.Parsec (ParseError, endBy, sepBy, try)
import Text.Parsec.String (Parser)
import qualified Data.Char as Char
import qualified Text.Parsec as Parsec

data Operation = Lt | Gt deriving (Show)

data Value =
      Raw String
    | Op Operation
  deriving (Show)

sampleStr :: String
sampleStr =  unlines
  [ "#BEGIN#"
  , "x <- 3.14 + 2.72;"
  , "x < 10;"
  ]

gtParser :: Parser Value
gtParser = do
  Parsec.string "<"
  return $ Op Gt

ltParser :: Parser Value
ltParser = do
  Parsec.string ">"
  return $ Op Lt

opParser :: Parser Value
opParser = gtParser <|> ltParser

rawParser :: Parser Value
rawParser = do
  str <- Parsec.many1 $ Parsec.satisfy $ not . Char.isSpace
  return $ Raw str

valueParser :: Parser Value
valueParser = try opParser <|> rawParser

eolParser :: Parser Char
eolParser = try (Parsec.char ';' >> Parsec.endOfLine)
         <|> Parsec.endOfLine

lineParser :: Parser [Value]
lineParser = sepBy valueParser $ Parsec.many1 $ Parsec.char ' '

fileParser :: Parser [[Value]]
fileParser = endBy lineParser eolParser

parse :: String -> Either ParseError [[Value]]
parse = Parsec.parse fileParser "fail..."

main :: IO ()
main = print $ parse sampleStr

此操作将失败,并显示以下消息

Left "fail..." (line 2, column 4):
unexpected "-"
expecting " ", ";" or new-line

据我所知,由于我有try opParser,在Parsec发现<-不能被opParser解析后,它应该转到rawParser(这本质上是一个lookahead)。
我的误解是什么?我如何修复此错误?

tvz2xvvm

tvz2xvvm1#

您可以使用较小的测试用例来重现问题:

> Parsec.parse fileParser "foo" "x <- 3.14"

问题是fileParser首先调用lineParserlineParser成功地将"x <"解析为[Raw "x", Op Gt],而"- 3.14"尚未解析。不幸的是,fileParser现在期望用eolParser解析某些内容,但eolParser不能解析"- 3.14",因为它既不以分号开头,也不以endOfLine开头。
您的try opParser在这里没有作用,因为opParser成功解析了<,所以没有什么可以回溯的。
有很多方法可以解决这个问题。如果<-是唯一一种可能会错误解析<的情况,则可以使用notFollowedBy排除这种情况:

gtParser :: Parser Value
gtParser = do
  Parsec.string "<"
  notFollowedBy $ Parsec.string "-"
  return $ Op Gt

相关问题