许多(可能是全部?)编程语言由汇编语言组成
lisp 是如何用汇编语言实现的?
谷歌有什么好的参考、手册、教程或关键字吗?
建立自己的lisp实现的任何官方规则/约定?
例如尾递归应该遵循一些实施规则或其他东西..
谢谢
许多(可能是全部?)编程语言由汇编语言组成
lisp 是如何用汇编语言实现的?
谷歌有什么好的参考、手册、教程或关键字吗?
建立自己的lisp实现的任何官方规则/约定?
例如尾递归应该遵循一些实施规则或其他东西..
谢谢
虽然其他评论和帖子都是对的,但这个问题过于模糊,可能有点困惑,我忍不住分享一些建议。我收集了许多关于实现 Lisp 的链接和书籍,因为我最近对语言实现产生了一些兴趣。当然,这是一个深奥的话题,但是阅读与 Lisp 相关的内容尤其引人注目,因为如果您在 Lisp 中实现 Lisp 编译器或解释器,则可以跳过大量关于解析的深入阅读,而只需使用read
. 这使作者能够快速了解编译或解释的内容。这些建议是我已经阅读或开始或正在阅读的书籍,主要涉及 Scheme,而不是 Common Lisp,但仍然可能会引起一些兴趣。
如果您没有语言实现方面的背景,并且从未有过阅读经典 Lisp 和 Scheme “元循环评估器”的乐趣,我会推荐Structure and Interpretation of Computer Programs。如果您看过 Lisp-in-Lisp(或 Scheme-in-Scheme...),您可以跳过。在 SICP 的最后两章中,作者介绍了几种不同的 Lisp/Scheme 解释器和一些变体,以及一个字节码编译器和一个虚拟机。这只是一本精彩的书,而且是免费的。
如果您没有时间阅读 SICP,或者不想为了阅读和编译章节而费力地读完它,我会推荐The Little Schemer。尽管它很短,而且是为 Lisp 和 Scheme 的新手准备的,但如果你从未见过用 Lisp 编写的 Lisp 解释器,他们确实提供了一个,这是一本非常令人愉快的书,但由于可爱的风格,可能并不适合所有人.
还有另一本类似于 SICP 的关于 Scheme 的免费书籍,名为An Introduction to Scheme and its Implementation,我没有读过,但用作一些参考。那里有关于解释器和编译器的部分,它似乎比 SICP 更深入一些,也处理更复杂的事情,比如解析。它可能需要一个编辑器,但它仍然是一个令人印象深刻的产品。
有了如何在 Lisp 中实现 Lisp 的一个不错的想法,您就可以在较低级别上实现 Lisp。
Lisp in Small Pieces经常被推荐。我已经阅读了大部分内容,可以说这绝对是一本很棒的书,里面充满了细节。我会用一把细梳子把它重新梳理一遍,因为当你不明白的东西时,它很容易略读。我还努力从作者的站点获取代码以运行。如果你明白了,我建议使用 Gambit Scheme 并运行依赖于 Meroonet 和 Meroon 的代码,来自这个发行版。Lisp in Small Pieces 提供了许多用 Scheme 编写的解释器以及一个字节码编译器和一个 C 编译器。
Lisp in Small Pieces 移动很快,而且非常密集。如果对您来说太过分了,不妨从The Essentials of Programming Languages开始。我读过一些,它很好,但更多的是口译员。显然其中一个较旧的版本(第一个?我不确定......)包括一个编译器。3个版本之间似乎有很多变化,但第一个在亚马逊上超级便宜,所以检查一下。
至于编译成 C,这是一个带有很多毛茸茸的东西的粗俗主题。编译到 C 会带来所有这些奇怪的角落问题,比如如何优化尾调用和处理闭包、一流的延续和垃圾收集,但它非常有趣,许多 Scheme 的“真实”实现都走这条路。Marc Feeley 对此的介绍非常有趣,标题为The 90 Minute Scheme to C compiler。
从编译到汇编,我的资源较少,但是有一篇经常推荐的论文介绍了将 Scheme 编译到 x86,称为编译器构造的增量方法。它假设读者很少,但是我发现它太快了并且没有填写足够的细节。也许你会有更好的运气。
上面的很多建议都来自一年多前对黑客新闻的这个怪物评论,来自mahmud。它引用了许多 ML 资源,并使用延续进行编译。我的学习还没有那么远,所以我不能说什么好不好。但这是一个非常有价值的评论。参考的作品包括 Andrew Appel 的“Compiling with Continuations”和 Paul Wilson 的“Uniprocessor Garbage Collection Techniques”论文。
祝你好运!
查看Clozure Common Lisp以了解用于实现 lisp 的汇编语言示例。Clozure CL 主要是用 Common Lisp 本身实现的,但是有一个用 C 编写的内核和一些低级的汇编功能。
例如,下面是compiler/X86/x86-lapmacros.lisp中的一个宏,它在 x86 硬件上实现了一个原始CAR
函数,其中一条汇编指令分别用于 32 位和 64 位:
(defx86lapmacro %car (src dest)
(target-arch-case
(:x8632
`(movl (@ x8632::cons.car (% ,src)) (% ,dest)))
(:x8664
`(movq (@ x8664::cons.car (% ,src)) (% ,dest)))))
如图所示,汇编代码本身以 Lisp 形式编码。移植到另一个平台涉及(除其他外)将这些低级操作翻译成另一种汇编语言并交叉编译以在新平台上创建运行时。
ECL (Embeddable Common Lisp)通过编译成 C 采用另一种方法。这使得将实现移植到具有 C 编译器的平台上变得很方便。
我在过去考虑了一下(然后改用 C 内核)。当然没有单一的“组装”,但对于 x86/32bit,这是我的计划:
基本值存储在 64 位节点中,最低三位用作标记,含义如下:
000 -> cell (64 bits are basically two pointers: car/cdr)
001 -> fixnum (64-3-1 bits usable for values)
010 -> vector (32-3 bits for size and 32 bit for pointer to first element)
011 -> symbol (32 bits pointing to values in global env, 32 pointing to name)
100 -> native code (32 bits pointing to executable machine code, 32 bits to args)
101 -> float (using 64-3-1 bit by dropping 4 bits from mantissa)
110 -> string (using 32-3 bits for size and 32 bits pointing to bytes)
111 -> struct (32 bits pointing to definition, 32 bits pointing to content)
如果假设所有分配都是 8 字节的倍数(对于 8 字节的单元大小是合理的),则在考虑指针时 3 位仍然可用。实现一个简单的垃圾收集器需要一个额外的位(“活动”位)。在 C 实现中,我最终根据节点类型在各个部分中分配了这个位(例如,如果那是一个指针,则为高 32 位的最低有效位)。
我的想法是拥有两种类型的内存:在页面中分配并与空闲列表一起使用的“节点内存”(具有上述布局),以及用于可变大小的字符串/代码/数组的“二进制内存”。
根据节点类型需要特定的代码来实现touch
递归标记为活动节点引用的活动节点的功能。
所有这一切当然只是一种幼稚的方法,但我仍然让它在“C”中工作,我相信我也可以在汇编中做到这一点(我的 C 代码在void *
任何地方都在使用,所以基本上只是一个可移植的 32 位汇编器)。对于我的 C 实现中的懒惰,我只对浮点数和整数使用 32 位(使用较高的 32 位),而不是使用所有可用位。
您的问题是基于非常过时的假设。这些天来,几乎没有语言实现是用汇编语言编写的,而且我知道没有 Lisp 实现是这样的。除了自托管实现之外,如今 C 是一种通用的实现语言。
如果您想查看 lisp 函数的汇编语言表示,可以使用DISSASSEMBLE 函数。