【问题标题】:Continuing to lex after encountering an error遇到错误后继续 lex
【发布时间】:2014-09-12 17:26:51
【问题描述】:

我正在我的大学学习编译器课程。我选择使用 Haskell + Parsec 来做这个项目。词法分析器和解析器需要分开。我正在使用 Parsec 将字符串转换为令牌列表,然后将其传递到另一个 Parsec 解析器,该解析器将令牌列表转换为 AST。

问题是词法分析器应该继续尝试进行词法分析,即使在出现错误的情况下也是如此。为了尝试做到这一点,我在我的 Token 数据类型中引入了一个表示“意外标记”的标记,并且我尝试在我的代码中添加 unexpected 以便在出现错误时生成这个标记。这是很多样板文件,也很难推断将它们放在哪里。

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

这是我现在拥有的一些代码的 sn-p:http://lpaste.net/8144414997276000256 出于某种原因,我仍然会收到解析错误,即使 Unexpected 令牌应该捕获未处理的情况。

【问题讨论】:

    标签: haskell parsec


    【解决方案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 并回溯很多就可以了!

    【讨论】:

    • 这是我的code 的 sn-p 我通过折叠 来构造我的令牌解析器,就像您的令牌示例所做的那样,但不知何故,解析错误可以解决,即使解析器最终应该尝试 Unexpected 解析器。
    • @DionyRosa:哦,这可能是因为没有在正确的地方使用try。我会稍微回答一下。
    • 谢谢!在此之前我并没有真正理解 try 和 之间的区别
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 2022-11-25
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    相关资源
    最近更新 更多