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)