使用Python編寫一個Lisp語言的解釋器
一般的源代碼程序經(jīng)過編譯器解析生成解析樹。Lisp的奇特之處就在于,你可以完全卸除程序,控制這種解析樹,進行任意的存取操作,也就是可以用程序生成程序。
Python號稱最接近Lisp的語言,但它終究不是。但是因為幾乎所有語言都是圖靈完備的,所以即使Python無法實現(xiàn)Lisp的某個功能,也可以通過在Python中寫一個Lisp解釋器來實現(xiàn)那個功能。很奇妙是不是?
我們來寫一個簡單的基于Scheme語法的Lisp解析器吧:
先導(dǎo)入庫
################ 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)
創(chuàng)建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)創(chuàng)建環(huán)境
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, # 小數(shù)除
'quotient': op.floordiv, # 商 地板除法 整數(shù)除
'>': 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執(zhí)行函數(shù)
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] == '設(shè)': # (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)交互執(zhí)行函數(shù)
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:
# 實際上,這個函數(shù)只是簡單地迭代了run_lines的所有結(jié)果,并沒有對其進行任何操作。
# 最后,返回run_lines的最后一個結(jié)果。
for result in run_lines(source, env):
pass
return result運行測試
percent = """ (define a 126) (define b (* 6 50)) (* (/ a b) 100) """ run(percent)
輸出:42
當(dāng)然我們也可以用中文關(guān)鍵字:
percent = """ (設(shè) a 126) (設(shè) b (* 6 50)) (* (/ a b) 100) """ run(percent)
這樣看起來是不是更親切一些了呢?
以上代碼節(jié)選自:https://github.com/fluentpython/lispy
附:
scheme學(xué)習(xí)資料:The Scheme Programming Language, 4th Edition
到此這篇關(guān)于使用Python編寫一個Lisp語言的解釋器的文章就介紹到這了,更多相關(guān)Python Lisp語言解釋器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pycharm配置Anaconda虛擬環(huán)境全過程
這篇文章主要介紹了pycharm配置Anaconda虛擬環(huán)境全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Python數(shù)據(jù)分析庫pandas基本操作方法
下面小編就為大家分享一篇Python數(shù)據(jù)分析庫pandas基本操作方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04

