Approximately fully functional!
This commit is contained in:
@@ -174,3 +174,4 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
output.txt
|
||||||
@@ -1,6 +1,148 @@
|
|||||||
import atom
|
import math
|
||||||
import reader
|
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:
|
class Lisp:
|
||||||
|
def __init__(self, env=None):
|
||||||
|
if env is None:
|
||||||
|
self.env = {}
|
||||||
|
else:
|
||||||
|
self.env = env
|
||||||
|
|
||||||
def evaluate(self, expression):
|
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 re
|
||||||
import lisp
|
import lisp
|
||||||
|
from lisp import prettyprint
|
||||||
|
|
||||||
|
|
||||||
class Reader:
|
class Reader:
|
||||||
tokens: list[str]
|
tokens: list[str]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, outputfile: str):
|
||||||
self.tokens = []
|
self.tokens = []
|
||||||
self.interpreter = lisp.Lisp()
|
self.interpreter = lisp.Lisp()
|
||||||
|
self.outputfile = outputfile
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if len(self.tokens) == 0:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(self.outputfile, "a") as file:
|
||||||
while self.peek() != "":
|
while self.peek() != "":
|
||||||
self.interpreter.evaluate(self.read_expression())
|
result = prettyprint(self.interpreter.evaluate(self.read_expression()))
|
||||||
self.consume()
|
print(">> " + result)
|
||||||
|
file.write(result+"\n")
|
||||||
|
except Exception as e:
|
||||||
|
print("ERROR: " + str(e))
|
||||||
|
self.tokens = []
|
||||||
|
return
|
||||||
|
self.flush()
|
||||||
|
|
||||||
def tokenize(self, expression: str):
|
def tokenize(self, expression: str):
|
||||||
self.tokens += re.findall(r"""[\s,]*[;.*]*([()']|"(?:\\.|[^\\"])*"?|[^\s()'",;]*)""", expression)
|
self.tokens += re.findall(r"""(?:;.*|[\s,]*)([()']|"(?:\\.|[^\\"])*"?|[^\s()'",;]*)""", expression)
|
||||||
|
|
||||||
def peek(self):
|
def peek(self):
|
||||||
return self.tokens[0]
|
return self.tokens[0]
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
while len(self.tokens) > 0:
|
||||||
|
if self.peek() == "":
|
||||||
|
self.consume()
|
||||||
|
|
||||||
def consume(self):
|
def consume(self):
|
||||||
token = self.tokens[0]
|
token = self.tokens[0]
|
||||||
self.tokens = self.tokens[1:]
|
self.tokens = self.tokens[1:]
|
||||||
@@ -34,8 +52,8 @@ class Reader:
|
|||||||
while token != ")":
|
while token != ")":
|
||||||
atomlist.append(self.read_expression())
|
atomlist.append(self.read_expression())
|
||||||
while self.peek() == "":
|
while self.peek() == "":
|
||||||
self.consume()
|
self.flush()
|
||||||
expression: str = input("... ")
|
expression: str = input("... ").upper()
|
||||||
self.tokenize(expression)
|
self.tokenize(expression)
|
||||||
token = self.peek()
|
token = self.peek()
|
||||||
self.consume()
|
self.consume()
|
||||||
@@ -43,7 +61,9 @@ class Reader:
|
|||||||
elif self.peek() == "'":
|
elif self.peek() == "'":
|
||||||
# Expand the quote macro!
|
# Expand the quote macro!
|
||||||
self.consume()
|
self.consume()
|
||||||
return ["quote", self.read_expression()]
|
return ["QUOTE", self.read_expression()]
|
||||||
|
elif self.peek() == ")":
|
||||||
|
raise SyntaxError("Unexpected ')'")
|
||||||
else:
|
else:
|
||||||
return self.read_atom()
|
return self.read_atom()
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import reader
|
import reader
|
||||||
|
import sys
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Welcome message.")
|
print("Welcome message.")
|
||||||
interpreter = reader.Reader()
|
outputfile = "output.txt" if len(sys.argv) == 1 else sys.argv[1]
|
||||||
|
interpreter = reader.Reader(outputfile)
|
||||||
# REPL Loop
|
# REPL Loop
|
||||||
while True:
|
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.tokenize(expression)
|
||||||
interpreter.run()
|
interpreter.run()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user