我正在使用Parsec来解析一些表达式(有关更多上下文,请参阅this question),我的代码中最相关的部分是:
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
行动中:
boobla&gt; foo = 100 + ~100
167个
boobla&gt; foo
解析器错误:(第1行,第4列):
意外的输入结束
期待字母或数字或“=”
第二个表达式应评估为167
,值为foo
。
我认为当Parsec尝试提取令牌reservedOp "="
时,它应该失败,因为字符串中没有这样的令牌,然后是尝试第二个函数simpleStmt
并成功使用它。但它的工作方式不同:它期望更多的输入,只是抛出这个异常。
如果字符串(或当前行)中没有更多字符,我应该使用什么来使assignStmt
失败。应使用foo = 10
解析assignStmt
,并使用foo
解析simpleStmt
。
答案 0 :(得分:8)
您错过了try
功能。
默认情况下,<|>
运算符将尝试使用左解析器,如果失败而不使用任何字符,它将尝试使用正确的解析器。
但是,如果 - 在您的情况下 - 解析器在消耗了一些字符后失败,并且正确的解析器从未尝试过。请注意,这通常是您想要的行为;如果你有像
这样的东西parseForLoop <|> parseWhileLoop
然后如果输入类似于“for break
”,那么这不是一个有效的for循环,并且没有必要尝试将其解析为while循环,因为这肯定也会失败。
try
组合器会更改此行为。具体来说,它使失败的解析器似乎没有消耗任何输入。 (这会造成空间损失;输入可能已被丢弃,但try
会使其徘徊。)