aboutsummaryrefslogtreecommitdiff
path: root/interpreter.py
diff options
context:
space:
mode:
Diffstat (limited to 'interpreter.py')
-rw-r--r--interpreter.py159
1 files changed, 77 insertions, 82 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?
-