如何实现没有递归的Scheme解释器?
我试图设计一个非递归的Scheme解释器,使用堆栈和指针在AST上遍历和求值 如果我只需要处理纯过程调用,一切都很好。然而,一旦宏出现,不规则的语法就很难编写非递归例程。(由于不同语义的混合)更糟糕的是,当考虑到内置宏(如if、conf-let等)时,非递归方法似乎变得异常复杂 关于实现非递归解释器有什么好的建议吗?或者有关于这个的资料吗?我努力地在谷歌上搜索,但什么也没找到如何实现没有递归的Scheme解释器?,scheme,interpreter,Scheme,Interpreter,我试图设计一个非递归的Scheme解释器,使用堆栈和指针在AST上遍历和求值 如果我只需要处理纯过程调用,一切都很好。然而,一旦宏出现,不规则的语法就很难编写非递归例程。(由于不同语义的混合)更糟糕的是,当考虑到内置宏(如if、conf-let等)时,非递归方法似乎变得异常复杂 关于实现非递归解释器有什么好的建议吗?或者有关于这个的资料吗?我努力地在谷歌上搜索,但什么也没找到 我想知道主流方案口译员是否使用这种方法。也许我可以用递归编写,这样就不会受到指责。在香草r5rs方案中,宏只是重新排列A
我想知道主流方案口译员是否使用这种方法。也许我可以用递归编写,这样就不会受到指责。在香草r5rs方案中,宏只是重新排列AST的DSL。它们在纯语法层面上运行,应该与解释器分开 在R6RS或CL或其他任何情况下,宏实际上可以进行计算,这意味着它们需要运行两次解释器,一次用于扩展宏,另一次用于计算生成的AST 例如,给定以下代码
(let ((x 5))
(if (= x 5)
(display "Woohoo")
(error)))
您应该在离开AST的第一阶段对其运行宏扩展器
((lambda (x)
(cond
((= x 5) (display "Woohoo"))
(else (error)))) 5)
这样做应该不会计算代码。只需重新安排AST即可。然后,当解释器运行它时,它甚至不必知道宏存在
因此,您的最终方案解释器应该如下所示
Parse File
|
|
|
Expand All Macros
|
|
|
Optimize/Black Magic
|
|
|
Optional ByteCode compilation or similar IL
|
|
Evaluate
|
|
Profit
在研究我的Scheme编译器时,我读了很多关于编译相关的各种老问题的论文。与宏和@jozefg有关的,我发现的分离实际解释和宏扩展的很好的插图,是由Al Petrofsky写的。他还写过,这是一个很好的解释语法规则 我以前写过一篇文章。它有一个交替的电池地址堆栈来设置汽车!结果和要计算的表达式 Eval是这样的:
(pop_stack register1) ; expression
(while-not-zero register1
... do computation
(pop_stack register2) ; return address
(open-cons register2) ; opens location
(set-car register1) ; sets value
(pop_stack register1)) ; next job
它支持标准的McCharty LISP1内容,包括宏(flambda)
类似(cons'A'b)的简单表达式在while循环中使用了6轮,如下所示:
(pop_stack register1) ; expression
(while-not-zero register1
... do computation
(pop_stack register2) ; return address
(open-cons register2) ; opens location
(set-car register1) ; sets value
(pop_stack register1)) ; next job
(let ((mylambda lambda))
(mylambda (x) (cons '1 x)))
必须在解释器的不同阶段展开宏,然后evalIf如果您只实现
语法规则
,那么是的,您可以通过简单、愚蠢的模式展开潜行而过syntax case
支持任意模式,因此如果或cond是宏,则您还需要在两个阶段运行解释器。不是两者都有。需要至少有一种形式的条件求值。@ymfoilet
也是一个宏。类似于(let((foo 42)(bar 10))(+foo bar))
的表达式扩展为((lambda(foo bar)(+foo bar))42 10)
。它们都可以是内置的。它们不能都是宏。@ymfoi理想情况下,您需要尽可能少的内置项,同时保持合理的性能。由于let
很容易被定义为lambda
构造和调用,因此将其作为特例似乎会适得其反,除非您的lambda是重量级的,在这种情况下您会遇到更大的问题。@ChrisJester-Young在强类型函数式语言中,let和lambda之间存在差异,但只有这样。这是HM算法的一部分,参见-generalization@ChrisJester-Young关键区别在于\x:T.y
T
是一个单型,但let可以多态绑定。@jozefg在搜索“let泛化”时第一次击中目标:,由Simon Peyton Jones合著。-)我检查了上面的代码,如果将if视为一个函数,则将一些函数应用于3个参数。这些参数在函数的应用程序中进行求值。不管你在函数中做了什么,它们已经被计算过了。