FParsec 中的递归语法
我决定查看 FParsec,并尝试为 λ 表达式编写一个解析器。事实证明,急切使得递归解析变得困难。我该如何解决这个问题?
代码:
open FParsec
type λExpr =
| Variable of char
| Application of λExpr * λExpr
| Lambda of char * λExpr
let rec FV = function
| Variable v -> Set.singleton v
| Application (f, x) -> FV f + FV x
| Lambda (x, m) -> FV m - Set.singleton x
let Λ0 = FV >> (=) Set.empty
let apply f p =
parse
{ let! v = p
return f v }
let λ e =
let expr, exprR = createParserForwardedToRef()
let var = lower |> apply Variable
let app = tuple2 expr expr
|> apply Application
let lam = pipe2 (pchar 'λ' >>. many lower)
(pchar '.' >>. expr) (fun vs e ->
List.foldBack (fun c e -> Lambda (c, e)) vs e)
exprR := choice [
lam
app
var
(pchar '(' >>. expr .>> pchar ')')
]
run expr e
谢谢!
I've decided to check out FParsec, and tried to write a parser for λ expressions. As it turns out, eagerness makes recursive parsing difficult. How can I solve this?
Code:
open FParsec
type λExpr =
| Variable of char
| Application of λExpr * λExpr
| Lambda of char * λExpr
let rec FV = function
| Variable v -> Set.singleton v
| Application (f, x) -> FV f + FV x
| Lambda (x, m) -> FV m - Set.singleton x
let Λ0 = FV >> (=) Set.empty
let apply f p =
parse
{ let! v = p
return f v }
let λ e =
let expr, exprR = createParserForwardedToRef()
let var = lower |> apply Variable
let app = tuple2 expr expr
|> apply Application
let lam = pipe2 (pchar 'λ' >>. many lower)
(pchar '.' >>. expr) (fun vs e ->
List.foldBack (fun c e -> Lambda (c, e)) vs e)
exprR := choice [
lam
app
var
(pchar '(' >>. expr .>> pchar ')')
]
run expr e
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您所指出的,问题在于您的 application 解析器递归调用 expr ,因此存在无限循环。需要编写解析器,使其始终消耗一些输入,然后决定要做什么。
对于 lambda 演算,棘手的事情是识别应用程序和变量,因为如果您有像
x...
这样的输入,那么第一个性格表明它可能是他们中的任何一个。您可以像这样合并应用程序和变量的规则:
首先解析一个变量,然后解析另一个表达式(在这种情况下它是一个应用程序)或者只是返回变量(如果变量后面没有表达式)。其余规则类似:
我只是通过使用 parse.Delay() 避免了显式突变的需要(这使得创建递归值引用成为可能)。原则上,它可以写为:
...但由于某种原因,FParsec 没有实现如果您想在中使用
return!
所需的ReturnFrom
成员计算表达式。As you pointed out, the problem is that your parser for application calls expr recursively and so there is an infinite loop. The parser needs to be written such that it always consumes some input and then decides what to do.
In case of lambda calculus, the tricky thing is recognizing an application and a variable because if you have input like
x...
then the first character suggests it could be either of them.You can merge the rules for application and variable like this:
This first parses a variable and then either parses another expression (in which case it is an application) or it just returns the variable (if there is no expression following the variable). The rest of the rules are similar:
I just avoided the need for explicit mutation by using
parse.Delay()
(which makes it possible to create recursive value references). In principle, it could be written as:...but for some reason, FParsec doesn't implement the
ReturnFrom
member that is needed if you want to usereturn!
in computation expressions.