Compiler construction 在类似于编译器的方案中创建闭包

Compiler construction 在类似于编译器的方案中创建闭包,compiler-construction,elixir,Compiler Construction,Elixir,我正在实现一个类似lisp的方案,它将在某个时刻被编译成某种形式的字节码,可以看到。不幸的是,我把自己编码成了一个小洞,不知道如何走出这个洞。本质上,给定一个看起来像这样的lambda(外部b是故意不使用的): 我的代码生成以下语法树: [ type: :lambda, args: [[type: :word, val: 'a'], [type: :word, val: 'b']], body: [ [ type: :lambda, args: [[t

我正在实现一个类似lisp的方案,它将在某个时刻被编译成某种形式的字节码,可以看到。不幸的是,我把自己编码成了一个小洞,不知道如何走出这个洞。本质上,给定一个看起来像这样的lambda(外部b是故意不使用的):

我的代码生成以下语法树:

[
  type: :lambda,
  args: [[type: :word, val: 'a'], [type: :word, val: 'b']],
  body: [
    [
      type: :lambda,
      args: [[type: :word, val: 'c']],
      body: [
        [
          type: :expr,
          val: [
            [type: :word, val: '+'],
            [type: :word, val: 'a'],
            [type: :word, val: 'c']
          ]
        ]
      ]
    ]
  ]
]
不幸的是,当我真正开始生成字节码时,创建这些lambda所需的闭包并不容易(据我所知)。理想情况下,我希望生成一棵如下所示的树:

[
  type: :lambda,
  args: [[type: :word, val: 'a'], [type: :word, val: 'b']],
  closure: [],
  body: [
    [
      type: :lambda,
      args: [[type: :word, val: 'c']],
      closure: [[type: :word, val: 'a']],
      body: [
        [
          type: :expr,
          val: [
            [type: :word, val: '+'],
            [type: :word, val: 'a'],
            [type: :word, val: 'c']
          ]
        ]
      ]
    ]
  ]
]

但是,通过查看给定参数是否显示在env中,很容易判断它是否应该是闭包的一部分,因为我只是通过调用
Enum.map
,我不确定如何将该信息返回到lambda对象。我不需要特定的代码来解决这个问题,但是一个通用的指导方针/提示/向正确的方向推进会很好(我知道这有点模糊,但我不太确定如何为这个问题创建更具体的测试用例)

您可以遍历AST,在每个节点上构建绑定标识符列表

例如,
lambda
节点绑定其参数(如果这些名称已经在绑定列表中,请随意重写),以及
let
let*
。在返回此树时,还可以为每个AST节点构建一个引用的空闲标识符列表

lambda
let
let*
从这些自由变量列表中删除标识符

剩下的很简单:在每个
lambda
节点上,计算引用列表和绑定列表之间的交集,结果将是这个闭包必须捕获的环境。如果为空,则这是一个没有环境的简单函数

在您的示例中,它将是:

[b:()f:()](lambda(ab)[b:(ab)f:(a)](lambda(c)[b:(ab)f:(ac)](+ac)))

如您所见,内部lambda在其
b:
f:
列表之间有
a
的共同点,因此您必须在这里发出闭包分配指令,构建一个元素
a
的环境


您可以将此过程与变量重命名相结合(例如,将lambda参数名称转换为参数编号)。

您可以在AST中遍历,在每个节点上构建绑定标识符列表

例如,
lambda
节点绑定其参数(如果这些名称已经在绑定列表中,请随意重写),以及
let
let*
。在返回此树时,还可以为每个AST节点构建一个引用的空闲标识符列表

lambda
let
let*
从这些自由变量列表中删除标识符

剩下的很简单:在每个
lambda
节点上,计算引用列表和绑定列表之间的交集,结果将是这个闭包必须捕获的环境。如果为空,则这是一个没有环境的简单函数

在您的示例中,它将是:

[b:()f:()](lambda(ab)[b:(ab)f:(a)](lambda(c)[b:(ab)f:(ac)](+ac)))

如您所见,内部lambda在其
b:
f:
列表之间有
a
的共同点,因此您必须在这里发出闭包分配指令,构建一个元素
a
的环境


您可以将此过程与变量重命名相结合(例如,将lambda参数名称转换为参数编号)。

您可能需要查看。您可能需要查看。
[
  type: :lambda,
  args: [[type: :word, val: 'a'], [type: :word, val: 'b']],
  closure: [],
  body: [
    [
      type: :lambda,
      args: [[type: :word, val: 'c']],
      closure: [[type: :word, val: 'a']],
      body: [
        [
          type: :expr,
          val: [
            [type: :word, val: '+'],
            [type: :word, val: 'a'],
            [type: :word, val: 'c']
          ]
        ]
      ]
    ]
  ]
]