下面是一个例子:
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)。
我的误解是什么?我如何修复此错误?
1条答案
按热度按时间tvz2xvvm1#
您可以使用较小的测试用例来重现问题:
问题是
fileParser
首先调用lineParser
,lineParser
成功地将"x <"
解析为[Raw "x", Op Gt]
,而"- 3.14"
尚未解析。不幸的是,fileParser
现在期望用eolParser
解析某些内容,但eolParser
不能解析"- 3.14"
,因为它既不以分号开头,也不以endOfLine
开头。您的
try opParser
在这里没有作用,因为opParser
成功解析了<
,所以没有什么可以回溯的。有很多方法可以解决这个问题。如果
<-
是唯一一种可能会错误解析<
的情况,则可以使用notFollowedBy
排除这种情况: