使用Python编写一个Lisp语言的解释器
一般的源代码程序经过编译器解析生成解析树。Lisp的奇特之处就在于,你可以完全卸除程序,控制这种解析树,进行任意的存取操作,也就是可以用程序生成程序。
Python号称最接近Lisp的语言,但它终究不是。但是因为几乎所有语言都是图灵完备的,所以即使Python无法实现Lisp的某个功能,也可以通过在Python中写一个Lisp解释器来实现那个功能。很奇妙是不是?
我们来写一个简单的基于Scheme语法的Lisp解析器吧:
先导入库
################ lis.py: Scheme Interpreter in Python 3.10 ## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html ## Type hints and minor additions by Luciano Ramalho import math import operator as op from collections import ChainMap from itertools import chain from typing import Any, NoReturn from typing import Union, List, MutableMapping, Optional, Iterator Symbol = str Atom = Union[float, int, Symbol] Expression = Union[Atom, List] Environment = MutableMapping[Symbol, object] print(Atom, Expression) print(Environment)
创建Parse解析
def parse(program: str) -> Expression: "Read a Scheme expression from a string." return read_from_tokens(tokenize(program)) def tokenize(s: str) -> List[str]: "Convert a string into a list of tokens." return s.replace('(', ' ( ').replace(')', ' ) ').split() def read_from_tokens(tokens: List[str]) -> Expression: "Read an expression from a sequence of tokens." if len(tokens) == 0: raise SyntaxError('unexpected EOF while reading') token = tokens.pop(0) if '(' == token: exp = [] while tokens[0] != ')': exp.append(read_from_tokens(tokens)) tokens.pop(0) # discard ')' return exp elif ')' == token: raise SyntaxError('unexpected )') else: return parse_atom(token) def parse_atom(token: str) -> Atom: "Numbers become numbers; every other token is a symbol." try: return int(token) except ValueError: try: return float(token) except ValueError: return Symbol(token)
创建环境
def standard_env() -> Environment: "An environment with some Scheme standard procedures." env: Environment = {} env.update(vars(math)) # sin, cos, sqrt, pi, ... env.update( { '+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv, # 小数除 'quotient': op.floordiv, # 商 地板除法 整数除 '>': op.gt, '<': op.lt, '>=': op.ge, '<=': op.le, '=': op.eq, 'abs': abs, 'append': lambda *args: list(chain(*args)), 'apply': lambda proc, args: proc(*args), 'begin': lambda *x: x[-1], '起': lambda *x: x[-1], 'car': lambda x: x[0], 'cdr': lambda x: x[1:], 'cons': lambda x, y: [x] + y, 'eq?': op.is_, 'equal?': op.eq, 'filter': lambda *args: list(filter(*args)), 'length': len, 'list': lambda *x: list(x), 'list?': lambda x: isinstance(x, list), 'map': lambda *args: list(map(*args)), 'max': max, 'min': min, 'not': op.not_, 'null?': lambda x: x == [], 'number?': lambda x: isinstance(x, (int, float)), 'procedure?': callable, 'round': round, 'symbol?': lambda x: isinstance(x, Symbol), 'display': lambda x: print(lispstr(x), end=''), '显': lambda x: print(lispstr(x), end=''), 'newline': lambda: print(), } ) return env
执行函数
def evaluate(x: Expression, env: Environment) -> Any: "Evaluate an expression in an environment." if isinstance(x, str): # variable reference return env[x] elif not isinstance(x, list): # constant literal return x elif x[0] == 'define': # (define var exp) _, var, exp = x env[var] = evaluate(exp, env) elif x[0] == 'lambda': # (lambda (var...) body) _, parms, body = x return Procedure(parms, body, env) elif x[0] == 'quote': # (quote exp) _, exp = x return exp elif x[0] == 'if': # (if test consequence alternative) _, test, consequence, alternative = x if evaluate(test, env): return evaluate(consequence, env) else: return evaluate(alternative, env) elif x[0] == '设': # (define var exp) _, var, exp = x env[var] = evaluate(exp, env) elif x[0] == '函': # (lambda (var...) body) _, parms, body = x return Procedure(parms, body, env) elif x[0] == '引': # (quote exp) _, exp = x return exp elif x[0] == '若': # (if test consequence alternative) _, test, consequence, alternative = x if evaluate(test, env): return evaluate(consequence, env) else: return evaluate(alternative, env) else: # (proc arg...) proc_exp, *args = x proc = evaluate(proc_exp, env) arg_values = [evaluate(exp, env) for exp in args] return proc(*arg_values)
交互执行函数
def run_lines(source: str, env: Optional[Environment] = None) -> Iterator[Any]: global_env: Environment = ChainMap({}, standard_env()) if env is not None: global_env.update(env) tokens = tokenize(source) while tokens: exp = read_from_tokens(tokens) yield evaluate(exp, global_env) def run(source: str, env: Optional[Environment] = None) -> Any: # 实际上,这个函数只是简单地迭代了run_lines的所有结果,并没有对其进行任何操作。 # 最后,返回run_lines的最后一个结果。 for result in run_lines(source, env): pass return result
运行测试
percent = """ (define a 126) (define b (* 6 50)) (* (/ a b) 100) """ run(percent)
输出:42
当然我们也可以用中文关键字:
percent = """ (设 a 126) (设 b (* 6 50)) (* (/ a b) 100) """ run(percent)
这样看起来是不是更亲切一些了呢?
以上代码节选自:https://github.com/fluentpython/lispy
附:
scheme学习资料:The Scheme Programming Language, 4th Edition
到此这篇关于使用Python编写一个Lisp语言的解释器的文章就介绍到这了,更多相关Python Lisp语言解释器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
提升Python项目整洁度使用import linter实例探究
在复杂的Python项目中,良好的代码组织结构是维护性和可读性的关键,本文将深入研究 import-linter 工具,它是一个强大的静态分析工具,旨在优化项目的模块导入,提高代码质量和可维护性2024-01-01
最新评论