(如何编写(Lisp)解释器(用 Python))(2010)

2026-06-21 1 阅读 tosh
(如何编写 (Lisp) 解释器(用 Python))此页面有两个目的:一般性地描述如何实现计算机语言解释器,特别是使用 Python 3 作为实现语言为大多数 Lisp 方言构建解释器。我将我的语言和解释器称为 Lispy (lis.py)。几年前,我展示了如何在 Java 和 Common Lisp 中编写半实用的Scheme解释器。这次的目标是尽可能简洁明了地演示艾伦·凯所说的“麦克斯韦软件方程”。为什么这很重要?正如 Steve Yegge 所说,“如果你不知道编译器是如何工作的,那么你就不知道计算机是如何工作的。” Yegge 描述了 8 个可以用编译器解决的问题(或者用解释器也同样可以解决,或者用 Yegge 典型的愤世嫉俗的态度)。方案程序的语法和语义 语言的语法是字符的排列以形成正确的语句或表达式;语义是这些陈述或表达式的含义。例如,在数学表达式的语言中(以及在许多编程语言中),一加二相加的语法是“1 + 2”,语义是对两个数字进行加法运算,得到值 3。当我们确定一个表达式的值时,我们说我们正在评估一个表达式;我们会说“1 + 2”的计算结果为 3,并将其写为“1 + 2”⇒ 3。Scheme 语法与大多数其他编程语言不同。考虑:Java 方案 if (x.val() > 0) { return fn(A[i] + 3 * i, new String[] {"one", "two"}); } ( if (> (val x) 0) (fn (+ (aref A i) (* 3 i)) ( quote (一二))) Java 有各种各样的语法约定(关键字、中缀运算符、三种括号、运算符优先级、点符号、引号、逗号、分号),但Scheme 语法要简单得多:Scheme 程序仅由表达式组成。没有语句/表达式区别。数字(例如 1 )和符号(例如 A )被称为原子表达式;它们与它们的 Java 对应物类似,除了在Scheme中,运算符如 + 和 > 也是符号,并且与 A 和 fn 的处理方式相同。其他都是列表表达式:一个“(”,后跟零个或多个表达式,后跟一个“)”。列表的第一个元素决定了它的含义:以关键字开头的列表,例如 (if ...) 。 form ;以非关键字开头的列表,例如 (fn ...) ,是一个函数调用。相比之下,Python 有 33 个关键字和 110 个语法形式,而 Java 有 50 个关键字和 133 个语法形式。开玩笑说“Lisp”代表“L ots of I rritating Silly Parentheses”;我认为它代表“L isp I s Syntropically Pure”。)在本页中,我们将介绍Scheme语言的所有要点及其解释(省略一些次要细节),但我们将采取两个步骤来实现这一目标,首先定义一个简化的语言,然后再定义近乎完整的Scheme语言1:Lispy计算器。是仅使用五种语法形式(两种原子、两种特殊形式和过程调用)的Scheme 子集,只要您熟悉前缀表示法,Lispy 计算器就可以执行典型计算器语言中未提供的两件事:“if”表达式和新变量的定义。下面是一个示例程序,它使用公式 π r 2 计算半径为 10 的圆的面积:(define r)。 10) (* pi (* r r)) 以下是所有允许的表达式的表: 表达式语法语义和示例 变量引用符号 符号被解释为变量名称;其值是变量的值 示例:r ⇒ 10(假设 r 先前定义为 10) 常量文字数字 示例:12 ⇒ 12 或 -3.45e+6 ⇒ -3.45e+6 条件(如果测试)。 conseq alt ) 计算 test ;如果为 true,则计算并返回 conseq ;否则 alt 。 示例: (if (> 10 20) (+ 1 1) (+ 3 3)) ⇒ 6 定义 (define symbol exp ) 定义一个新变量并赋予其计算表达式 exp 的值。示例: (define r 10) 过程调用 ( proc arg... ) 如果 proc 不是以下符号之一,定义或引用,然后将其视为过程,然后将过程应用于 arg 值列表。示例: (sqrt (* 2 8)) ⇒ 4.0 在此表的语法列中,符号必须是符号,数字必须是整数或浮点数,其他斜体字可以是任何表达式。口译员是否解释某种语言