遇到错误后继续lex

时间:2014-09-12 17:26:52

标签: haskell parsec

我正在攻读我大学的编译器课程。我选择使用Haskell + Parsec来完成项目。词法分析器和解析器必须是分开的。我正在使用Parsec将字符串转换为令牌列表,然后将其传递到另一个将令牌列表转换为AST的Parsec解析器。

问题是词法分析器应该继续尝试lex,即使出现错误。为了尝试这样做,我向我的令牌数据类型引入了一个代表“意外令牌”的令牌,我尝试用< |>来填充我的代码。出现意外情况,以便在出现错误时生成此令牌。这是很多样板,也很难说出放置这些的位置。

我首选的解决方案是以某种方式使Parsec自动执行此操作:如果存在ParseError,则在该位置生成意外的标记,并在稍后继续解析一个位置。我该怎么做?

以下是我现有的一些代码片段:http://lpaste.net/8144414997276000256 出于某种原因,我仍然可以得到一个解析错误,即使Unexpected令牌应该捕获未处理的案例。

1 个答案:

答案 0 :(得分:1)

似乎你应该能够获得一个额外的unexpected个术语。我假设你有token类型,看起来像这样:

token' =  number
      <|> identifier
      <|> ...

我可能会让每个令牌(numberidentifier等)管理自己的空白:

number :: Parser Token
number = Number . read <$> many1 digit <* spaces

为什么不在这结尾添加额外的意外期限作为一个全能?

token' =  number
      <|> identifier
      <|> ...
      <|> unexpected'

让它消耗一个字符。为了获得更好的错误消息,您甚至可以在值中包含该字符。然后,当您使用此列表创建列表时,您的词法分析器不会知道如何处理每个字符的Unexpected值。

unexpected' :: Parser Token
unexpected' = Unexpected <$ anyChar

最后,整个lex只是many token'。在我的测试中,这可以很好地处理中间的无效字符。

*Main> parse (many token') "<foo>" "1 2 abc ~ ~def"
Right [Number 1,Number 2,Identifier "abc",Unexpected,Unexpected,Unexpected,Identifier "def"]

要记住的一件事是默认情况下Parsec 不会回溯。这意味着如果解析在某个部分通过令牌失败,它将不会返回并尝试unexpected:您将收到错误。要启用回溯,您必须在可能出错的解析器上使用try。例如,如果identifier需要两个字符:

identifier :: Parser Token
identifier = Identifier <$> liftA2 (:) letter (many1 alphaNum) <* spaces

然后它可能会失败一部分而不是回溯。但如果你将它包装在try中,它应该可以工作:

token' =  number
      <|> try identifier
      <|> ...

try的问题在于,如果您不小心,它可能会降低您的代码速度。但是,如果你不介意放慢速度,你可能只需在任何地方添加try并回溯很多就可以侥幸成功!