Approximately fully functional!
This commit is contained in:
@@ -174,3 +174,4 @@ cython_debug/
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
output.txt
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user