aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormryouse2022-05-22 20:17:41 +0000
committermryouse2022-05-22 20:17:41 +0000
commitb492029b36072dca554d199fe091a8ebf7c73e6b (patch)
tree8d3852a58a5cf643c4c2cb804a4f7ed6fe4cf0d5
parent4eca7ce14c5ad11514a44de3cdadc38d32128810 (diff)
Unary -> List
-rw-r--r--interpreter.py159
-rw-r--r--parser.py42
2 files changed, 81 insertions, 120 deletions
diff --git a/interpreter.py b/interpreter.py
index f133b1f..4adb972 100644
--- a/interpreter.py
+++ b/interpreter.py
@@ -20,14 +20,14 @@ class Builtin(Function):
super().__init__("<builtin>", None, callable_, *arities)
def call(self, expr, env):
- if self.arities is not None and len(expr.args) not in self.arities:
+ if self.arities is not None and len(expr.args[1:]) not in self.arities:
#if self.arity >= 0 and len(args) != self.arity:
fmt = f"[{self.arities[0]}"
for arity in self.arities[1:]:
fmt += f", {arity}"
fmt += "]"
raise Exception(f"expected {fmt} arguments, received {len(expr.args)}")
- return self.body(expr, env)
+ return self.body(expr.args[0], expr.args[1:], env)
class UserFunction(Function):
@@ -38,7 +38,7 @@ class UserFunction(Function):
this_env = Environment(env)
for idx, param in enumerate(self.params):
# TODO this is wrong!!! this won't always be a literal
- this_env.register(param.name, Expr.Literal(evaluate(expr.args[idx],env)))
+ this_env.register(param.name, Expr.Literal(evaluate(expr.args[idx+1],env)))
return interpret(self.body, this_env)
class Environment:
@@ -86,21 +86,24 @@ def evaluate(expr, env):
elif isinstance(expr, Expr.Symbol):
if not env.contains(expr.name):
raise Exception(f"no such symbol: {expr}")
- return interpretEnv(expr, env.get(expr.name), env)
+ return evaluate(env.get(expr.name), env)
- name = expr.symbol.name
+ #name = expr.symbol.name
+ if not isinstance(expr.args[0], Expr.Symbol):
+ raise Exception("can't evaluate without a symbol")
+ name = expr.args[0].name
if name == "def":
- return interpretDef(expr, env)
+ return interpretDef(expr.args[0], expr.args[1:], env)
elif env.contains(name):
return env.get(name).call(expr, env)
else:
raise Exception(f"unable to evaluate: {expr}")
-def interpretOr(expr, env):
+def interpretOr(symbol, args, env):
# or returns true for the first expression that returns true
- if len(expr.args) < 2:
+ if len(args) < 2:
raise Exception("'or' has at least two operands")
- for arg in expr.args:
+ for arg in args:
ev = evaluate(arg, env)
if ev not in (True, False):
raise Exception("'or' needs boolean arguments")
@@ -110,11 +113,11 @@ def interpretOr(expr, env):
GLOBALS.register("or", Builtin(interpretOr))
-def interpretAnd(expr, env):
+def interpretAnd(symbol, args, env):
# and returns false for the first expression that returns false
- if len(expr.args) < 2:
+ if len(args) < 2:
raise Exception("'and' has at least two operands")
- for arg in expr.args:
+ for arg in args:
ev = evaluate(arg, env)
if ev not in (True, False):
raise Exception("'and' needs boolean arguments")
@@ -124,29 +127,29 @@ def interpretAnd(expr, env):
GLOBALS.register("and", Builtin(interpretAnd))
-def interpretEq(expr, env):
+def interpretEq(symbol, args, env):
# equal
- first = evaluate(expr.args[0], env)
- second = evaluate(expr.args[1], env)
+ first = evaluate(args[0], env)
+ second = evaluate(args[1], env)
return first == second
GLOBALS.register("eq?", Builtin(interpretEq, 2))
-def interpretComparison(expr, env):
- left = evaluate(expr.args[0], env)
+def interpretComparison(symbol, args, env):
+ left = evaluate(args[0], env)
if type(left) not in (int, float):
raise Exception("'left' must be a number")
- right = evaluate(expr.args[1], env)
+ right = evaluate(args[1], env)
if type(right) not in (int, float):
raise Exception("'right' must be a number")
- if expr.symbol.name == ">":
+ if symbol.name == ">":
return left > right
- elif expr.symbol.name == ">=":
+ elif symbol.name == ">=":
return left >= right
- elif expr.symbol.name == "<":
+ elif symbol.name == "<":
return left < right
- elif expr.symbol.name == "<=":
+ elif symbol.name == "<=":
return left <= right
GLOBALS.register(">", Builtin(interpretComparison, 2))
@@ -154,42 +157,42 @@ GLOBALS.register(">=", Builtin(interpretComparison, 2))
GLOBALS.register("<", Builtin(interpretComparison, 2))
GLOBALS.register("<=", Builtin(interpretComparison, 2))
-def interpretTerm(expr, env):
- if len(expr.args) < 1:
+def interpretTerm(symbol, args, env):
+ if len(args) < 1:
raise Exception("term has at least one operand")
res = None
- for arg in expr.args:
+ for arg in args:
ev = evaluate(arg, env)
if type(ev) not in (int, float):
raise Exception("term must be a number")
if res is None:
res = ev
- elif expr.symbol.name == "+":
+ elif symbol.name == "+":
res += ev
- elif expr.symbol.name == "-":
+ elif symbol.name == "-":
res -= ev
return res
GLOBALS.register("+", Builtin(interpretTerm))
GLOBALS.register("-", Builtin(interpretTerm))
-def interpretFactor(expr, env):
- if expr.symbol.name == "/":
- num = evaluate(expr.args[0], env)
+def interpretFactor(symbol, args, env):
+ if symbol.name == "/":
+ num = evaluate(args[0], env)
if type(num) not in (int, float):
raise Exception("numerator must be a number")
- denom = evaluate(expr.args[1], env)
+ denom = evaluate(args[1], env)
if type(denom) not in (int, float):
raise Exception("denominator must be a number")
return num / denom # TODO floats and ints
else:
- if len(expr.args) < 2:
+ if len(args) < 2:
raise Exception("'*' requires at least two operands")
- first = evaluate(expr.args[0], env)
+ first = evaluate(args[0], env)
if type(first) not in (int, float):
raise Exception("'*' operand must be a number")
res = first
- for arg in expr.args[1:]:
+ for arg in args[1:]:
tmp = evaluate(arg, env)
if type(tmp) not in (int, float):
raise Exception("'*' operand must be a number")
@@ -199,29 +202,29 @@ def interpretFactor(expr, env):
GLOBALS.register("*", Builtin(interpretFactor))
GLOBALS.register("/", Builtin(interpretFactor, 2))
-def interpretNot(expr, env):
- res = evaluate(expr.args[0], env)
+def interpretNot(symbol, args, env):
+ res = evaluate(args[0], env)
if res not in (True, False):
raise Exception("'not' only works on booleans")
return not res
GLOBALS.register("not", Builtin(interpretNot, 1))
-def interpretIf(expr, env):
+def interpretIf(symbol, args, env):
# if cond t-branch [f-branch]
- cond = evaluate(expr.args[0], env)
+ cond = evaluate(args[0], env)
if cond not in (True, False):
raise Exception("'if' condition must be boolean")
if cond:
- return evaluate(expr.args[1], env)
- elif len(expr.args) == 3:
- return evaluate(expr.args[2], env)
+ return evaluate(args[1], env)
+ elif len(args) == 3:
+ return evaluate(args[2], env)
return None # this shouldn't be reached
GLOBALS.register("if", Builtin(interpretIf, 2, 3))
-def interpretPrint(expr, env):
- ev = evaluate(expr.args[0], env)
+def interpretPrint(symbol, args, env):
+ ev = evaluate(args[0], env)
if not isinstance(ev, str):
raise Exception("can only 'print' strings")
print(ev)
@@ -230,45 +233,42 @@ def interpretPrint(expr, env):
GLOBALS.register("print", Builtin(interpretPrint, 1))
-def interpretDef(expr, env):
- if not isinstance(expr.args[0], Expr.Symbol):
+def interpretDef(symbol, args, env):
+ if not isinstance(args[0], Expr.Symbol):
raise Exception("'def' requires a string literal as a name")
- name = expr.args[0].name # NOTE: we are not evaluating the name!!
+ name = args[0].name # NOTE: we are not evaluating the name!!
if not isinstance(name, str):
raise Exception("'def' requires a string literal as a name")
- ev = evaluate(expr.args[1], env)
+ ev = evaluate(args[1], env)
if isinstance(ev, UserFunction):
env.register(name, ev)
else:
- env.register(name, expr.args[1])
+ env.register(name, args[1])
return None
GLOBALS.register("def", Builtin(interpretDef, 2))
-def interpretLambda(expr, env):
- if expr.args[0].symbol != None:
- args = expr.args[0].args
- args = [expr.args[0].symbol] + args
- func = UserFunction("<lambda>", args, expr.args[1:])
+def interpretLambda(symbol, args, env):
+ if args[0].args[0] != None:
+ func = UserFunction("<lambda>", args[0].args, args[1:])
else:
- func = UserFunction("<lambda>", [], expr.args[1:])
- #GLOBALS.register(name, func)
+ func = UserFunction("<lambda>", [], args[1:])
return func
GLOBALS.register("lambda", Builtin(interpretLambda))
-def interpretToString(expr, env):
- return str(evaluate(expr.args[0], env))
+def interpretToString(symbol, args, env):
+ return str(evaluate(args[0], env))
GLOBALS.register("->string", Builtin(interpretToString, 1))
-def interpretConcat(expr, env):
+def interpretConcat(symbol, args, env):
# concat str1 str2...strN
- if len(expr.args) < 2:
+ if len(args) < 2:
raise Exception("'concat' takes at least two arguments")
out = ""
- for arg in expr.args:
+ for arg in args:
tmp = evaluate(arg, env)
if not isinstance(tmp, str):
raise Exception("'concat' arguments must be strings")
@@ -277,27 +277,27 @@ def interpretConcat(expr, env):
GLOBALS.register("concat", Builtin(interpretConcat))
-def interpretForCount(expr, env):
+def interpretForCount(symbol, args, env):
# for-count int exprs
- num = evaluate(expr.args[0], env)
+ num = evaluate(args[0], env)
if type(num) is not int:
raise Exception("'for-count' count must be an integer")
new_env = Environment(env)
ret = None
for idx in range(0, num):
new_env.register("idx", Expr.Literal(idx + 1))
- for arg in expr.args[1:]:
+ for arg in args[1:]:
ret = evaluate(arg, new_env)
return ret
GLOBALS.register("for-count", Builtin(interpretForCount))
-def interpretPipe(expr, env):
- if len(expr.args) < 2:
+def interpretPipe(symbol, args, env):
+ if len(args) < 2:
raise Exception("'|' takes at least two expressions")
new_env = Environment(env)
pipe = None
- for arg in expr.args:
+ for arg in args:
if pipe is not None:
new_env.register("items", pipe)
pipe = Expr.Literal(evaluate(arg, new_env))
@@ -305,37 +305,32 @@ def interpretPipe(expr, env):
GLOBALS.register("|", Builtin(interpretPipe))
-def interpretBranch(expr, env):
- if len(expr.args) == 0:
+def interpretBranch(symbol, args, env):
+ if len(args) == 0:
raise Exception("'branch' takes at least one expression")
- for arg in expr.args:
- if len(arg.args) != 1:
+ for arg in args:
+ if len(arg.args) != 2:
raise Exception("'branch' branches have two expressions")
- cond = evaluate(arg.symbol, env) # this is the condition
+ cond = evaluate(arg.args[0], env) # this is the condition
if cond:
- return evaluate(arg.args[0], env)
+ return evaluate(arg.args[1], env)
return None
GLOBALS.register("branch", Builtin(interpretBranch))
-def interpretFunc(expr, env):
+def interpretFunc(symbol, args, env):
# func <name> (args) (exprs)
- if len(expr.args) < 3:
+ if len(args) < 3:
raise Exception("'func' takes a name, arguments, and at least one expression")
- if not isinstance(expr.args[0], Expr.Symbol):
+ if not isinstance(args[0], Expr.Symbol):
raise Exception("'func' requires a string literal as a name")
- name = expr.args[0].name # NOTE: we are not evaluating the name!!
+ name = args[0].name # NOTE: we are not evaluating the name!!
# compose a lambda
- lambda_expr = Expr.Nary("lambda", expr.args[1:])
- func = interpretLambda(lambda_expr, env)
+ func = interpretLambda(None, args[1:], env)
env.register(name, func)
return None
GLOBALS.register("func", Builtin(interpretFunc))
-def interpretEnv(expr, env_expr, env):
- ev = evaluate(env_expr, env)
- return ev # TODO more than this?
-
diff --git a/parser.py b/parser.py
index 6acda5d..7916a97 100644
--- a/parser.py
+++ b/parser.py
@@ -27,47 +27,13 @@ class Expr:
def __str__(self):
return f"'{self.name}"
- class Nary:
- def __init__(self, symbol, args):
- self.symbol = symbol
+ class List:
+ def __init__(self, args):
self.args = args
def accept(self, visitor):
visitor.visitNary(self)
def __str__(self):
- out = f"[{self.symbol} => ("
- first = True
- for arg in self.args:
- if first:
- out = f"{out} {arg}"
- first = False
- else:
- out = f"{out}, {arg}"
- return f"{out} )]"
-
- class Binary:
- def __init__(self, symbol, left, right):
- self.symbol = symbol
- self.left = left
- self.right = right
- def accept(self, visitor):
- visitor.visitBinary(self)
-
-class Visitor:
- def visitLiteral(self, literal):
- return literal.value
- def visitBinary(self, binary):
- sym = binary.symbol.text
- left = binary.left.accept(self)
- right = binary.right.accept(self)
- return "({sym} {left} {right}"
- def visitNary(self, nary):
- sym = nary.symbol.text
- args = []
- for arg in nary.args:
- args.append(arg.accept(self))
- return "({sym} {' '.join(args)})"
- def visitSymbol(self, symbol):
- return symbol.name
+ return "(" + " ".join(f"{arg}" for arg in self.args) + ")"
def parseExpression(token, prev, tokens):
idx = 0
@@ -91,7 +57,7 @@ def parseExpression(token, prev, tokens):
idx += inc
prev = token
- return Expr.Nary(args[0], args[1:]), idx + 2 # parens
+ return Expr.List(args), idx + 2 # parens
def parseSymbol(token, prev, tokens):
return Expr.Symbol(token.text), 1