diff options
Diffstat (limited to 'interpreter.py')
| -rw-r--r-- | interpreter.py | 159 |
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? - |
