如何用Python编写一个Lisp解释器

本文介绍了如何使用Python构建一个Lisp方言Schema的解释器,包括解析、执行和交互式REPL。阐述了语言解释器的工作原理,展示了简单Lispy计算器的实现,并讨论了Lispy的限制和优点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

640?wx_fmt=jpeg
作者 | Peter Norvig 译者 | Tianyu 编辑 | Freesia

出品 | Python大本营(ID: pythonnews)


本文有两个目的:一是展示如何实现一个计算机语言的解释器,二是演示如何使用 Python 3 构造 Lisp 的一种方言 Schema,我把我的语言解释器称作 Lispy。几年前,作者曾展示过如何用 Java 和 Common Lisp 写 Schema 解释器。而本次的目的很纯粹,作者会尽可能简明扼要,就像 Alan Kay 所谓的 “软件中的麦克斯韦方程组”。

  为什么这很重要呢?正如 Steve Yegge 所说的,“如果你不知道编译器是如何工作的,那么你也不会知道计算机是如何工作的。” Yegge 描述了 8 种可以由编译器解决的问题。

Schema 程序的语法和语义


语言的语法是指组成正确的语句或表达式的顺序;语义指那些表达式或语句的内在含义。例如,在数学表达式语言中(以及诸多编程语言中),一加二的语法是 “1 + 2”,而语义是指对两个数字执行相加操作,得到的结果为 3 。当我们计算一个数值时,也可以说我们在评估一种表达形式;我们可以说 “1+2” 估值为 3,并写成 “1 + 2” ⇒ 3.   Schema 的语法不同于其他大多数编程语言。考虑如下情况:           640?wx_fmt=png
Java 的语法规范十分繁杂(关键词、中缀运算符、三种括号、运算符优先级、点语法、引号、逗号、分号),但 Schema 的语法要简单得多:
  • Schema 程序仅由表达式组成。没有表达式和语句之分。
  • 数字(比如:1)和符号(比如:A)都可以称为原子表达式;它们无法再细分了。这和 Java 中的 counterpart 类似,但 Schema 不同,一些运算符号,如 + 和 > 也是标识符,和 A 及 fn 的地位是平等的。
  • 还有列表表达式:一个 "(" ,后面接零或多个表达式,后面再接一个 ")"。列表的第一个元素决定了其含义是什么:
    • 以关键词作为开头的列表,如 (if ...),是一种特殊形式,含义取决于关键词是什么。
    • 以非关键词开头的列表,如 (fn ...),是函数的调用。

Schema 的妙处在于整个语言体系仅需 5 个关键词和 8 种语法形式。对比之下,Python 有 33 个关键词和 110 种语法形式,Java 有 50 个关键词和 133 种语法形式。那些括号也许看起来有些吓人,但 Schema 的语法具备着简单性与一致性。(有人开玩笑说 Lisp 就是“大量把人搞疯的括号”;而我认为 Lisp 象征着语法的纯粹性。)   在本文中,我们会介绍 Schema 语言及其解释器的所有特点,但中间要经过两个步骤,先定义一个简单的语言,再定义 Schema 语言的全部内容。


语言1:Lispy Calculator


Lispy Calculator 是 Schema 的一部分,仅使用了五种语法方式。因其基于 Lispy Calculator,只要你熟练使用前缀表示法,就可以做任何典型计算机可以做的运算。你可以做两件典型计算机语言所不能做的两件事:"if" 表达式和定义新变量。下面是一个示例程序,基于公式 π r2,计算半径为10的圆形面积:
 
 
 
 
(define r 10)	
(* pi (* r r))
  下面是一张有关全部表达式的表格:
Expression(表达式) Syntax(语法) Semantics and Example(语义和例子)
variable reference symbol 一个标识符被解释为变量名;它的值是变量的值。例子:r ⇒ 10 (假设 r 已被定义为10)
constant literal number 计算结果为数字本身。例子:12 ⇒ 12 or -3.45e+6 ⇒ -3.45e+6
conditional (if test conseq alt) 执行 test;如果结果为 true,计算返回 conseq;否则返回 alt。例子:(if (> 10 20) (+ 1 1) (+ 3 3)) ⇒ 6
definition (define symbol exp) 定义一个新变量,并计算表达式 exp 赋值给它。例子:(define r 10)
procedure call (proc arg...) 如果表达式不是这些标识符 if, define 或 quote,那它就是一个过程。执行表达式及全部参数,那么该过程就会被调用,而参数值列表也被调用。例子:(sqrt (* 2 8)) ⇒ 4.0

在该表的语法一栏,标识符必须为符号,数字必须为整数或小数,而其它斜体 字可以为任何表达式,arg... 则表示零或多个 arg 的重复。


语言解释器到底是做什么的?

语言解释器包括两个部分:
  1. Parsing:parsing 组件获得字符串形式的输入,并根据语言的语法规则进行验证,然后将程序翻译成内部的表示形式。在一个简单的解释器中,内部的表示形式是一个树形结构(一般被称为抽象语法树),反应了程序语句和表达式的嵌套结构。在被称为编译器的语言翻译器中,常常有一系列内部的表示形式,以抽象语法树开头,然后紧接着一系列指令,可以直接被计算机执行。
  2. Execution:内部的表示形式是根据语言的语义规则进行处理的,因此才能执行计算。Lispy 的 execution 函数叫作 eval(注意
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值