105 lines
3.2 KiB
Python
105 lines
3.2 KiB
Python
import re
|
|
import lisp
|
|
|
|
### READER.PY
|
|
### Autumn Wolf
|
|
### 04/26/26
|
|
### CSE3024
|
|
# This file contains the main Reader class, which is responsible for tokenizing, parsing, and handling user input,
|
|
# passing it to the lisp interpreter for evaluation, then returning the results to the user.
|
|
|
|
# Recursively print data for human readability
|
|
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 Reader:
|
|
tokens: list[str]
|
|
|
|
def __init__(self, outputfile: str):
|
|
self.tokens = []
|
|
self.interpreter = lisp.Lisp()
|
|
self.outputfile = outputfile
|
|
|
|
# Run through tokens, passing them to the interpreter
|
|
def run(self):
|
|
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()
|
|
|
|
# Capture or discard tokens based on Lisp syntax
|
|
def tokenize(self, expression: str):
|
|
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:]
|
|
return token
|
|
|
|
# Parse an expression into a nested list of values for interpreter consumption
|
|
def read_expression(self):
|
|
if len(self.tokens) == 0:
|
|
return None
|
|
if self.peek() == "(":
|
|
self.consume()
|
|
atomlist = []
|
|
token = self.peek()
|
|
while token != ")":
|
|
atomlist.append(self.read_expression())
|
|
while self.peek() == "":
|
|
self.flush()
|
|
expression: str = input("... ").upper()
|
|
self.tokenize(expression)
|
|
token = self.peek()
|
|
self.consume()
|
|
return atomlist
|
|
elif self.peek() == "'":
|
|
# Expand the quote macro!
|
|
self.consume()
|
|
return ["QUOTE", self.read_expression()]
|
|
elif self.peek() == ")":
|
|
raise SyntaxError("Unexpected ')'")
|
|
else:
|
|
return self.read_atom()
|
|
|
|
# Smart value conversions for some Lisp-specific syntax, such as T and NIL.
|
|
def read_atom(self):
|
|
token = self.consume()
|
|
if token.upper() == "T":
|
|
return True
|
|
elif token.upper() == "NIL":
|
|
return False
|
|
try:
|
|
return int(token)
|
|
except ValueError:
|
|
try:
|
|
return float(token)
|
|
except ValueError:
|
|
return token |