aboutsummaryrefslogtreecommitdiff
path: root/neb/std/core.py
diff options
context:
space:
mode:
authormryouse2022-06-21 01:49:29 +0000
committermryouse2022-06-21 01:49:29 +0000
commit776fe3193b515c028b5ac69326baed51d760d32f (patch)
treedb111c3fe7a20143b2f058259f86dbbecae4cbd6 /neb/std/core.py
parentb1550660adaca68bb38541aed371e36b7000e124 (diff)
refactor: break stdlib into several files
Diffstat (limited to 'neb/std/core.py')
-rw-r--r--neb/std/core.py184
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]))