Files
lisp-interpreter/lisp.py
T
2026-04-27 01:44:38 -06:00

156 lines
4.6 KiB
Python

import math
from functools import reduce
### LISP.PY
### Autumn Wolf
### 04/26/26
### CSE3024
# This file (and contained class) handles lisp interpreter details, environment management, and expression evaluation.
class Function:
def __init__(self, parameters, expression):
self.parameters = parameters
self.expression = expression
class Lisp:
def __init__(self, env=None):
if env is None:
self.env = {}
else:
self.env = env
# Recursively evaluate an expression
def evaluate(self, expression):
# Handle literals
if type(expression) is not list:
if expression in self.env.keys():
return self.env[expression]
return expression
# Handling of some special operations
operation = expression[0]
if type(operation) is list:
operation = self.evaluate(operation)
if type(operation) is list:
raise TypeError("This value is not a valid operation: " + str(operation))
if operation == "QUOTE":
return expression[1:][0]
if operation != "DEFUN":
arguments = [self.evaluate(x) for x in expression[1:]] # Don't evaluate for defun or quote!
else:
arguments = expression[1:]
match operation:
case "+":
return sum(arguments)
case "-":
return arguments[0] - sum(arguments[1:])
case "*":
return reduce(lambda x,y: x*y, arguments)
case "/":
return reduce(lambda x,y: x/y, arguments)
case "CAR":
if len(arguments) != 1:
raise ValueError("Expected 1 argument")
return arguments[0][0]
case "CDR":
if len(arguments) != 1:
raise ValueError("Expected 1 argument")
return arguments[0][1:]
case "CONS":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
if type(arguments[0]) == list:
return [arguments[0]]+arguments[1]
else:
return [arguments[0]]+arguments[1]
case "SQRT":
if len(arguments) != 1:
raise ValueError("Expected 1 argument")
return math.sqrt(arguments[0])
case "POW":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return pow(arguments[0], arguments[1])
case "IF":
if len(arguments) != 3:
raise ValueError("Expected 3 arguments")
if arguments[0]:
return arguments[1]
else:
return arguments[2]
case ">":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] > arguments[1]
case "<":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] < arguments[1]
case "=":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] == arguments[1]
case "!=":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] != arguments[1]
case "AND":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] and arguments[1]
case "OR":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
return arguments[0] or arguments[1]
case "NOT":
if len(arguments) != 1:
raise ValueError("Expected 1 argument")
return not arguments[0]
case "QUIT":
print(">> bye")
raise SystemExit
case "DEFINE":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
self.env[arguments[0]] = arguments[1]
return arguments[0]
case "DEFUN":
if len(arguments) != 3:
raise ValueError("Expected 3 arguments")
self.env[arguments[0]] = Function(arguments[1], arguments[2])
return arguments[0]
case "SET!":
if len(arguments) != 2:
raise ValueError("Expected 2 arguments")
if arguments[0] in self.env:
self.env[arguments[0]] = arguments[1]
else:
raise ValueError("Undefined variable: " + str(arguments[0]))
return arguments[0]
case "MAPCAR":
if len(arguments) != 3:
raise ValueError("Expected 3 arguments")
result = []
for i in range(0, min(len(arguments[1]), len(arguments[2]))):
result.append(self.evaluate([arguments[0], arguments[1][i], arguments[2][i]]))
return result
case _:
if operation in self.env.keys(): # Check for user-defined functions
if type(self.env[operation]) == Function:
func = self.env[operation]
functionrunner = Lisp(self.env)
if len(arguments) != len(func.parameters):
raise ValueError("Expected " + str(len(func.parameters)) + " arguments")
for i in range(0, len(arguments)):
functionrunner.env[func.parameters[i]] = arguments[i] # Populate sub-interpreter environment
return functionrunner.evaluate(self.env[operation].expression)
else:
raise TypeError("This value is not a valid function: " + operation)
else:
raise ValueError("Undefined operation: " + operation)