Lithp.py (~2008)
摘要
一个用Python编写的John McCarthy原始Lisp解释器,其源代码附有大量注释,旨在讲解Lisp的内部原理。
暂无内容
查看缓存全文
缓存时间: 2026/06/24 07:51
# lithp.py 源码:https://fogus.me/fun/lithp/
Lithp——John McCarthy 原始 Lisp 语言的解释器。Lithp 的详细注释代码可以在 Github(http://github.com/fogus/lithp)上找到。仅仅编写一个 Lisp 解释器还不够——我还想与你*分享*我的所学。阅读这份源码能让你一窥 John McCarthy、Steve Russell、Timothy P. Hart 和 Mike Levin 的思想,额外附赠我本人的思考。以下源文件可供阅读:
- atom.py(https://fogus.me/fun/lithp/atom.html)
- env.py(https://fogus.me/fun/lithp/env.html)
- error.py(https://fogus.me/fun/lithp/error.html)
- fun.py(https://fogus.me/fun/lithp/fun.html)
- interface.py(https://fogus.me/fun/lithp/interface.html)
- lisp.py(https://fogus.me/fun/lithp/lisp.html)
- lithp.py(https://fogus.me/fun/lithp/index.html)*(本文件)*
- number.py(https://fogus.me/fun/lithp/number.html)
- reader.py(https://fogus.me/fun/lithp/reader.html)
- seq.py(https://fogus.me/fun/lithp/seq.html)
- core.lisp(https://fogus.me/fun/lithp/core.html)
Lithp 解释器需要 Python 2.6.1+ 才能运行。请将评论、错误报告、趣闻轶事等添加到 Lithp 的 Github 项目页面(http://github.com/fogus/lithp)。
```python
import pdb
import getopt, sys, io
from env import Environment
from fun import Function
from atom import TRUE
from atom import FALSE
from lisp import Lisp
from reader import Reader
from error import Error
from fun import Lambda
from fun import Closure
NAME = "Lithp"
VERSION = "v1.1"
WWW = "http://fogus.me/fun/lithp/"
PROMPT = "lithp"
DEPTH_MARK = "."
```
Lithper 类是解释器的驱动程序。它执行以下操作:
1. 初始化全局环境
2. 解析命令行参数并相应处理
3. 初始化基础 Lisp 函数
4. 读取输入
5. 求值
6. 打印
7. 返回步骤 4 进行循环
```python
def __init__(self):
iostreams=(sys.stdin, sys.stdout, sys.stderr)
(self.stdin, self.stdout, self.stderr) = iostreams
self.debug = False
self.verbose = True
self.core = True
self.closures = True
self.rdr = Reader()
self.environment = Environment()
self.init()
```
## 定义核心函数
```python
self.environment.set("eq", Function(self.eq))
self.environment.set("quote", Function(self.quote))
self.environment.set("car", Function(self.car))
self.environment.set("cdr", Function(self.cdr))
self.environment.set("cons", Function(self.cons))
self.environment.set("atom", Function(self.atom))
self.environment.set("cond", Function(self.cond))
```
## 定义工具函数
```python
self.environment.set("print", Function(self.println))
```
## 特殊形式
```python
self.environment.set("lambda", Function(self.lambda_))
self.environment.set("label", Function(self.label))
```
## 定义核心符号
```python
self.environment.set("t", TRUE)
```
这里只有一个空列表,它被命名为`nil`。
```python
self.environment.set("nil", FALSE)
```
## 定义元元素
```python
self.environment.set("__lithp__", self)
self.environment.set("__global__", self.environment)
```
```python
def usage(self):
self.print_banner()
print
print NAME.lower(), " [lithp files]\n"
def print_banner(self):
print "The", NAME, "programming shell", VERSION
print " by Fogus,", WWW
print " Type :help for more information"
print
def print_help(self):
print "Help for Lithp v", VERSION
print " Type :help for more information"
print " Type :env to see the bindings in the current environment"
print " Type :load followed by one or more filenames to load source files"
print " Type :quit to exit the interpreter"
def push(self, env=None):
if env:
self.environment = self.environment.push(env)
else:
self.environment = self.environment.push()
def pop(self):
self.environment = self.environment.pop()
def repl(self):
while True:
# 借鉴自CLIPS(http://clipsrules.sourceforge.net/)的S-表达式解析方法
source = self.get_complete_command()
# 检查任何REPL指令
if source in [":quit"]:
break
elif source in [":help"]:
self.print_help()
elif source.startswith(":load"):
files = source.split(" ")[1:]
self.process_files(files)
elif source in [":env"]:
print(self.environment)
else:
self.process(source)
```
源码一次处理一个 S-表达式。
```python
def process(self, source):
sexpr = self.rdr.get_sexpr(source)
while sexpr:
result = None
try:
result = self.eval(sexpr)
except Error as err:
print(err)
if self.verbose:
self.stdout.write(" %s\n" % result)
sexpr = self.rdr.get_sexpr()
```
在我的人生经历中,我一直听说闭包和动态作用域不能共存。作为一个思想实验,我能想象出为什么会出现这种情况。也就是说,虽然闭包捕获了变量的上下文绑定,但在动态作用域中,变量查找发生在动态栈上。这意味着你可以闭包一个变量,只要它是唯一的,但一旦其他人定义了同名的变量并尝试查找,那么闭包变量将解析为动态栈顶的绑定。这假设查找发生在同名变量被弹出之前。虽然这在概念上容易理解,但我还是想看看实践中会发生什么——结果并不美观。
```python
def lambda_(self, env, args):
if self.environment != env.get("__global__") and self.closures:
return Closure(env, args[0], args[1:])
else:
return Lambda(args[0], args[1:])
```
将求值委托给具体形式。
```python
def eval(self, sexpr):
try:
return sexpr.eval(self.environment)
except ValueError as err:
print(err)
return FALSE
```
一个完整的命令定义为一个完整的 S-表达式。简单来说,就是任意原子或任何括号平衡的列表。
```python
def get_complete_command(self, line="", depth=0):
if line != "":
line = line + " "
if self.environment.level != 0:
prompt = PROMPT + " %i%s " % (self.environment.level, DEPTH_MARK * (depth+1))
else:
if depth == 0:
prompt = PROMPT + "> "
else:
prompt = PROMPT + "%s " % (DEPTH_MARK * (depth+1))
line = line + self.read_line(prompt)
# 用于平衡括号
balance = 0
for ch in line:
if ch == "(":
# 目前不完美,但暂时够用
balance = balance + 1
elif ch == ")":
# 右括号过多是个问题
balance = balance - 1
if balance > 0:
# 平衡括号时返回零
return self.get_complete_command(line, depth+1)
elif balance < 0:
raise ValueError("Invalid paren pattern")
else:
return line
def read_line(self, prompt):
if prompt and self.verbose:
self.stdout.write("%s" % prompt)
self.stdout.flush()
line = self.stdin.readline()
if(len(line) == 0):
return "EOF"
if line[-1] == "\n":
line = line[:-1]
return line
```
Lithp 也使用读取器管道来处理文件。
```python
def process_files(self, files):
self.verbose = False
for filename in files:
infile = open(filename, 'r')
self.stdin = infile
source = self.get_complete_command()
while(source not in ["EOF"]):
self.process(source)
source = self.get_complete_command()
infile.close()
self.stdin = sys.stdin
self.verbose = True
if __name__ == '__main__':
lithp = Lithp()
try:
opts, files = getopt.getopt(sys.argv[1:], "hd", ["help", "debug", "no-core", "no-closures"])
except getopt.GetoptError as err:
# 打印帮助信息并退出:
print(str(err)) # 会打印类似“option -a not recognized”的内容
lithp.usage()
sys.exit(1)
for opt,arg in opts:
if opt in ("--help", "-h"):
lithp.usage()
sys.exit(0)
elif opt in ("--debug", "-d"):
lithp.verbose = True
elif opt in ("--no-core"):
lithp.core = False
elif opt in ("--no-closures"):
lithp.closures = False
else:
print("unknown option " + opt)
# 如果适用,处理核心lisp函数
if lithp.core:
lithp.process_files(["../core.lisp"])
if len(files) > 0:
lithp.process_files(files)
lithp.print_banner()
lithp.repl()
```
### 参考文献
- (McCarthy 1979) *History of Lisp* by John McCarthy
- (McCarthy 1960) *Recursive functions of symbolic expressions and their computation by machine, part I* by John McCarthy
- (Church 1941) *The Calculi of Lambda-Conversion* by Alonzo Church
- (Baker 1993) *Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same* by Henry Baker
- (Kleene 1952) *Introduction of Meta-Mathematics* by Stephen Kleene
- (McCarthy 1962) *LISP 1.5 Programmer's Manual* by John McCarthy, Daniel Edwards, Timothy Hart, and Michael Levin
- (IBM 1955) *IBM 704 Manual of Operation* here (http://www.cs.virginia.edu/brochure/images/manuals/IBM_704/IBM_704.html)
- (Hart 1963) *AIM-57: MACRO Definitions for LISP* by Timothy P. Hart
相似文章
(如何用Python编写一个(Lisp)解释器)(2010)
Peter Norvig 的经典教程,讲解如何在Python中实现Scheme解释器,阐述了语言解释和求值的核心概念。
将 Python 转译为 Lisp
LispE 是 NAVER 推出的一款开源 Lisp 方言,兼具函数式与数组编程特性,并支持 PyTorch、llama.cpp 以及 MLX 等 AI 库。该语言既可作为原生应用运行,也可打包为支持多线程与现代函数式编程特性的 WebAssembly 库。
CPPL:一种电路提示编程语言
CPPL是一个编译器中介框架,通过使用Python领域特定语言和基于JSON的中间表示,连接大语言模型与硬件设计,从而实现可静态检查、可优化的RTL生成。
我构建了一个将Python重写为面向模型表示的编译器
Vulpine是一个编译器,它将人类可读的Python代码转换为针对LLM优化的压缩宏表示,平均减少13.8%的token数,同时支持精确的结构重建。
Lunacy - 具备惰性基本块版本化与JIT的Lua 5.1解释器
Lunacy 是一个用Rust编写的Lua 5.1解释器,实现了惰性基本块版本化和即时编译器,详细信息见技术博客文章。