【问题标题】:Transforming Lisp to C++将 Lisp 转换为 C++
【发布时间】:2011-08-03 08:32:59
【问题描述】:

我正在研究一种基于 lisp(非常小的方案子集)编译为 C++ 的玩具语言,我试图弄清楚如何表示 let 表达式,

(let ((var 10)
      (test 12))
  (+ 1 1)
  var)

起初我以为先执行所有 expr 然后返回最后一个,但返回会破坏我嵌套 let 表达式的能力,表示 let 的方法是什么?

此外,任何关于源到源转换的资源都已得到应用,我已经用谷歌搜索过,但我只能找到 90 分钟的方案编译器。

【问题讨论】:

  • 这里有一篇博文可能会有所帮助,这个博主写了一堆关于编译主题的文章,这篇文章与处理闭包有关。它刚刚发布到 HN,让我想起了这个问题:matt.might.net/articles/closure-conversion 这是一种比我描述的幼稚技术更先进的技术,但它写得很清楚。

标签: c++ c scheme


【解决方案1】:

扩展let 的一种方法是将其视为lambda

((lambda (var test) (+ 1 1) var) 10 12)

然后,将其转换为函数和 C++ 中的相应调用:

int lambda_1(int var, int test) {
    1 + 1;
    return var;
}

lambda_1(10, 12);

所以在更大的范围内:

(display (let ((var 10)
               (test 12))
           (+ 1 1)
           var))

变成

display(lambda_1(10, 12));

还有更多细节,例如需要从let 中访问let 之外的词法变量。由于 C++ 没有词法嵌套函数(例如,与 Pascal 不同),这将需要额外的实现。

【讨论】:

    【解决方案2】:

    我将尝试解释一种简单的方法来编译嵌套 lambdas。由于 Greg 解释将 let 扩展为 lambda 非常好,我根本不会称呼let,我会假设let 是 派生形式或宏,并扩展为 lambda 形式,即 立即调用。

    将 Lisp 或 Scheme 函数直接编译成 C 或 C++ 函数 由于其他海报提出的问题,这将是棘手的。根据 这种方法,生成的 C 或 C++ 将无法识别(甚至 非常易读)。

    我在完成计算机程序的结构和解释后写了一个 Lisp-to-C 编译器(这是最后的练习之一,实际上我作弊,只是写了一个从 SICP 字节码到 C 的翻译器)。它发出的 C 子集根本没有使用 C 函数来处理 Lisp 函数。这是因为 SICP第5章注册机器语言真的是低级 比 C.

    假设您有某种形式的环境,将名称绑定到值,您可以像这样定义函数调用的关键:使用绑定到参数的形式参数扩展定义函数的环境,然后评估这个新环境中的函数体。

    在 SICP 的编译器中,环境保存在一个全局变量中,还有其他的 保存函数调用的参数列表的全局变量,如 以及被调用的过程对象(过程对象包括一个指向它被定义的环境的指针),以及一个函数返回时跳转到的标签。

    请记住,当您编译 lambda 表达式时, 是您在编译时知道的两个句法组件: lambda 的参数和正文。

    编译函数时,发出的代码类似于 这个伪代码:

    some-label
    *env* = definition env of *proc*
    *env* = extend [formals] *argl* *env*
    result of compiling [body]
    ...
    jump *continue*
    

    ... 其中*env**argl* 是持有 环境和参数列表,extend 是一些函数(这可以 是一个适当的 C++ 函数),它通过以下方式扩展了环境 *env**argl* 中的名称与[formals] 中的值配对。

    然后,当编译后的代码运行时,就会调用这个 lambda 在代码中的其他地方,调用约定是把 将参数列表求值的结果放入*argl*变量中,将返回标签放入*continue*变量中,然后跳转到some-label

    在嵌套lambdas 的情况下,发出的代码看起来像 像这样:

    some-label
    *env* = definition env of *proc*
    *env* = extend [formals-outer] *argl* *env*
    another-label
    *env* = definition env of *proc*
    *env* = extend [formals-inner] *argl* *env*
    result of compiling [body-inner]
    ...
    jump *continue*
    rest of result of compiling [body-outer]
    ... somewhere in here there might be a jump to another-label
    jump *continue*
    

    这有点难以解释,而且我确定我已经搞糊涂了 它的工作。我想不出一个不涉及我基本上草率地描述 SICP 的整个第 5 章的体面的例子。由于我花了时间写这个答案,所以我会发布它,但如果它令人绝望地令人困惑,我很抱歉。

    我强烈推荐SICPLisp in Small Pieces

    SICP 涵盖了面向初学者的元循环解释,以及解释器的许多变体,以及我在上面设法混淆和破坏的字节码编译器。这只是最后两章,前三章也一样好。这是一本很棒的书。如果您还没有阅读,请务必阅读。

    LiSP 包含许多用 Scheme 编写的解释器、一个字节码编译器和一个 C 编译器。我在其中,可以自信地说,这是一本内容丰富、内容丰富的书,值得任何感兴趣的人阅读在 Lisp 的实现中。在这一点上它可能有点过时了,但对于像我这样的初学者来说,它仍然很有价值。虽然它比 SICP 更先进,所以要小心。它在中间包含一章关于指称语义的章节,基本上就在我的脑海中。

    其他一些注意事项:

    Darius Bacon's self-hosting Lisp to C compiler

    lambda lifting,这是我认为 Marc Feeley 使用的更高级的技术

    【讨论】:

    • +1 强调这比将let 转换为lambda/application 复杂得多
    【解决方案3】:

    如果您正在寻找有助于进行源到源翻译的工具,我建议您使用ANTLR。这是最优秀的。

    但是,您需要考虑如何将松散类型的语言 (lisp) 翻译成松散类型的语言 (c)。例如,在您的问题中,10 的类型是什么? short? int? double?

    【讨论】:

    • 我有自己的对象系统 10 是 c++ 端的 int 我知道它在运行时是什么
    猜你喜欢
    • 1970-01-01
    • 2018-11-15
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多