Approximately fully functional!

This commit is contained in:
etc404
2026-04-27 00:43:43 -06:00
parent 405f5f545a
commit 4ed01f8439
5 changed files with 187 additions and 13 deletions
+1
View File
@@ -174,3 +174,4 @@ cython_debug/
# PyPI configuration file
.pypirc
output.txt
+2
View File
@@ -0,0 +1,2 @@
run:
python repl.py
+145 -3
View File
@@ -1,6 +1,148 @@
import atom
import reader
import math
import string
from functools import reduce
def prettyprint(expression) -> str:
if type(expression) is list:
retvalue = "("
for x in expression:
retvalue += prettyprint(x) + " "
return retvalue.rstrip() + ")"
elif type(expression) is bool:
return "T" if expression else "NIL"
elif str():
return expression.upper()
else:
return str(expression)
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
def evaluate(self, expression):
print(expression)
if type(expression) is not list:
if expression in self.env.keys():
return self.env[expression]
return expression
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:]]
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 _:
if operation in self.env.keys():
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]
return functionrunner.evaluate(self.env[operation].expression)
else:
raise TypeError("This value is not a valid function: " + operation)
else:
raise ValueError("Undefined operation: " + operation)
+28 -8
View File
@@ -1,24 +1,42 @@
import re
import lisp
from lisp import prettyprint
class Reader:
tokens: list[str]
def __init__(self):
def __init__(self, outputfile: str):
self.tokens = []
self.interpreter = lisp.Lisp()
self.outputfile = outputfile
def run(self):
while self.peek() != "":
self.interpreter.evaluate(self.read_expression())
self.consume()
if len(self.tokens) == 0:
return
try:
with open(self.outputfile, "a") as file:
while self.peek() != "":
result = prettyprint(self.interpreter.evaluate(self.read_expression()))
print(">> " + result)
file.write(result+"\n")
except Exception as e:
print("ERROR: " + str(e))
self.tokens = []
return
self.flush()
def tokenize(self, expression: str):
self.tokens += re.findall(r"""[\s,]*[;.*]*([()']|"(?:\\.|[^\\"])*"?|[^\s()'",;]*)""", expression)
self.tokens += re.findall(r"""(?:;.*|[\s,]*)([()']|"(?:\\.|[^\\"])*"?|[^\s()'",;]*)""", expression)
def peek(self):
return self.tokens[0]
def flush(self):
while len(self.tokens) > 0:
if self.peek() == "":
self.consume()
def consume(self):
token = self.tokens[0]
self.tokens = self.tokens[1:]
@@ -34,8 +52,8 @@ class Reader:
while token != ")":
atomlist.append(self.read_expression())
while self.peek() == "":
self.consume()
expression: str = input("... ")
self.flush()
expression: str = input("... ").upper()
self.tokenize(expression)
token = self.peek()
self.consume()
@@ -43,7 +61,9 @@ class Reader:
elif self.peek() == "'":
# Expand the quote macro!
self.consume()
return ["quote", self.read_expression()]
return ["QUOTE", self.read_expression()]
elif self.peek() == ")":
raise SyntaxError("Unexpected ')'")
else:
return self.read_atom()
+11 -2
View File
@@ -1,11 +1,20 @@
import reader
import sys
def main():
print("Welcome message.")
interpreter = reader.Reader()
outputfile = "output.txt" if len(sys.argv) == 1 else sys.argv[1]
interpreter = reader.Reader(outputfile)
# REPL Loop
while True:
expression: str = input(">>> ")
try:
expression: str = input("> ").upper()
except KeyboardInterrupt:
print("\n>> bye")
return
except EOFError:
print("\n>> bye")
return
interpreter.tokenize(expression)
interpreter.run()