使Parsec函数失败而不是期望更多输入

Mar*_*ark 3 haskell parsec

我正在使用Parsec来解析一些表达式(有关更多上下文,请参阅此问题),我的代码中最相关的部分是:

statement :: Parser Stmt
statement = assignStmt <|> simpleStmt

assignStmt :: Parser Stmt
assignStmt =
    do var  <- identifier
       reservedOp "="
       expr <- expression
       return $ Assign var expr

simpleStmt :: Parser Stmt
simpleStmt =
    do expr <- expression
       return $ Simple expr
Run Code Online (Sandbox Code Playgroud)

在行动:

boobla> foo = 100 + ~100
167
boobla> foo
解析器错误:(第1行,第4列):
意外结束输入需要
字母或数字或"="

第二个表达式应该评估为167,值foo.

我认为当Parsec尝试提取令牌时reservedOp "=",它应该失败,因为字符串中没有这样的令牌,那么就是尝试第二个函数simpleStmt并成功使用它.但它的工作方式不同:它期望更多的输入,只是抛出这个异常.

assignStmt如果字符串(或当前行)中没有其他字符,我应该使用什么来使失败.foo = 10应该解析,assignStmtfoo应该解析simpleStmt.

Mat*_*hid 8

你错过了这个try功能.

默认情况下,<|>操作员将尝试使用左解析器,如果失败而不消耗任何字符,它将尝试使用正确的解析器.

但是,如果 - 在您的情况下 - 解析器使用了一些字符失败,并且从未尝试过正确的解析器.请注意,这通常是您想要的行为; 如果你有类似的东西

parseForLoop <|> parseWhileLoop
Run Code Online (Sandbox Code Playgroud)

那么如果输入类似于" for break",那么这不是一个有效的for循环,并且没有必要尝试将其解析为while循环,因为这肯定也会失败.

try组合子改变了这种行为.具体来说,它使失败的解析器似乎没有消耗任何输入.(这有空间损失;输入可能已被丢弃,但try会让它徘徊.)