General source code program through the compiler to generate a parse tree, Lisp's peculiarities is that you can completely uninstall the program, control the parse tree, arbitrary access operations, that is, you can use the program to generate the program.
Python claims to be the closest language to Lisp, but it's ultimately not. But since almost all languages are Turing-complete, even if Python can't do something in Lisp, you can do that thing by writing a Lisp interpreter in Python. It's marvelous isn't it?
Let's write a simple Lisp parser based on Scheme syntax:
pilot entry
################ : Scheme Interpreter in Python 3.10 ## (c) Peter Norvig, 2010-18; See / ## 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)
Creating a Parse 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 ('(', ' ( ').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 = (0) if '(' == token: exp = [] while tokens[0] != ')': (read_from_tokens(tokens)) (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)
Creating the Environment
def standard_env() -> Environment: "An environment with some Scheme standard procedures." env: Environment = {} (vars(math)) # sin, cos, sqrt, pi, ... ( { '+': , '-': , '*': , '/': , # Fractional division 'quotient': , # Quotient Floor division Integer division '>': , '<': , '>=': , '<=': , '=': , 'abs': abs, 'append': lambda *args: list(chain(*args)), 'apply': lambda proc, args: proc(*args), 'begin': lambda *x: x[-1], 'Up': lambda *x: x[-1], 'car': lambda x: x[0], 'cdr': lambda x: x[1:], 'cons': lambda x, y: [x] + y, 'eq?': op.is_, 'equal?': , '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=''), 'conspicuous': lambda x: print(lispstr(x), end=''), 'newline': lambda: print(), } ) return env
executable function
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] == 'Set': # (define var exp) _, var, exp = x env[var] = evaluate(exp, env) elif x[0] == 'Letter': # (lambda (var...) body) _, parms, body = x return Procedure(parms, body, env) elif x[0] == 'Lead': # (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) 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)
executable function (math.)
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: # In fact, this function simply iterates over all the results of run_lines and does nothing with them. # Finally, return the last result of run_lines. for result in run_lines(source, env): pass return result
operational test
percent = """ (define a 126) (define b (* 6 50)) (* (/ a b) 100) """ run(percent)
Output: 42
Of course we can also use Chinese keywords:
percent = """ (found a 126) (found b (* 6 50)) (* (/ a b) 100) """ run(percent)
Doesn't this look a little more intimate?
The above code is excerpted from:/fluentpython/lispy
Attachment:
scheme learning materials:The Scheme Programming Language, 4th Edition
to this article on the use of Python to write a Lisp language interpreter is introduced to this article, more related to Python Lisp language interpreter content, please search for my previous articles or continue to browse the following related articles I hope you will support me in the future!