diff options
| -rw-r--r-- | interpreter.py | 308 | 
1 files changed, 75 insertions, 233 deletions
| diff --git a/interpreter.py b/interpreter.py index 84fae67..1e6d78d 100644 --- a/interpreter.py +++ b/interpreter.py @@ -193,15 +193,9 @@ def interpretAnd(symbol, args, env):  GLOBALS.register("and", Builtin(interpretAnd, [or_arg, or_arg], or_arg))  def interpretEq(symbol, args, env): -    # equal      # NOTE this currently only works for literals -    first = evaluate(args[0], env) -    second = evaluate(args[1], env) -    if not (isinstance(first, Literal) and isinstance(second, Literal)): -        raise InterpretPanic(symbol, "can only compare :literals")      # compare types because 0 != #false in neb -    # TODO number equality? -    if type(first) == type(second) and first.value == second.value: +    if type(args[0]) == type(args[1]) and args[0].value == args[1].value:          return Bool(True)      else:          return Bool(False) @@ -210,57 +204,30 @@ eq_arg = Arg("value", T.Literal, False, False)  GLOBALS.register("eq?", Builtin(interpretEq, [eq_arg, eq_arg]))  def interpretGreaterThan(symbol, args, env): -    left = evaluate(args[0], env) -    if not (isinstance(left, Int) or isinstance(left, Float)): -        raise InterpretPanic(symbol, "first argument must be a :number", left) -    right = evaluate(args[1], env) -    if not (isinstance(right, Int) or isinstance(right, Float)): -        raise InterpretPanic(symbol, "second argument must be a :number", right) -    return Bool(left.value > right.value) +    return Bool(args[0].value > args[1].value)  compare_arg = Arg("num", T.Number, False, False)  GLOBALS.register(">", Builtin(interpretGreaterThan, [compare_arg, compare_arg]))  def interpretGreaterThanEqual(symbol, args, env): -    left = evaluate(args[0], env) -    if not (isinstance(left, Int) or isinstance(left, Float)): -        raise InterpretPanic(symbol, "first argument must be a :number", left) -    right = evaluate(args[1], env) -    if not (isinstance(right, Int) or isinstance(right, Float)): -        raise InterpretPanic(symbol, "second argument must be a :number", right) -    return Bool(left.value >= right.value) +    return Bool(args[0].value >= args[1].value)  GLOBALS.register(">=", Builtin(interpretGreaterThanEqual, [compare_arg, compare_arg]))  def interpretLessThan(symbol, args, env): -    left = evaluate(args[0], env) -    if not (isinstance(left, Int) or isinstance(left, Float)): -        raise InterpretPanic(symbol, "first argument must be a :number", left) -    right = evaluate(args[1], env) -    if not (isinstance(right, Int) or isinstance(right, Float)): -        raise InterpretPanic(symbol, "second argument must be a :number", right) -    return Bool(left.value < right.value) +    return Bool(args[0].value < args[1].value)  GLOBALS.register("<", Builtin(interpretLessThan, [compare_arg, compare_arg]))  def interpretLessThanEqual(symbol, args, env): -    left = evaluate(args[0], env) -    if not (isinstance(left, Int) or isinstance(left, Float)): -        raise InterpretPanic(symbol, "first argument must be a :number", left) -    right = evaluate(args[1], env) -    if not (isinstance(right, Int) or isinstance(right, Float)): -        raise InterpretPanic(symbol, "second argument must be a :number", right) -    return Bool(left.value <= right.value) +    return Bool(args[0].value <= args[1].value)  GLOBALS.register("<=", Builtin(interpretLessThanEqual, [compare_arg, compare_arg]))  def interpretAddition(symbol, args, env):      res = 0      for arg in args: -        ev = evaluate(arg, env) -        if not (isinstance(ev, Int) or isinstance(ev, Float)): -            raise InterpretPanic(symbol, "argument must be a :number", ev) -        res += ev.value +        res += arg.value      if isinstance(res, float):          return Float(res)      else: @@ -270,18 +237,12 @@ term_arg = Arg("term", T.Number, False, False)  GLOBALS.register("+", Builtin(interpretAddition, [term_arg], term_arg))  def interpretSubtraction(symbol, args, env): -    first = evaluate(args[0], env) -    if not (isinstance(first, Int) or isinstance(first, Float)): -        raise InterpretPanic(symbol, "argument must be a :number", first)      if len(args) == 1: -        res = -first.value +        res = -args[0].value      else: -        res = first.value +        res = args[0].value      for arg in args[1:]: -        ev = evaluate(arg, env) -        if not (isinstance(ev, Int) or isinstance(ev, Float)): -            raise InterpretPanic(symbol, "argument must be a :number", ev) -        res -= ev.value +        res -= arg.value      if isinstance(res, float):          return Float(res)      else: @@ -290,15 +251,9 @@ def interpretSubtraction(symbol, args, env):  GLOBALS.register("-", Builtin(interpretSubtraction, [term_arg], term_arg))  def interpretMultiplication(symbol, args, env): -    first = evaluate(args[0], env) -    if not (isinstance(first, Int) or isinstance(first, Float)): -        raise InterpretPanic(symbol, "argument must be a :number", first) -    res = first.value +    res = args[0].value      for arg in args[1:]: -        tmp = evaluate(arg, env) -        if not (isinstance(tmp, Int) or isinstance(tmp, Float)): -            raise InterpretPanic(symbol, "argument must be a :number", tmp) -        res = res * tmp.value +        res = res * arg.value      if isinstance(res, float):          return Float(res)      else: @@ -308,13 +263,7 @@ factor_arg = Arg("factor", T.Number, False, False)  GLOBALS.register("*", Builtin(interpretMultiplication, [factor_arg, factor_arg], factor_arg))  def interpretDivision(symbol, args, env): -    num = evaluate(args[0], env) -    if not (isinstance(num, Int) or isinstance(num, Float)): -        raise InterpretPanic(symbol, "numerator must be a :number", num) -    denom = evaluate(args[1], env) -    if not (isinstance(denom, Int) or isinstance(denom, Float)): -        raise InterpretPanic(symbol, "denominator must be a :number", denom) -    ret = num.value / denom.value +    ret = args[0].value / args[1].value      if int(ret) == ret:          return Int(int(ret))      else: @@ -323,24 +272,18 @@ def interpretDivision(symbol, args, env):  GLOBALS.register("/", Builtin(interpretDivision, [factor_arg, factor_arg]))  def interpretNot(symbol, args, env): -    res = evaluate(args[0], env) -    if not isinstance(res, Bool): -        raise InterpretPanic(symbol, "requires a :bool", res) -    return Bool(not res.value) +    return Bool(not args[0].value)  not_arg = Arg("not", T.Bool, False, False)  GLOBALS.register("not", Builtin(interpretNot, [not_arg]))  def interpretIf(symbol, args, env):      # if cond t-branch [f-branch] -    cond = evaluate(args[0], env) -    if not isinstance(cond, Bool): -        raise InterpretPanic(symbol, "condition must be :bool", cond) -    if cond.value: +    if args[0].value:          return evaluate(args[1], env)      elif len(args) == 3:          return evaluate(args[2], env) -    return List([])  # this shouldn't be reached +    return List([])  cond = Arg("cond", T.Bool, False, False)  t_branch = Arg("t-branch", T.Any, False, True) @@ -348,11 +291,7 @@ f_branch = Arg("f-branch", T.Any, True, True)  GLOBALS.register("if", Builtin(interpretIf, [cond, t_branch, f_branch]))  def interpretPrint(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "requires a :string") -    print(ev.value) - +    print(args[0].value)      return List([])  # print returns nothing  GLOBALS.register("print", Builtin(interpretPrint, [Arg("arg", T.String, False, False)])) @@ -364,8 +303,7 @@ def interpretDef(symbol, args, env):      if not isinstance(name, str):          raise InterpretPanic(symbol, "requires a :string name") -    ev = evaluate(args[1], env) -    env.register(name, ev) +    env.register(name, args[1])  # TODO since this isn't lazily evaluated, side effects are allowed (bad!)      return List([])  def_name_arg = Arg("name", T.Any, False, True) @@ -379,8 +317,7 @@ def interpretRedef(symbol, args, env):      if not env.contains(name):          raise InterpretPanic(symbol, "not previously defined", args[0]) -    ev = evaluate(args[1], env) -    env.reregister(name, ev) +    env.reregister(name, args[1])      return List([])  GLOBALS.register("redef", Builtin(interpretRedef, [def_name_arg, def_val_arg])) @@ -397,13 +334,13 @@ lambda_body_arg = Arg("body", T.Any, False, True)  GLOBALS.register("lambda", Builtin(interpretLambda, [lambda_args_arg, lambda_body_arg], lambda_body_arg))  def interpretToString(symbol, args, env): -    ev = evaluate(args[0], env) -    if isinstance(ev, String): -        return ev -    elif isinstance(ev, Literal): -        return String(str(ev)) +    item = args[0] +    if isinstance(item, String): +        return item +    elif isinstance(item, Literal): +        return String(str(item))      else: -        return String(f"{ev}") +        return String(f"{item}")  GLOBALS.register("->string", Builtin(interpretToString, [Arg("arg", T.Any, False, False)])) @@ -411,10 +348,7 @@ def interpretConcat(symbol, args, env):      # concat str1 str2...strN      out = ""      for arg in args: -        tmp = evaluate(arg, env) -        if not isinstance(tmp, String): -            raise InterpretPanic(symbol, "requires :string", tmp) -        out += tmp.value +        out += arg.value      return String(out)  string_arg = Arg("arg", T.String, False, False) @@ -422,12 +356,9 @@ GLOBALS.register("concat", Builtin(interpretConcat, [string_arg, string_arg], st  def interpretForCount(symbol, args, env):      # for-count int exprs -    num = evaluate(args[0], env) -    if not isinstance(num, Int): -        raise InterpretPanic(symbol, "count must be an integer", num)      new_env = Environment(env)      ret = None -    for idx in range(0, num.value): +    for idx in range(0, args[0].value):          new_env.register("idx", Int(idx + 1))          for arg in args[1:]:              ret = evaluate(arg, new_env) @@ -441,12 +372,9 @@ GLOBALS.register("for-count", Builtin(interpretForCount, [for_count_arg, for_bod  def interpretForEach(symbol, args, env):      # for-each list exprs -    lst = evaluate(args[0], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbok, "requires a :list", lst)      new_env = Environment(env)      ret = None -    for item in lst.args: +    for item in args[0].args:          new_env.register("_item_", evaluate(item, env))          for arg in args[1:]:              ret = evaluate(arg, new_env) @@ -509,7 +437,7 @@ GLOBALS.register("func", Builtin(interpretFunc, [def_name_arg, lambda_args_arg,  # THINGS NEEDED FOR AOC  #  - read the contents of a file  def interpretReadLines(symbol, args, env): -    target_file_name = evaluate(args[0], env).value +    target_file_name = args[0].value      target_file = Path(target_file_name).resolve()      if not target_file.exists():          raise InterpretPanic(symbol, "no such file", target_file) @@ -522,36 +450,26 @@ GLOBALS.register("read-lines", Builtin(interpretReadLines, [Arg("filename", T.St  #  - strip whitespace from string  def interpretStrip(symbol, args, env): -    out = evaluate(args[0], env) -    if not isinstance(out, String): -        raise InterpretPanic(symbol, "requires a :string", out) -    return String(out.value.strip()) +    return String(args[0].value.strip())  GLOBALS.register("strip", Builtin(interpretStrip, [Arg("filename", T.String, False, False)]))  #  - string->int and string->float  def interpretStringToInt(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "requires a :string", ev)      try: -        val = int(ev.value) +        val = int(args[0].value)          return Int(val)      except: -        raise InterpretPanic(symbol, "can't convert to an :int", ev) +        raise InterpretPanic(symbol, "can't convert to an :int", args[0])  GLOBALS.register("string->int", Builtin(interpretStringToInt, [Arg("arg", T.String, False, False)]))  #  - split a string by a given field  def interpretSplit(symbol, args, env): -    target = evaluate(args[0], env) -    if not isinstance(target, String): -        raise InterpretPanic(symbol, "requires a :string as its first argument", target) +    target = args[0]      if len(args) == 1:          return List([String(char) for char in target.value], True) -    splitter = evaluate(args[1], env) -    if not isinstance(splitter, String): -        raise InterpretPanic(symbol, "requires a :string as its second argument", splitter) +    splitter = args[1]      ret = target.value.split(splitter.value)      return List([String(r) for r in ret], True) @@ -559,30 +477,21 @@ GLOBALS.register("split", Builtin(interpretSplit, [Arg("target", T.String, False  #  - get the length of a list  def interpretListLength(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic(symbol, "requires a :list", ev) -    return Int(len(ev.args)) +    return Int(len(args[0].args))  GLOBALS.register("list-length", Builtin(interpretListLength, [Arg("arg", T.List, False, False)]))  #  - first/rest of list  def interpretFirst(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic(symbol, "requires a :list", ev) -    if len(ev.args) == 0: +    if len(args[0].args) == 0:          raise InterpretPanic(symbol, "list is empty") -    return evaluate(ev.args[0], env) +    return evaluate(args[0].args[0], env)  GLOBALS.register("first", Builtin(interpretFirst, [Arg("arg", T.List, False, False)]))  def interpretRest(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic(symbol, "requires a :list", ev)      # TODO do we know it's not evaluated? -    return List(ev.args[1:], True) # we don't evaluate the remainder of the list +    return List(args[0].args[1:], True) # we don't evaluate the remainder of the list  GLOBALS.register("rest", Builtin(interpretRest, [Arg("arg", T.List, False, False)])) @@ -603,20 +512,17 @@ def interpretMap(symbol, args, env):      return List(out, True)  GLOBALS.register("map", Builtin(interpretMap, [Arg("func", T.Any, False, True), Arg("list", T.List, False, False)])) +#GLOBALS.register("map", Builtin(interpretMap, [Arg("func", T.Any, False, False), Arg("list", T.List, False, False)]))  def interpretZip(symbol, args, env): -    z1 = evaluate(args[0], env) -    if not isinstance(z1, List): -        raise InterpretPanic(symbol, "requires two :lists", z1) -    z2 = evaluate(args[1], env) -    if not isinstance(z2, List): -        raise InterpretPanic(symbol, "requires two :lists", z2) +    z1 = args[0] +    z2 = args[1]      if len(z1.args) != len(z2.args):          raise InterpretPanic(symbol, "requires two :lists of the same size")      out = []      for idx in range(len(z1.args)): -        f = evaluate(z1.args[idx], env) -        s = evaluate(z2.args[idx], env) +        f = z1.args[idx] +        s = z2.args[idx]          out.append(List([f, s], True))      return List(out, True) @@ -624,75 +530,54 @@ zip_arg = Arg("list", T.List, False, False)  GLOBALS.register("zip", Builtin(interpretZip, [zip_arg, zip_arg]))  def interpretList(symbol, args, env): -    out = [] -    for arg in args: -        out.append(evaluate(arg, env)) -    return List(out, True) +    return List(args, True)  GLOBALS.register("list", Builtin(interpretList, [], Arg("item", T.Any, False, False)))  def interpretListReverse(symbol, args, env): -    lst = evaluate(args[0], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "requires a :list", lst) -    new_args = lst.args[:] # make a copy of the args +    new_args = args[0].args[:] # make a copy of the args      new_args.reverse()      return List(new_args, True)  GLOBALS.register("list-reverse", Builtin(interpretListReverse, [Arg("list", T.List, False, False)]))  def interpretApply(symbol, args, env): +    # TODO: to support lambdas, we can't assume the func is defined      func = args[0]      if not isinstance(func, Symbol):          raise InterpretPanic(symbol, "requires a symbol as its first argument", func) -    lst = evaluate(args[1], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "requires a :list as its second argument". lst) -    new_lst = List([func] + lst.args) +    new_lst = List([func] + args[1].args)      return evaluate(new_lst, env)  GLOBALS.register("apply", Builtin(interpretApply, [Arg("func", T.Any, False, True), Arg("list", T.List, False, False)]))  def interpretGlob(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "requires a :string", ev) -    items = glob(ev.value) +    items = glob(args[0].value)      return List([String(item) for item in items], True)  GLOBALS.register("glob", Builtin(interpretGlob, [Arg("regex", T.String, False, False)]))  def interpretShell(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "requires a :string", ev)      # TODO either fail or throw exception (?) on error -    ret = subprocess.run(shlex.split(ev.value), capture_output=True) +    ret = subprocess.run(shlex.split(args[0].value), capture_output=True)      return List([String(r) for r in ret.stdout.decode("utf-8").split("\n")], True)  GLOBALS.register("$", Builtin(interpretShell, [Arg("command", T.String, False, False)]))  def interpretEmpty(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic(symbol, "requires a :list", ev) -    return Bool(len(ev.args) == 0) +    return Bool(len(args[0].args) == 0)  GLOBALS.register("empty?", Builtin(interpretEmpty, [Arg("list", T.List, False, False)]))  def interpretShuf(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic(symbol, "expects a :list", ev) -    items = ev.args[:] +    items = args[0].args[:]      random.shuffle(items)      return List(items, True)  GLOBALS.register("shuf", Builtin(interpretShuf, [Arg("list", T.List, False, False)]))  def interpretIsList(symbol, args, env): -    ev = evaluate(args[0], env) -    return Bool(isinstance(ev, List)) +    return Bool(isinstance(args[0], List))  GLOBALS.register("list?", Builtin(interpretIsList, [Arg("arg", T.Any, False, False)])) @@ -706,9 +591,7 @@ block_arg = Arg("expr", T.Any, False, True)  GLOBALS.register("block", Builtin(interpretBlock, [block_arg], block_arg))  def interpretExit(symbol, args, env): -    status = 0 if len(args) == 0 else evaluate(args[0], env).value -    if not isinstance(status, int): -        raise InterpretPanic(symbol, "expects an :int", status) +    status = 0 if len(args) == 0 else args[0].value      sys.exit(status)      return List([]) @@ -716,10 +599,7 @@ exit_arg = Arg("status", T.Int, True, False)  GLOBALS.register("exit", Builtin(interpretExit, [exit_arg]))  def interpretUnlink(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "expects a :string", ev) -    target_path = Path(ev.value).resolve() +    target_path = Path(args[0].value).resolve()      if not target_path.exists():          raise InterpretPanic(symbol, "target file does not exist", target_path)      target_path.unlink() @@ -736,15 +616,10 @@ def interpretArgv(symbol, args, env):  GLOBALS.register("argv", Builtin(interpretArgv, []))  def interpretIn(symbol, args, env): -    target = evaluate(args[0], env) -    if not isinstance(target, Literal): -        raise InterpretPanic(symbol, "expects a :literal as its first argument", target) -    lst = evaluate(args[1], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "expects a :list as its second argument", lst) +    target = args[0] +    lst = args[1]      for arg in lst.args: -        ev = evaluate(arg, env) -        if type(ev) == type(target) and ev.value == target.value: +        if type(arg) == type(target) and arg.value == target.value:              return Bool(True)      return Bool(False) @@ -753,22 +628,15 @@ in_list_arg = Arg("list", T.List, False, False)  GLOBALS.register("in?", Builtin(interpretIn, [in_target_arg, in_list_arg]))  def interpretLast(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, List): -        raise InterpretPanic("'last' expects a List") -    if len(ev.args) == 0: +    if len(args[0].args) == 0:          raise InterpretPanic("List is empty") -    return evaluate(ev.args[-1], env) +    return evaluate(args[0].args[-1], env)  GLOBALS.register("last", Builtin(interpretLast, [Arg("list", T.List, False, False)]))  def interpretJoin(symbol, args, env): -    lst = evaluate(args[0], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "expects a :list as its first argument", lst) -    target = evaluate(args[1], env) -    if not isinstance(target, String): -        raise InterpretPanic(symbol, "expects a :string as its second argument", target) +    lst = args[0] +    target = args[1]      return String(target.value.join([a.value for a in lst.args]))  join_list_arg = Arg("list", T.List, False, False) @@ -776,11 +644,7 @@ join_string_arg = Arg("joiner", T.String, False, False)  GLOBALS.register("join", Builtin(interpretJoin, [join_list_arg, join_string_arg]))  def interpretWithWrite(symbol, args, env): -    if len(args) == 0: -        raise InterpretPanic(symbol, "expects at least one argument") -    target_file = evaluate(args[0], env) -    if not isinstance(target_file, String): -        raise InterpretPanic(symbol, "expects a :string as its first argument", target_file) +    target_file = args[0]      new_env = Environment(env)      target_path = Path(target_file.value).resolve()      ret = Literal([]) @@ -790,14 +654,12 @@ def interpretWithWrite(symbol, args, env):              ret = evaluate(arg, new_env)      return ret -GLOBALS.register("with-write", Builtin(interpretWithWrite, [Arg("filename", T.String, False, False)])) +GLOBALS.register("with-write", Builtin(interpretWithWrite, [Arg("filename", T.String, False, False)], Arg("exprs", T.Any, False, True)))  def interpretWrite(symbol, args, env):      # write :string :filehandle -    line = evaluate(args[0], env) -    if not isinstance(line, String): -        raise InterpretPanic(symbol, "expects a :string as its first argument", line) -    handle = evaluate(args[1], env) +    line = args[0] +    handle = args[1]      handle.args[0].write(line.value)  # TODO wrong! how do we evaluate a handle?      return Literal([]) @@ -809,43 +671,28 @@ def interpretNewline(symbol, args, env):  GLOBALS.register("newline", Builtin(interpretNewline, []))  def interpretExists(symbol, args, env): -    file_or_dir = evaluate(args[0], env) -    if not isinstance(file_or_dir, String): -        raise InterpretPanic(symbol, "expects a :string", file_or_dir) -    return Bool(Path(file_or_dir.value).resolve().exists()) +    return Bool(Path(args[0].value).resolve().exists())  GLOBALS.register("exists?", Builtin(interpretExists, [Arg("filename", T.String, False, False)]))  def interpretFirstChar(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "expects a :string", ev) -    if len(ev.value) == 0: +    if len(args[0].value) == 0:          raise InterpretPanic(symbol, ":string is empty", ev) -    return String(ev.value[0]) +    return String(args[0].value[0])  GLOBALS.register("first-char", Builtin(interpretFirstChar, [Arg("string", T.String, False, False)]))  def interpretRestChar(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "expects a string", ev) -    return String(ev.value[1:]) +    return String(args[0].value[1:])  GLOBALS.register("rest-char", Builtin(interpretRestChar, [Arg("string", T.String, False, False)]))  def interpretSlice(symbol, args, env): -    lst = evaluate(args[0], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "expects a :list as its first argument", lst) -    idx = evaluate(args[1], env) -    if not isinstance(idx, Int): -        raise InterpretPanic(symbol, "expects an :int as its second argument", idx) +    lst = args[0] +    idx = args[1]      if len(args) == 2:          return List(lst.args[idx.value - 1:]) -    length = evaluate(args[2], env) -    if not isinstance(length, Int): -        raise InterpretPanic(symbol, "expects an :int as its third argument", length) +    length = args[2]      diff = idx.value - 1 + length.value      return List(lst.args[idx.value - 1:diff]) @@ -861,19 +708,14 @@ def interpretClear(symbol, args, env):  GLOBALS.register("clear", Builtin(interpretClear, []))  def interpretInput(symbol, args, env): -    ev = evaluate(args[0], env) -    if not isinstance(ev, String): -        raise InterpretPanic(symbol, "expects a :string", ev) -    ret = input(ev.value) +    ret = input(args[0].value)      return String(ret)  GLOBALS.register("input", Builtin(interpretInput, [Arg("prompt", T.String, False, False)]))  def interpretAppend(symbol, args, env): -    lst = evaluate(args[0], env) -    if not isinstance(lst, List): -        raise InterpretPanic(symbol, "expects a :list as its first argument", lst) -    val = evaluate(args[1], env) +    lst = args[0] +    val = args[1]      items = lst.args[:]      return List(items + [val], True) @@ -914,7 +756,7 @@ def interpretAnsiEscape(symbol, args, env):  GLOBALS.register("ansi-escape", Builtin(interpretAnsiEscape, []))  def interpretUse(symbol, args, env): -    target_file_name = evaluate(args[0], env).value +    target_file_name = args[0].value      target_file = Path(target_file_name).resolve()      if not target_file.exists():          raise InterpretPanic(symbol, "no such file", target_file) | 
