尝试解析lambda表达式的函数

时间:2014-02-13 10:45:49

标签: parsing haskell lambda

我对Haskell完全不熟悉,并试图实现一个“Lambda演算”解析器,它将用于读取lambda reducer的输入。需要首先解析绑定“identifier = expression;”从一个文本文件,然后在最后只有一个表达式.. 到目前为止,它只能解析绑定,并在单独遇到表达式时显示错误..当我尝试使用tryoption函数时,它会出现类型不匹配错误:

Couldn't match type `[Expr]'
                  with `Text.Parsec.Prim.ParsecT s0 u0 m0 [[Expr]]'
    Expected type: Text.Parsec.Prim.ParsecT
                     s0 u0 m0 (Text.Parsec.Prim.ParsecT s0 u0 m0 [[Expr]])
      Actual type: Text.Parsec.Prim.ParsecT s0 u0 m0 [Expr]
    In the second argument of `option', namely `bindings'

绑定不应该返回任何内容,但我尝试添加一个return语句,它还返回了一个类型不匹配错误:

Couldn't match type `[Expr]' with `Expr'
    Expected type: Text.Parsec.Prim.ParsecT
                     [Char] u0 Data.Functor.Identity.Identity [Expr]
      Actual type: Text.Parsec.Prim.ParsecT
                     [Char] u0 Data.Functor.Identity.Identity [[Expr]]
    In the second argument of `(<|>)', namely `expressions'

1 个答案:

答案 0 :(得分:3)

如果要同时允许

,请不要使用<|>

您的program解析器主要使用

program = do
      spaces
      try bindings <|> expressions
      spaces >> eof

这个<|>是选择 - 如果可以,它会bindings,如果失败,expressions,这不是你想要的。你想要零个或多个绑定,然后是表达式,所以让它做到这一点。 遗憾的是,即使这样做,解析器的最后一行是eof

首先,让我们允许零绑定,因为它们是可选的,然后让我们得到绑定和表达式:

bindings = many binding

program = do
      spaces
      bs <- bindings 
      es <- expressions
      spaces >> eof
      return (bs,es)

使用更多<?> "binding"类型提示更容易找到此错误,以便您更清楚地了解所期望的内容。

endBy不需要many

您出现的错误消息来自

expressions = many (endBy expression eol)

应该是

expressions :: Parser [Expr]
expressions = endBy expression eol

endBy的工作方式与sepBy类似 - 您无需在其上使用many,因为它已经解析了很多。

使用更强大的数据类型树可以更容易地找到此错误,因此:

使用try处理公共前缀

您遇到的一个难以调试的问题是在解析表达式时遇到错误expecting space or "="。如果我们考虑一下,我们期望=唯一的位置是绑定,所以当我们给它一个表达式时,它必须是解析绑定的一部分。只有当我们的表达式以标识符开头时才会发生这种情况,就像绑定一样。

binding看到了第一个标识符,并说“没关系,我已经得到了这个”,但后来发现没有=并给你一个错误,我们希望它回溯并让{{ 1}}去吧。关键点是我们已经使用了标识符输入,我们想要不使用它。 expression是正确的。

使用try包含您的binding解析器,如果失败,我们将返回到行的开头并移交给try

expression

重要的是,每个解析器尽可能以匹配独特的东西开始,以避免此问题。 (binding = try (do (Var id) <- identifier _ <- char '=' spaces exp <- expression spaces eol <?> "end of line" return $ Eq id exp <?> "binding") 正在回溯,因此效率低下,如果可能的话应该避免。)

特别是,避免使用try启动解析器,而是确保使用空格完成所有解析器。如果您愿意,主spaces可以program开头,因为它是唯一的选择。

大多数作品的使用类型 - 更好的结构和可读性

我的第一条一般建议是你可以使用更细粒度的数据类型,并且应该使用它们的类型注释你的解析器。目前,所有内容都包含在spaces中,这意味着您只能收到有关您是Expr还是Expr的错误消息。您必须将[Expr]添加到Eq这一事实表明您将此类型推得太过分了。

通常值得为很多作品制作数据类型,如果你Expr Control.Applicative,你可以使用import Control.Applicative hiding ((<|>),(<$>),many)<$>来制作,数据类型和解析器都是相同的结构:

<*>

不要以字母为例,但重要的事情。重要的事情是判断问题,但我从识别者开始。 (您可以使用--<program> ::= <spaces> [<bindings>] <expressions> data Program = Prog [Binding] [Expr] program = spaces >> Prog <$> bindings <*> expressions -- <expression> ::= <abstraction> | factors data Expression = Ab Abstraction | Fa [Factor] expression = Ab <$> abstraction <|> Fa <$> factors <?> "expression" <*在结果中不包含*>等语法。)

修改后的代码:

在重构类型并使用Applicative here

之前

之后here