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? - | 
