diff options
Diffstat (limited to 'neb/std/core.py')
| -rw-r--r-- | neb/std/core.py | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/neb/std/core.py b/neb/std/core.py new file mode 100644 index 0000000..d481daa --- /dev/null +++ b/neb/std/core.py @@ -0,0 +1,184 @@ +from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, evaluate, interpret, parse, lex +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(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(interpretDef, [def_name_arg, def_val_arg])) + +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(interpretRedef, [def_name_arg, def_val_arg])) + +def interpretLambda(symbol, args, env, ns): + if len(args[0].args) != 0: + func = UserFunction("<lambda>", args[0].args, args[1:]) + else: + func = UserFunction("<lambda>", [], args[1:]) + return func + +lambda_args_arg = Arg("args", TypeEnum.ANY, lazy=True) +lambda_body_arg = Arg("body", TypeEnum.ANY, lazy=True) +CORE.register("lambda", Builtin(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(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(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(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(interpretFunc, [def_name_arg, lambda_args_arg, lambda_body_arg], lambda_body_arg)) + +def interpretBlock(symbol, args, env, ns): + ret = List([]) + for arg in args: + ret = evaluate(arg, env, ns) + return ret + +block_arg = Arg("expr", TypeEnum.ANY, lazy=True) +CORE.register("block", Builtin(interpretBlock, [block_arg], block_arg)) + +def interpretWhile(symbol, args, env, ns): + cond = args[0] + ret = List([]) + while True: + 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, ns) + return ret + +CORE.register("while", Builtin(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(interpretUse, [Arg("filename", TypeEnum.STRING)])) + +def interpretAssert(symbol, args, env, ns): + if args[0].value != True: + raise InterpretPanic(symbol, "assertion failed") + return List([]) + +CORE.register("assert", Builtin(interpretAssert, [Arg("cond", TypeEnum.BOOL)])) + +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([]) + +CORE.register("use-as", Builtin(interpretUseAs, [Arg("filename", TypeEnum.STRING), Arg("namespace", TypeEnum.ANY, lazy=True)])) + +def interpretQuote(symbol, args, env, ns): + return args[0] + +quote_arg = Arg("arg", TypeEnum.ANY, lazy=True) +CORE.register("quote", Builtin(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(interpretEval, [eval_arg])) |
