from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, evaluate, interpret, parse, lex, InterpretPanic, TypeWrap, Function, UserType from ..structs import * from pathlib import Path CORE = Environment() def interpretIf(symbol, args, env, ns): if args[0].value: return evaluate(args[1], env, ns) elif len(args) == 3: return evaluate(args[2], env, ns) return List([]) cond = Arg("cond", TypeEnum.BOOL) t_branch = Arg("t-branch", TypeEnum.ANY, lazy=True) f_branch = Arg("f-branch", TypeEnum.ANY, optional=True, lazy=True) CORE.register("if", Builtin("if", interpretIf, [cond, t_branch, f_branch])) 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!! if not isinstance(name, str): 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, lazy=True) def_val_arg = Arg("value", TypeEnum.ANY) CORE.register("def", Builtin("def", interpretDef, [def_name_arg, def_val_arg], return_type=Type(":list"))) 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!! if not env.contains(name): raise InterpretPanic(symbol, "not previously defined", args[0]) env.reregister(name, args[1]) return List([]) CORE.register("redef", Builtin("redef", interpretRedef, [def_name_arg, def_val_arg], return_type=Type(":list"))) def interpretLambda(symbol, args, env, ns): new_args = args return_type = Type(":any") if isinstance(args[0], Type): return_type = args[0] new_args = args[1:] if len(new_args[0].args) != 0: func = UserFunction("", new_args[0].args, new_args[1:]) else: func = UserFunction("", [], new_args[1:]) func.return_type = return_type return func lambda_args_arg = Arg("args", TypeEnum.ANY, lazy=True) lambda_body_arg = Arg("body", TypeEnum.ANY, lazy=True) CORE.register("lambda", Builtin("lambda", interpretLambda, [lambda_args_arg, lambda_body_arg], lambda_body_arg)) def interpretForCount(symbol, args, env, ns): 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, ns) if ret is None: return List([]) return ret for_count_arg = Arg("count", TypeEnum.INT) for_body_arg = Arg("body", TypeEnum.ANY, lazy=True) CORE.register("for-count", Builtin("for-count", interpretForCount, [for_count_arg, for_body_arg], for_body_arg)) def interpretForEach(symbol, args, env, ns): new_env = Environment(env) ret = None for item in args[0].args: new_env.register("_item_", evaluate(item, env, ns)) for arg in args[1:]: ret = evaluate(arg, new_env, ns) if ret is None: return List([]) return ret for_each_arg = Arg("list", TypeEnum.LIST) CORE.register("for-each", Builtin("for-each", interpretForEach, [for_each_arg, for_body_arg], for_body_arg)) 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, 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, ns) return List([]) CORE.register("branch", Builtin("branch", interpretBranch, [for_body_arg], for_body_arg)) def interpretFunc(symbol, args, env, ns): 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, ns) # add the name to the function func.name = name env.register(name, func) return List([]) CORE.register("func", Builtin("func", interpretFunc, [def_name_arg, lambda_args_arg, lambda_body_arg], lambda_body_arg, Type(":list"))) def interpretBlock(symbol, args, env, ns): new_env = Environment(env) ret = List([]) for arg in args: ret = evaluate(arg, new_env, ns) return ret block_arg = Arg("expr", TypeEnum.ANY, lazy=True) CORE.register("block", Builtin("block", interpretBlock, [block_arg], block_arg)) def interpretWhile(symbol, args, env, ns): new_env = Environment(env) cond = args[0] ret = List([]) while True: ev = evaluate(cond, new_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, new_env, ns) return ret CORE.register("while", Builtin("while", interpretWhile, [Arg("cond", TypeEnum.BOOL, lazy=True)], Arg("expr", TypeEnum.ANY, lazy=True))) def interpretUse(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)), env, ns) return List([]) CORE.register("use", Builtin("use", interpretUse, [Arg("filename", TypeEnum.STRING)], return_type=Type(":list"))) def interpretAssert(symbol, args, env, ns): if args[0].value != True: raise InterpretPanic(symbol, "assertion failed") return List([]) CORE.register("assert", Builtin("assert", interpretAssert, [Arg("cond", TypeEnum.BOOL)], return_type=Type(":list"))) 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)), env, args[1].name) return List([]) CORE.register("use-as", Builtin("use-as", interpretUseAs, [Arg("filename", TypeEnum.STRING), Arg("namespace", TypeEnum.ANY, lazy=True)], return_type=Type(":list"))) def interpretQuote(symbol, args, env, ns): return args[0] quote_arg = Arg("arg", TypeEnum.ANY, lazy=True) CORE.register("quote", Builtin("quote", interpretQuote, [quote_arg])) def interpretEval(symbol, args, env, ns): return evaluate(args[0], env, ns) # TODO why do i have to explicitly evaluate here? eval_arg = Arg("arg", TypeEnum.ANY) CORE.register("eval", Builtin("eval", interpretEval, [eval_arg])) def interpretType(symbol, args, env, ns): # (type typename parent func) if not isinstance(args[0], Type): raise InterpretPanic(symbol, "types must begin with a colon") name = args[0].name # NOTE: we are not evaluating the name!! # TODO we may need to do namespace things here # also, we probably shouldn't be able to rename types if not isinstance(args[1], TypeWrap): raise InterpretPanic(symbol, "parent must be a valid type", args[1]) elif not env.contains(args[1].name): raise InterpretPanic(symbol, f"no such type {args[1]}") parent = env.get(args[1].name) func = args[2] if not isinstance(func, Function): raise InterpretPanic(symbol, "validation must be a :func", func) new_type = UserType(name, parent, func) env.register(name, new_type) return List([]) type_name_arg = Arg("name", TypeEnum.ANY, lazy=True) type_parent_arg = Arg("name", TypeEnum.ANY) type_func_arg = Arg("func", TypeEnum.ANY) CORE.register("type", Builtin("type", interpretType, [type_name_arg, type_parent_arg, type_func_arg]))