From b492029b36072dca554d199fe091a8ebf7c73e6b Mon Sep 17 00:00:00 2001 From: mryouse Date: Sun, 22 May 2022 20:17:41 +0000 Subject: Unary -> List --- interpreter.py | 159 ++++++++++++++++++++++++++++----------------------------- parser.py | 42 ++------------- 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__("", 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("", args, expr.args[1:]) +def interpretLambda(symbol, args, env): + if args[0].args[0] != None: + func = UserFunction("", args[0].args, args[1:]) else: - func = UserFunction("", [], expr.args[1:]) - #GLOBALS.register(name, func) + func = UserFunction("", [], 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 (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 -- cgit v1.2.3