如何实现没有递归的Scheme解释器?

如何实现没有递归的Scheme解释器?,scheme,interpreter,Scheme,Interpreter,我试图设计一个非递归的Scheme解释器,使用堆栈和指针在AST上遍历和求值 如果我只需要处理纯过程调用,一切都很好。然而,一旦宏出现,不规则的语法就很难编写非递归例程。(由于不同语义的混合)更糟糕的是,当考虑到内置宏(如if、conf-let等)时,非递归方法似乎变得异常复杂 关于实现非递归解释器有什么好的建议吗?或者有关于这个的资料吗?我努力地在谷歌上搜索,但什么也没找到 我想知道主流方案口译员是否使用这种方法。也许我可以用递归编写,这样就不会受到指责。在香草r5rs方案中,宏只是重新排列A

我试图设计一个非递归的Scheme解释器,使用堆栈和指针在AST上遍历和求值

如果我只需要处理纯过程调用,一切都很好。然而,一旦宏出现,不规则的语法就很难编写非递归例程。(由于不同语义的混合)更糟糕的是,当考虑到内置宏(如if、conf-let等)时,非递归方法似乎变得异常复杂

关于实现非递归解释器有什么好的建议吗?或者有关于这个的资料吗?我努力地在谷歌上搜索,但什么也没找到


我想知道主流方案口译员是否使用这种方法。也许我可以用递归编写,这样就不会受到指责。

在香草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
  • (反对意见‘a’b)
  • cons=>过程:cons
  • (预应用程序:cons'a'b)
  • 'a=>a
  • 'b=>b
  • (适用程序:cons'a'b)
  • 因此,我可以重命名每个关键字。这项工作:

    (let ((mylambda lambda))
       (mylambda (x) (cons '1 x)))
    

    必须在解释器的不同阶段展开宏,然后evalIf如果您只实现
    语法规则
    ,那么是的,您可以通过简单、愚蠢的模式展开潜行而过
    syntax case
    支持任意模式,因此如果或cond是宏,则您还需要在两个阶段运行解释器。不是两者都有。需要至少有一种形式的条件求值。@ymfoi
    let
    也是一个宏。类似于
    (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个参数。这些参数在函数的应用程序中进行求值。不管你在函数中做了什么,它们已经被计算过了。