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