From 9af564b2725d7112493974dec968ceb2aa2539fd Mon Sep 17 00:00:00 2001 From: mryouse Date: Sun, 12 Jun 2022 03:58:24 +0000 Subject: initial commit of namespace support with 'use-as' --- interpreter.py | 226 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 129 insertions(+), 97 deletions(-) (limited to 'interpreter.py') diff --git a/interpreter.py b/interpreter.py index 80c6a50..869f424 100644 --- a/interpreter.py +++ b/interpreter.py @@ -59,7 +59,7 @@ class Function: raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(params)}") return True - def evaluate_args(self, symbol, params, env): + def evaluate_args(self, symbol, params, env, ns): self.arity_check(symbol, params) ret = [] @@ -87,10 +87,10 @@ class Builtin(Function): def __init__(self, callable_, args=None, many=None): super().__init__("", None, callable_, args, many) - def call(self, expr, env): + def call(self, expr, env, ns): self.arity_check(expr.args[0], expr.args[1:]) - evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env) - return self.body(expr.args[0], evaluated_args, env) + evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env, ns) + return self.body(expr.args[0], evaluated_args, env, ns) class UserFunction(Function): @@ -100,13 +100,14 @@ class UserFunction(Function): args = [Arg(p.name, TypeEnum.ANY, False, False) for p in params] super().__init__(name, params, body, args) - def call(self, expr, env): + def call(self, expr, env, ns): self.arity_check(expr.args[0], expr.args[1:]) - evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env) + evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env, ns) this_env = Environment(env) for idx, param in enumerate(self.params): this_env.register(param.name, evaluated_args[idx]) - return interpret(self.body, this_env) + #return interpret(self.body, this_env, ns) + return interpret(self.body, env=this_env, ns=ns) class Environment: @@ -149,20 +150,32 @@ class Environment: GLOBALS = Environment() -def interpret(exprs, env=GLOBALS): +def interpret(exprs, *, env=GLOBALS, ns=None): ret = None for expr in exprs: - ret = evaluate(expr, env) + ret = evaluate(expr, env, ns) return ret -def evaluate(expr, env): +def evaluate(expr, env, ns=None): if isinstance(expr, Literal) or isinstance(expr, Function): #return expr.value return expr elif isinstance(expr, Symbol): + if env.contains(expr.name): + return evaluate(env.get(expr.name), env, ns) + elif ns is not None and env.contains(f"{ns}/{expr.name}"): + return evaluate(env.get(f"{ns}/{expr.name}"), env, ns) + else: + raise NebPanic(f"no such symbol: {expr}") + + ''' if not env.contains(expr.name): raise NebPanic(f"no such symbol: {expr}") - return evaluate(env.get(expr.name), env) + if ns is None: + return evaluate(env.get(expr.name), env, ns) + else: + return evaluate(env.get(f"{ns}/{expr.name}"), env, ns) + ''' # if it's a literal list, return it if expr.data: @@ -174,16 +187,19 @@ def evaluate(expr, env): if not isinstance(expr.args[0], Symbol): raise NebPanic("can't evaluate without a symbol") name = expr.args[0].name - + #if ns is not None: + # name = f"{ns}/{name}" if env.contains(name): - return env.get(name).call(expr, env) + return env.get(name).call(expr, env, ns) + elif ns is not None and env.contains(f"{ns}/{name}"): + return env.get(f"{ns}/{name}").call(expr, env, ns) else: raise InterpretPanic(expr.args[0], "unable to evaluate") -def interpretOr(symbol, args, env): +def interpretOr(symbol, args, env, ns): # or returns true for the first expression that returns true for arg in args: - ev = evaluate(arg, env) + ev = evaluate(arg, env, ns) if not isinstance(ev, Bool): raise InterpretPanic(symbol, "requires :bool arguments") if ev.value == True: @@ -194,10 +210,10 @@ def interpretOr(symbol, args, env): or_arg = Arg("arg", TypeEnum.BOOL, False, True) GLOBALS.register("or", Builtin(interpretOr, [or_arg, or_arg], or_arg)) -def interpretAnd(symbol, args, env): +def interpretAnd(symbol, args, env, ns): # and returns false for the first expression that returns false for arg in args: - ev = evaluate(arg, env) + ev = evaluate(arg, env, ns) if not isinstance(ev, Bool): raise InterpretPanic(symbol, "requires :bool arguments") if ev.value == False: @@ -206,7 +222,7 @@ def interpretAnd(symbol, args, env): GLOBALS.register("and", Builtin(interpretAnd, [or_arg, or_arg], or_arg)) -def interpretEq(symbol, args, env): +def interpretEq(symbol, args, env, ns): # NOTE this currently only works for literals # compare types because 0 != #false in neb if type(args[0]) == type(args[1]) and args[0].value == args[1].value: @@ -217,28 +233,28 @@ def interpretEq(symbol, args, env): eq_arg = Arg("value", TypeEnum.LITERAL, False, False) GLOBALS.register("eq?", Builtin(interpretEq, [eq_arg, eq_arg])) -def interpretGreaterThan(symbol, args, env): +def interpretGreaterThan(symbol, args, env, ns): return Bool(args[0].value > args[1].value) compare_arg = Arg("num", TypeEnum.NUMBER, False, False) GLOBALS.register(">", Builtin(interpretGreaterThan, [compare_arg, compare_arg])) -def interpretGreaterThanEqual(symbol, args, env): +def interpretGreaterThanEqual(symbol, args, env, ns): return Bool(args[0].value >= args[1].value) GLOBALS.register(">=", Builtin(interpretGreaterThanEqual, [compare_arg, compare_arg])) -def interpretLessThan(symbol, args, env): +def interpretLessThan(symbol, args, env, ns): return Bool(args[0].value < args[1].value) GLOBALS.register("<", Builtin(interpretLessThan, [compare_arg, compare_arg])) -def interpretLessThanEqual(symbol, args, env): +def interpretLessThanEqual(symbol, args, env, ns): return Bool(args[0].value <= args[1].value) GLOBALS.register("<=", Builtin(interpretLessThanEqual, [compare_arg, compare_arg])) -def interpretAddition(symbol, args, env): +def interpretAddition(symbol, args, env, ns): res = 0 for arg in args: res += arg.value @@ -250,7 +266,7 @@ def interpretAddition(symbol, args, env): term_arg = Arg("term", TypeEnum.NUMBER, False, False) GLOBALS.register("+", Builtin(interpretAddition, [term_arg], term_arg)) -def interpretSubtraction(symbol, args, env): +def interpretSubtraction(symbol, args, env, ns): if len(args) == 1: res = -args[0].value else: @@ -264,7 +280,7 @@ def interpretSubtraction(symbol, args, env): GLOBALS.register("-", Builtin(interpretSubtraction, [term_arg], term_arg)) -def interpretMultiplication(symbol, args, env): +def interpretMultiplication(symbol, args, env, ns): res = args[0].value for arg in args[1:]: res = res * arg.value @@ -276,7 +292,7 @@ def interpretMultiplication(symbol, args, env): factor_arg = Arg("factor", TypeEnum.NUMBER, False, False) GLOBALS.register("*", Builtin(interpretMultiplication, [factor_arg, factor_arg], factor_arg)) -def interpretDivision(symbol, args, env): +def interpretDivision(symbol, args, env, ns): ret = args[0].value / args[1].value if int(ret) == ret: return Int(int(ret)) @@ -285,18 +301,18 @@ def interpretDivision(symbol, args, env): GLOBALS.register("/", Builtin(interpretDivision, [factor_arg, factor_arg])) -def interpretNot(symbol, args, env): +def interpretNot(symbol, args, env, ns): return Bool(not args[0].value) not_arg = Arg("not", TypeEnum.BOOL, False, False) GLOBALS.register("not", Builtin(interpretNot, [not_arg])) -def interpretIf(symbol, args, env): +def interpretIf(symbol, args, env, ns): # if cond t-branch [f-branch] if args[0].value: - return evaluate(args[1], env) + return evaluate(args[1], env, ns) elif len(args) == 3: - return evaluate(args[2], env) + return evaluate(args[2], env, ns) return List([]) cond = Arg("cond", TypeEnum.BOOL, False, False) @@ -304,13 +320,14 @@ t_branch = Arg("t-branch", TypeEnum.ANY, False, True) f_branch = Arg("f-branch", TypeEnum.ANY, True, True) GLOBALS.register("if", Builtin(interpretIf, [cond, t_branch, f_branch])) -def interpretPrint(symbol, args, env): +def interpretPrint(symbol, args, env, ns): print(args[0].value) return List([]) # print returns nothing GLOBALS.register("print", Builtin(interpretPrint, [Arg("arg", TypeEnum.STRING, False, False)])) -def interpretDef(symbol, args, env): +def interpretDef(symbol, args, env, ns): + if not isinstance(args[0], Symbol): raise InterpretPanic(symbol, "requires a :string name", args[0]) name = args[0].name # NOTE: we are not evaluating the name!! @@ -318,13 +335,14 @@ def interpretDef(symbol, args, env): raise InterpretPanic(symbol, "requires a :string name") env.register(name, args[1]) # TODO since this isn't lazily evaluated, side effects are allowed (bad!) + return List([]) def_name_arg = Arg("name", TypeEnum.ANY, False, True) def_val_arg = Arg("value", TypeEnum.ANY, False, False) GLOBALS.register("def", Builtin(interpretDef, [def_name_arg, def_val_arg])) -def interpretRedef(symbol, args, env): +def interpretRedef(symbol, args, env, ns): if not isinstance(args[0], Symbol): raise InterpretPanic(symbol, "requires a :string name", args[0]) name = args[0].name # NOTE: we are not evaluating the name!! @@ -336,7 +354,7 @@ def interpretRedef(symbol, args, env): GLOBALS.register("redef", Builtin(interpretRedef, [def_name_arg, def_val_arg])) -def interpretLambda(symbol, args, env): +def interpretLambda(symbol, args, env, ns): if len(args[0].args) != 0: func = UserFunction("", args[0].args, args[1:]) else: @@ -347,7 +365,7 @@ lambda_args_arg = Arg("args", TypeEnum.ANY, False, True) lambda_body_arg = Arg("body", TypeEnum.ANY, False, True) GLOBALS.register("lambda", Builtin(interpretLambda, [lambda_args_arg, lambda_body_arg], lambda_body_arg)) -def interpretToString(symbol, args, env): +def interpretToString(symbol, args, env, ns): item = args[0] if isinstance(item, String): return item @@ -358,7 +376,7 @@ def interpretToString(symbol, args, env): GLOBALS.register("->string", Builtin(interpretToString, [Arg("arg", TypeEnum.ANY, False, False)])) -def interpretConcat(symbol, args, env): +def interpretConcat(symbol, args, env, ns): # concat str1 str2...strN out = "" for arg in args: @@ -368,14 +386,14 @@ def interpretConcat(symbol, args, env): string_arg = Arg("arg", TypeEnum.STRING, False, False) GLOBALS.register("concat", Builtin(interpretConcat, [string_arg, string_arg], string_arg)) -def interpretForCount(symbol, args, env): +def interpretForCount(symbol, args, env, ns): # for-count int exprs new_env = Environment(env) ret = None 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) + ret = evaluate(arg, new_env, ns) if ret is None: return List([]) return ret @@ -384,14 +402,14 @@ for_count_arg = Arg("count", TypeEnum.INT, False, False) for_body_arg = Arg("body", TypeEnum.ANY, False, True) GLOBALS.register("for-count", Builtin(interpretForCount, [for_count_arg, for_body_arg], for_body_arg)) -def interpretForEach(symbol, args, env): +def interpretForEach(symbol, args, env, ns): # for-each list exprs new_env = Environment(env) ret = None for item in args[0].args: - new_env.register("_item_", evaluate(item, env)) + new_env.register("_item_", evaluate(item, env, ns)) for arg in args[1:]: - ret = evaluate(arg, new_env) + ret = evaluate(arg, new_env, ns) if ret is None: return List([]) return ret @@ -399,13 +417,13 @@ def interpretForEach(symbol, args, env): for_each_arg = Arg("list", TypeEnum.LIST, False, False) GLOBALS.register("for-each", Builtin(interpretForEach, [for_each_arg, for_body_arg], for_body_arg)) -def interpretPipe(symbol, args, env): +def interpretPipe(symbol, args, env, ns): new_env = Environment(env) pipe = None for arg in args: if pipe is not None: new_env.register("items", pipe) - pipe = evaluate(arg, new_env) + pipe = evaluate(arg, new_env, ns) if pipe is None: return List([]) return pipe @@ -413,34 +431,35 @@ def interpretPipe(symbol, args, env): # TODO GLOBALS.register("|", Builtin(interpretPipe, 2)) -def interpretBranch(symbol, args, env): +def interpretBranch(symbol, args, env, ns): for arg in args: if len(arg.args) != 2: raise InterpretPanic(symbol, "each branch requires two expressions") - cond = evaluate(arg.args[0], env) # this is the condition + cond = evaluate(arg.args[0], env, ns) # this is the condition if not isinstance(cond, Bool): raise InterpretPanic(symbol, "branch condition must be :bool", cond) if cond.value: - return evaluate(arg.args[1], env) + return evaluate(arg.args[1], env, ns) return List([]) GLOBALS.register("branch", Builtin(interpretBranch, [for_body_arg], for_body_arg)) -def interpretFunc(symbol, args, env): +def interpretFunc(symbol, args, env, ns): # func (args) (exprs) # maybe: # arg [:type] -> type is optional # ?arg default -> 'arg' is optional, defaulted # *arg [:type] -> 'arg' is a list containing the remaining args - - if not isinstance(args[0], Symbol): raise InterpretPanic(symbol, "requires a :string name") name = args[0].name # NOTE: we are not evaluating the name!! + if ns is not None: + name = f"{ns}/{name}" + # compose a lambda - func = interpretLambda(None, args[1:], env) + func = interpretLambda(None, args[1:], env, ns) env.register(name, func) return List([]) @@ -449,7 +468,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): +def interpretReadLines(symbol, args, env, ns): target_file_name = args[0].value target_file = Path(target_file_name).resolve() if not target_file.exists(): @@ -462,13 +481,13 @@ def interpretReadLines(symbol, args, env): GLOBALS.register("read-lines", Builtin(interpretReadLines, [Arg("filename", TypeEnum.STRING, False, False)])) # - strip whitespace from string -def interpretStrip(symbol, args, env): +def interpretStrip(symbol, args, env, ns): return String(args[0].value.strip()) GLOBALS.register("strip", Builtin(interpretStrip, [Arg("filename", TypeEnum.STRING, False, False)])) # - string->int and string->float -def interpretStringToInt(symbol, args, env): +def interpretStringToInt(symbol, args, env, ns): try: val = int(args[0].value) return Int(val) @@ -478,7 +497,7 @@ def interpretStringToInt(symbol, args, env): GLOBALS.register("string->int", Builtin(interpretStringToInt, [Arg("arg", TypeEnum.STRING, False, False)])) # - split a string by a given field -def interpretSplit(symbol, args, env): +def interpretSplit(symbol, args, env, ns): target = args[0] if len(args) == 1: return List([String(char) for char in target.value], True) @@ -489,20 +508,20 @@ def interpretSplit(symbol, args, env): GLOBALS.register("split", Builtin(interpretSplit, [Arg("target", TypeEnum.STRING, False, False)], Arg("splitter", TypeEnum.STRING, True, False))) # - get the length of a list -def interpretListLength(symbol, args, env): +def interpretListLength(symbol, args, env, ns): return Int(len(args[0].args)) GLOBALS.register("list-length", Builtin(interpretListLength, [Arg("arg", TypeEnum.LIST, False, False)])) # - first/rest of list -def interpretFirst(symbol, args, env): +def interpretFirst(symbol, args, env, ns): if len(args[0].args) == 0: raise InterpretPanic(symbol, "list is empty") - return evaluate(args[0].args[0], env) + return evaluate(args[0].args[0], env, ns) GLOBALS.register("first", Builtin(interpretFirst, [Arg("arg", TypeEnum.LIST, False, False)])) -def interpretRest(symbol, args, env): +def interpretRest(symbol, args, env, ns): # TODO do we know it's not evaluated? return List(args[0].args[1:], True) # we don't evaluate the remainder of the list @@ -510,23 +529,23 @@ GLOBALS.register("rest", Builtin(interpretRest, [Arg("arg", TypeEnum.LIST, False # - iterate over list # - map -def interpretMap(symbol, args, env): +def interpretMap(symbol, args, env, ns): # 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) + lst = evaluate(args[1], env, ns) if not isinstance(lst, List): raise InterpretPanic(symbol, "requires a :list as its second argument", lst) out = [] for arg in lst.args: - ev = evaluate(List([func, arg]), env) + ev = evaluate(List([func, arg]), env, ns) out.append(ev) return List(out, True) GLOBALS.register("map", Builtin(interpretMap, [Arg("func", TypeEnum.ANY, False, True), Arg("list", TypeEnum.LIST, False, False)])) -def interpretZip(symbol, args, env): +def interpretZip(symbol, args, env, ns): z1 = args[0] z2 = args[1] if len(z1.args) != len(z2.args): @@ -541,68 +560,68 @@ def interpretZip(symbol, args, env): zip_arg = Arg("list", TypeEnum.LIST, False, False) GLOBALS.register("zip", Builtin(interpretZip, [zip_arg, zip_arg])) -def interpretList(symbol, args, env): +def interpretList(symbol, args, env, ns): return List(args, True) GLOBALS.register("list", Builtin(interpretList, [], Arg("item", TypeEnum.ANY, False, False))) -def interpretListReverse(symbol, args, env): +def interpretListReverse(symbol, args, env, ns): 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", TypeEnum.LIST, False, False)])) -def interpretApply(symbol, args, env): +def interpretApply(symbol, args, env, ns): # 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) new_lst = List([func] + args[1].args) - return evaluate(new_lst, env) + return evaluate(new_lst, env, ns) GLOBALS.register("apply", Builtin(interpretApply, [Arg("func", TypeEnum.ANY, False, True), Arg("list", TypeEnum.LIST, False, False)])) -def interpretGlob(symbol, args, env): +def interpretGlob(symbol, args, env, ns): items = glob(args[0].value) return List([String(item) for item in items], True) GLOBALS.register("glob", Builtin(interpretGlob, [Arg("regex", TypeEnum.STRING, False, False)])) -def interpretShell(symbol, args, env): +def interpretShell(symbol, args, env, ns): # TODO either fail or throw exception (?) on error 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", TypeEnum.STRING, False, False)])) -def interpretEmpty(symbol, args, env): +def interpretEmpty(symbol, args, env, ns): return Bool(len(args[0].args) == 0) GLOBALS.register("empty?", Builtin(interpretEmpty, [Arg("list", TypeEnum.LIST, False, False)])) -def interpretShuf(symbol, args, env): +def interpretShuf(symbol, args, env, ns): items = args[0].args[:] random.shuffle(items) return List(items, True) GLOBALS.register("shuf", Builtin(interpretShuf, [Arg("list", TypeEnum.LIST, False, False)])) -def interpretIsList(symbol, args, env): +def interpretIsList(symbol, args, env, ns): return Bool(isinstance(args[0], List)) GLOBALS.register("list?", Builtin(interpretIsList, [Arg("arg", TypeEnum.ANY, False, False)])) -def interpretBlock(symbol, args, env): +def interpretBlock(symbol, args, env, ns): ret = List([]) for arg in args: - ret = evaluate(arg, env) + ret = evaluate(arg, env, ns) return ret block_arg = Arg("expr", TypeEnum.ANY, False, True) GLOBALS.register("block", Builtin(interpretBlock, [block_arg], block_arg)) -def interpretExit(symbol, args, env): +def interpretExit(symbol, args, env, ns): status = 0 if len(args) == 0 else args[0].value sys.exit(status) return List([]) @@ -610,7 +629,7 @@ def interpretExit(symbol, args, env): exit_arg = Arg("status", TypeEnum.INT, True, False) GLOBALS.register("exit", Builtin(interpretExit, [exit_arg])) -def interpretUnlink(symbol, args, env): +def interpretUnlink(symbol, args, env, ns): target_path = Path(args[0].value).resolve() if not target_path.exists(): raise InterpretPanic(symbol, "target file does not exist", target_path) @@ -619,7 +638,7 @@ def interpretUnlink(symbol, args, env): GLOBALS.register("unlink", Builtin(interpretUnlink, [Arg("filename", TypeEnum.STRING, False, False)])) -def interpretArgv(symbol, args, env): +def interpretArgv(symbol, args, env, ns): out = [] for arg in sys.argv[1:]: out.append(String(arg)) @@ -627,7 +646,7 @@ def interpretArgv(symbol, args, env): GLOBALS.register("argv", Builtin(interpretArgv, [])) -def interpretIn(symbol, args, env): +def interpretIn(symbol, args, env, ns): target = args[0] lst = args[1] for arg in lst.args: @@ -639,14 +658,14 @@ in_target_arg = Arg("target", TypeEnum.LITERAL, False, False) in_list_arg = Arg("list", TypeEnum.LIST, False, False) GLOBALS.register("in?", Builtin(interpretIn, [in_target_arg, in_list_arg])) -def interpretLast(symbol, args, env): +def interpretLast(symbol, args, env, ns): if len(args[0].args) == 0: raise InterpretPanic("List is empty") - return evaluate(args[0].args[-1], env) + return evaluate(args[0].args[-1], env, ns) GLOBALS.register("last", Builtin(interpretLast, [Arg("list", TypeEnum.LIST, False, False)])) -def interpretJoin(symbol, args, env): +def interpretJoin(symbol, args, env, ns): lst = args[0] target = args[1] return String(target.value.join([a.value for a in lst.args])) @@ -655,7 +674,7 @@ join_list_arg = Arg("list", TypeEnum.LIST, False, False) join_string_arg = Arg("joiner", TypeEnum.STRING, False, False) GLOBALS.register("join", Builtin(interpretJoin, [join_list_arg, join_string_arg])) -def interpretWithWrite(symbol, args, env): +def interpretWithWrite(symbol, args, env, ns): target_file = args[0] new_env = Environment(env) target_path = Path(target_file.value).resolve() @@ -663,12 +682,12 @@ def interpretWithWrite(symbol, args, env): with open(str(target_path), "w") as fil: new_env.register("_file_", List([fil], True)) # TODO wrong! for arg in args[1:]: - ret = evaluate(arg, new_env) + ret = evaluate(arg, new_env, ns) return ret GLOBALS.register("with-write", Builtin(interpretWithWrite, [Arg("filename", TypeEnum.STRING, False, False)], Arg("exprs", TypeEnum.ANY, False, True))) -def interpretWrite(symbol, args, env): +def interpretWrite(symbol, args, env, ns): # write :string :filehandle line = args[0] handle = args[1] @@ -677,29 +696,29 @@ def interpretWrite(symbol, args, env): GLOBALS.register("write", Builtin(interpretWrite, [Arg("string", TypeEnum.STRING, False, False), Arg("filename", TypeEnum.LIST, False, False)])) -def interpretNewline(symbol, args, env): +def interpretNewline(symbol, args, env, ns): return String("\n") GLOBALS.register("newline", Builtin(interpretNewline, [])) -def interpretExists(symbol, args, env): +def interpretExists(symbol, args, env, ns): return Bool(Path(args[0].value).resolve().exists()) GLOBALS.register("exists?", Builtin(interpretExists, [Arg("filename", TypeEnum.STRING, False, False)])) -def interpretFirstChar(symbol, args, env): +def interpretFirstChar(symbol, args, env, ns): if len(args[0].value) == 0: raise InterpretPanic(symbol, ":string is empty", ev) return String(args[0].value[0]) GLOBALS.register("first-char", Builtin(interpretFirstChar, [Arg("string", TypeEnum.STRING, False, False)])) -def interpretRestChar(symbol, args, env): +def interpretRestChar(symbol, args, env, ns): return String(args[0].value[1:]) GLOBALS.register("rest-char", Builtin(interpretRestChar, [Arg("string", TypeEnum.STRING, False, False)])) -def interpretSlice(symbol, args, env): +def interpretSlice(symbol, args, env, ns): lst = args[0] idx = args[1] if len(args) == 2: @@ -713,19 +732,19 @@ slice_idx_arg = Arg("idx", TypeEnum.INT, False, False) slice_length_arg = Arg("length", TypeEnum.INT, True, False) GLOBALS.register("slice", Builtin(interpretSlice, [slice_list_arg, slice_idx_arg, slice_length_arg])) -def interpretClear(symbol, args, env): +def interpretClear(symbol, args, env, ns): subprocess.run(["clear"]) return List([]) GLOBALS.register("clear", Builtin(interpretClear, [])) -def interpretReadLine(symbol, args, env): +def interpretReadLine(symbol, args, env, ns): ret = input(args[0].value) return String(ret) GLOBALS.register("read-line", Builtin(interpretReadLine, [Arg("prompt", TypeEnum.STRING, False, False)])) -def interpretReadChar(symbol, args, env): +def interpretReadChar(symbol, args, env, ns): import termios, tty fd = sys.stdin.fileno() old = termios.tcgetattr(fd) @@ -740,7 +759,7 @@ def interpretReadChar(symbol, args, env): GLOBALS.register("read-char", Builtin(interpretReadChar, [])) -def interpretAppend(symbol, args, env): +def interpretAppend(symbol, args, env, ns): lst = args[0] val = args[1] items = lst.args[:] @@ -749,7 +768,7 @@ def interpretAppend(symbol, args, env): GLOBALS.register("append", Builtin(interpretAppend, [Arg("list", TypeEnum.LIST, False, False), Arg("item", TypeEnum.ANY, False, False)])) # TODO: this is actually for records/structs/whatever they're called -def interpretRemove(symbol, args, env): +def interpretRemove(symbol, args, env, ns): lst = args[0] key = args[1] out = [] @@ -760,22 +779,22 @@ def interpretRemove(symbol, args, env): GLOBALS.register("remove", Builtin(interpretRemove, [Arg("list", TypeEnum.LIST, False, False), Arg("key", TypeEnum.ANY, False, False)])) -def interpretWhile(symbol, args, env): +def interpretWhile(symbol, args, env, ns): cond = args[0] ret = List([]) while True: - ev = evaluate(cond, env) + ev = evaluate(cond, env, ns) if not isinstance(ev, Bool): raise InterpretPanic(symbol, "expects a :bool condition", ev) if not ev.value: break for arg in args[1:]: - ret = evaluate(arg, env) + ret = evaluate(arg, env, ns) return ret GLOBALS.register("while", Builtin(interpretWhile, [Arg("cond", TypeEnum.BOOL, False, True)], Arg("expr", TypeEnum.ANY, False, True))) -def interpretUse(symbol, args, env): +def interpretUse(symbol, args, env, ns): target_file_name = args[0].value target_file = Path(target_file_name).resolve() if not target_file.exists(): @@ -817,3 +836,16 @@ def interpretSymbols(symbol, args, env): return List([]) GLOBALS.register("symbols", Builtin(interpretSymbols, [])) + +def interpretUseAs(symbol, args, env, ns): + 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) + with open(target_file, "r") as fil: + data = fil.read() + interpret(parse(lex(data)), ns=args[1].name) + return List([]) + +# TODO takes a symbol as its second, that may be wrong +GLOBALS.register("use-as", Builtin(interpretUseAs, [Arg("filename", T.String, False, False), Arg("namespace", T.Any, False, True)])) -- cgit v1.2.3