import operator as op
# 1. Tokenizer: split input string into tokens
def tokenize(s):
return s.replace('(', ' ( ').replace(')', ' ) ').split()
# 2. Parser: convert tokens into nested Python lists (AST)
def parse(tokens):
if len(tokens) == 0:
raise SyntaxError("Unexpected EOF")
token = tokens.pop(0)
if token == '(':
L = []
while tokens[0] != ')':
L.append(parse(tokens))
tokens.pop(0) # remove ')'
return L
elif token == ')':
raise SyntaxError("Unexpected )")
else:
return atom(token)
# 3. Atom: numbers become int/float, others are symbols (strings)
def atom(token):
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
return str(token)
# 4. Environment: built-in functions
def standard_env():
env = {}
env.update({
'+': op.add,
'-': op.sub,
'*': op.mul,
'/': op.truediv,
'eq?': op.eq,
'lt?': op.lt,
'gt?': op.gt,
'print': print
})
return env
# 5. Evaluator
def eval(x, env):
if isinstance(x, str): # variable reference
return env[x]
elif not isinstance(x, list): # constant literal
return x
elif x[0] == 'define': # (define var expr)
(_, var, expr) = x
env[var] = eval(expr, env)
else: # procedure call
proc = eval(x[0], env)
args = [eval(arg, env) for arg in x[1:]]
return proc(*args)
# 6. REPL
def repl():
env = standard_env()
while True:
try:
expr = input('lisp> ')
if expr.strip() == "exit":
break
val = eval(parse(tokenize(expr)), env)
if val is not None:
print(val)
except Exception as e:
print("Error:", e)
if __name__ == "__main__":
repl()
No comments:
Post a Comment