【问题标题】:try function in parsing lambda expressions尝试解析 lambda 表达式的函数
【发布时间】:2014-02-13 10:45:48
【问题描述】:

我对 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'

【问题讨论】:

    标签: parsing haskell lambda


    【解决方案1】:

    如果你想同时允许,不要使用&lt;|&gt;

    您的 program 解析器主要使用

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

    这个&lt;|&gt; 是选择 - 如果可以,它会选择bindings,如果失败,expressions,这不是你想要的。您想要零个或多个绑定,然后是表达式,所以让我们这样做。 可悲的是,即使这样有效,解析器的最后一行是 eof

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

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

    使用更多&lt;?&gt; "binding" 类型提示会更容易找到此错误,因此您可以更清楚地看到预期的内容。

    endBy 不需要 many

    您收到的错误消息源于该行

    expressions = many (endBy expression eol)
    

    应该是

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

    endBysepBy 一样工作 - 您无需在其上使用 many,因为它已经解析了很多。

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

    使用try处理常用前缀

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

    binding 看到第一个标识符并说“没关系,伙计们,我有这个”,但随后找不到 = 并给你一个错误,我们希望它回溯并让 expression 试一试.关键是我们已经使用了标识符输入,并且我们想取消使用它。 try 是正确的。

    try 封装你的binding 解析器,所以如果它失败了,我们将回到行首并交给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 的事实表明您将类型推得太远了。

    通常值得为相当多的产生式制作数据类型,如果你import Control.Applicative hiding ((&lt;|&gt;),(&lt;$&gt;),many) Control.Applicative 你可以使用&lt;$&gt;&lt;*&gt; 这样产生式、数据类型和解析器都是相同的结构:

    --<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"
    

    例如,不要对字母这样做,而是对重要的事情这样做。重要的是判断问题,但我将从标识符开始。 (您可以使用&lt;**&gt; 在结果中不包含= 之类的语法。)

    修改代码:

    在重构类型和使用 Applicative 之前here

    然后here

    【讨论】:

    • 谢谢 .. 我尝试进行您提到的修改,但是当解析器到达只有表达式的行时,它会显示错误并期望绑定:code unexpected "(" 期待空格或“=”
    • 我将Binding定义为:data Binding = Eq Symbol Expr 绑定&绑定的类型是:bindings :: Parser [Binding] binding :: Parser Binding 这样对吗?
    • 是的。我正在使用 this 之类的更正但未改进的版本解析括号表达式 OK。如果这不起作用,您可以发布您的"Program.txt"吗?
    • 解析表达式时仍然返回相同的错误,“Program.txt”只有在删除所有绑定后才包含表达式:eq (div (add 2 7) (sub 5 2)) 3;
    • @user3305447 啊,是的 - expressionbinding 都可以以标识符开头,因此 binding 可以尝试并未能期待 =。这是常见的前缀问题。查看新版本的答案。
    猜你喜欢
    • 2010-09-19
    • 1970-01-01
    • 2017-06-01
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多