如何在没有递归的情况下实现Scheme解释器?

时间:2013-07-29 13:42:25

标签: scheme interpreter

我尝试设计一个非递归的Scheme解释器,使用堆栈和指针在AST上行走并进行评估。

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

关于实现非递归解释器的任何好建议?或者那些材料?我用Google搜索但没有找到任何东西。

而且,我想知道主流的Scheme解释器是否使用这种方法。也许我只能写递归而且不会受到指责。

2 个答案:

答案 0 :(得分:6)

在vanilla r5rs方案中,宏只是用于重新排列AST的DSL。它们在纯粹的语法层面上运作,应该与解释器分开。

在R6RS或CL等等中,宏实际上可以进行计算,这意味着它们需要2次运行解释器,一次用于扩展宏,另一次用于评估生成的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

答案 1 :(得分:4)

我正在研究我的Scheme编译器,我已经阅读了很多关于编译相关的各种旧问题的论文。与宏和@ jozefg相关的分离我发现的实际解释和宏观扩展的好例子是Alexpander,由Al Petrofsky编写。他还编写了Eval in one define,这是一个很好的语法规则解释器。

我之前写过Lisp1 interpreter that runs on Brainf*ck。它有一个堆栈,交替的单元格地址设置为汽车!结果和要评估的表达式。

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轮,如下所示:

  1. (cons'a'b)
  2. cons =>过程:缺点
  3. (预先申请程序:cons'a'b)
  4. 'a =>一个
  5. 'b => b
  6. (申请程序:cons'a'b)
  7. 因此我可以重命名每个关键字。例如。这有效:

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