diff options
| author | mryouse | 2022-07-09 02:39:27 +0000 |
|---|---|---|
| committer | mryouse | 2022-07-09 02:39:27 +0000 |
| commit | 4038aa87fddbe7e79c61603cf8d4514ebb47f3fe (patch) | |
| tree | 8f5493f5fb8d9f9ea1de4cd1ade03b7d6c9447d9 /neb/std | |
| parent | 7ffeef0faef3fbc389069df853109afc76260f0d (diff) | |
| parent | 4fb8873f45c8596ba044c87060191778b8238952 (diff) | |
Merge branch 'master' into feature/listtypes
Diffstat (limited to 'neb/std')
| -rw-r--r-- | neb/std/boolean.py | 25 | ||||
| -rw-r--r-- | neb/std/core.py | 172 | ||||
| -rw-r--r-- | neb/std/fs.py | 13 | ||||
| -rw-r--r-- | neb/std/functools.py | 10 | ||||
| -rw-r--r-- | neb/std/repl.py | 28 | ||||
| -rw-r--r-- | neb/std/sys.py | 9 |
6 files changed, 145 insertions, 112 deletions
diff --git a/neb/std/boolean.py b/neb/std/boolean.py index 973fa67..e716a87 100644 --- a/neb/std/boolean.py +++ b/neb/std/boolean.py @@ -3,31 +3,6 @@ from ..structs import * BOOLEAN = Environment() -def interpretOr(symbol, args, env, ns): - # or returns true for the first expression that returns true - for arg in args: - ev = evaluate(arg, env, ns) - if not isinstance(ev, Bool): - raise InterpretPanic(symbol, "requires :bool arguments") - if ev.value == True: - return ev - return Bool(False) - -or_arg = Arg("arg", TypeEnum.BOOL, lazy=True) -BOOLEAN.register("or", Builtin("or", interpretOr, [or_arg, or_arg], or_arg, Type(":bool"))) - -def interpretAnd(symbol, args, env, ns): - # and returns false for the first expression that returns false - for arg in args: - ev = evaluate(arg, env, ns) - if not isinstance(ev, Bool): - raise InterpretPanic(symbol, "requires :bool arguments") - if ev.value == False: - return ev - return Bool(True) - -BOOLEAN.register("and", Builtin("and", interpretAnd, [or_arg, or_arg], or_arg, Type(":bool"))) - def interpretEq(symbol, args, env, ns): # NOTE this currently only works for literals # compare types because 0 != #false in neb diff --git a/neb/std/core.py b/neb/std/core.py index a44417c..9e2e743 100644 --- a/neb/std/core.py +++ b/neb/std/core.py @@ -1,48 +1,49 @@ -from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, evaluate, interpret, parse, lex, InterpretPanic, TypeWrap, Function, UserType +from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, evaluate, interpret, parse, lex, InterpretPanic, TypeWrap, Function, UserType, NebSyntax from ..structs import * from pathlib import Path CORE = Environment() def interpretIf(symbol, args, env, ns): - if args[0].value: + cond = evaluate(args[0], env, ns) + if not isinstance(cond, Bool): + raise InterpretPanic(symbol, "requires a :bool condition", cond) + + if cond.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])) +t_branch = Arg("t-branch", TypeEnum.ANY) +f_branch = Arg("f-branch", TypeEnum.ANY, optional=True) +CORE.register("if", NebSyntax("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]) + raise InterpretPanic(symbol, "requires a :symbol", 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!) - + res = evaluate(args[1], env, ns) + env.register(name, res) return List([]) -def_name_arg = Arg("name", TypeEnum.ANY, lazy=True) +def_name_arg = Arg("name", TypeEnum.ANY) def_val_arg = Arg("value", TypeEnum.ANY) -CORE.register("def", Builtin("def", interpretDef, [def_name_arg, def_val_arg], return_type=Type(":list"))) +CORE.register("def", NebSyntax("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]) + raise InterpretPanic(symbol, "requires a :symbol", 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]) + res = evaluate(args[1], env, ns) + env.reregister(name, res) return List([]) -CORE.register("redef", Builtin("redef", interpretRedef, [def_name_arg, def_val_arg], return_type=Type(":list"))) +CORE.register("redef", NebSyntax("redef", interpretRedef, [def_name_arg, def_val_arg], return_type=Type(":list"))) def interpretLambda(symbol, args, env, ns): new_args = args @@ -58,14 +59,17 @@ def interpretLambda(symbol, args, env, ns): 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)) +lambda_args_arg = Arg("args", TypeEnum.ANY) +lambda_body_arg = Arg("body", TypeEnum.ANY) +CORE.register("lambda", NebSyntax("lambda", interpretLambda, [lambda_args_arg, lambda_body_arg], lambda_body_arg)) def interpretForCount(symbol, args, env, ns): + num = evaluate(args[0], env, ns) + if not isinstance(num, Int): + raise InterpretPanic(symbol, "count must be an :int", num) new_env = Environment(env) ret = None - for idx in range(0, args[0].value): + for idx in range(0, num.value): new_env.register("idx", Int(idx + 1)) for arg in args[1:]: ret = evaluate(arg, new_env, ns) @@ -74,13 +78,16 @@ def interpretForCount(symbol, args, env, ns): 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)) +for_body_arg = Arg("body", TypeEnum.ANY) +CORE.register("for-count", NebSyntax("for-count", interpretForCount, [for_count_arg, for_body_arg], for_body_arg)) def interpretForEach(symbol, args, env, ns): + coll = evaluate(args[0], env, ns) + if not isinstance(coll, List): + raise InterpretPanic(symbol, "coll must be a :list", coll) new_env = Environment(env) ret = None - for item in args[0].args: + for item in coll.args: new_env.register("_item_", evaluate(item, env, ns)) for arg in args[1:]: ret = evaluate(arg, new_env, ns) @@ -89,12 +96,12 @@ def interpretForEach(symbol, args, env, ns): 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)) +CORE.register("for-each", NebSyntax("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") + raise InterpretPanic(symbol, "each branch requires two expressions", len(arg.args)) 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) @@ -102,11 +109,11 @@ def interpretBranch(symbol, args, env, ns): return evaluate(arg.args[1], env, ns) return List([]) -CORE.register("branch", Builtin("branch", interpretBranch, [for_body_arg], for_body_arg)) +CORE.register("branch", NebSyntax("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") + raise InterpretPanic(symbol, "requires a :symbol") name = args[0].name # NOTE: we are not evaluating the name!! if ns is not None: @@ -121,7 +128,7 @@ def interpretFunc(symbol, args, env, ns): 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"))) +CORE.register("func", NebSyntax("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) @@ -130,8 +137,8 @@ def interpretBlock(symbol, args, env, ns): 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)) +block_arg = Arg("expr", TypeEnum.ANY) +CORE.register("block", NebSyntax("block", interpretBlock, [block_arg], block_arg)) def interpretWhile(symbol, args, env, ns): new_env = Environment(env) @@ -140,18 +147,21 @@ def interpretWhile(symbol, args, env, ns): while True: ev = evaluate(cond, new_env, ns) if not isinstance(ev, Bool): - raise InterpretPanic(symbol, "expects a :bool condition", ev) + raise InterpretPanic(symbol, "requires a :bool condition", cond) 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))) +CORE.register("while", NebSyntax("while", interpretWhile, [Arg("cond", TypeEnum.BOOL)], Arg("expr", TypeEnum.ANY))) +# NOTE this doesn't technically need to be a macro def interpretUse(symbol, args, env, ns): - target_file_name = args[0].value - target_file = Path(target_file_name).resolve() + target = evaluate(args[0], env, ns) + if not isinstance(target, String): + raise InterpretPanic(symbol, "filename must be a :string", target) + target_file = Path(target.value).resolve() if not target_file.exists(): raise InterpretPanic(symbol, "no such file", target_file) with open(target_file, "r") as fil: @@ -159,38 +169,48 @@ def interpretUse(symbol, args, env, ns): interpret(parse(lex(data)), env, ns) return List([]) -CORE.register("use", Builtin("use", interpretUse, [Arg("filename", TypeEnum.STRING)], return_type=Type(":list"))) +CORE.register("use", NebSyntax("use", interpretUse, [Arg("filename", TypeEnum.STRING)], return_type=Type(":list"))) +# NOTE this doesn't technically need to be a macro def interpretAssert(symbol, args, env, ns): - if args[0].value != True: + cond = evaluate(args[0], env, ns) + if not isinstance(cond, Bool): + raise InterpretPanic(symbol, "requires a :bool condition", cond) + if cond.value != True: raise InterpretPanic(symbol, "assertion failed") return List([]) -CORE.register("assert", Builtin("assert", interpretAssert, [Arg("cond", TypeEnum.BOOL)], return_type=Type(":list"))) +CORE.register("assert", NebSyntax("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() + target = evaluate(args[0], env, ns) + if not isinstance(target, String): + raise InterpretPanic(symbol, "filename must be a :string", target) + target_file = Path(target.value).resolve() if not target_file.exists(): raise InterpretPanic(symbol, "no such file", target_file) + if not isinstance(args[1], Symbol): + raise InterpretPanic(symbol, "requires a :symbol", args[1]) + new_ns = args[1].name with open(target_file, "r") as fil: data = fil.read() - interpret(parse(lex(data)), env, args[1].name) + interpret(parse(lex(data)), env, new_ns) return List([]) -CORE.register("use-as", Builtin("use-as", interpretUseAs, [Arg("filename", TypeEnum.STRING), Arg("namespace", TypeEnum.ANY, lazy=True)], return_type=Type(":list"))) +CORE.register("use-as", NebSyntax("use-as", interpretUseAs, [Arg("filename", TypeEnum.STRING), Arg("namespace", TypeEnum.ANY)], 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])) +quote_arg = Arg("arg", TypeEnum.ANY) +CORE.register("quote", NebSyntax("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? + ev = evaluate(args[0], env, ns) # TODO why do i have to evaluate twice? + return evaluate(ev, env, ns) eval_arg = Arg("arg", TypeEnum.ANY) -CORE.register("eval", Builtin("eval", interpretEval, [eval_arg])) +CORE.register("eval", NebSyntax("eval", interpretEval, [eval_arg])) def interpretType(symbol, args, env, ns): # (type typename parent func) @@ -201,13 +221,14 @@ def interpretType(symbol, args, env, ns): # 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.name): - raise InterpretPanic(symbol, f"no such type {args[1]}") - parent = env.get(args[1].name.name) + parent_type = evaluate(args[1], env, ns) + if not isinstance(parent_type, TypeWrap): + raise InterpretPanic(symbol, "parent must be a valid type", parent_type) + elif not env.contains(parent_type.name): + raise InterpretPanic(symbol, f"no such type {parent_type}") + parent = env.get(parent_type.name) - func = args[2] + func = evaluate(args[2], env, ns) if not isinstance(func, Function): raise InterpretPanic(symbol, "validation must be a :func", func) @@ -215,8 +236,51 @@ def interpretType(symbol, args, env, ns): env.register(name, new_type) return List([]) -type_name_arg = Arg("name", TypeEnum.ANY, lazy=True) +type_name_arg = Arg("name", TypeEnum.ANY) 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])) +CORE.register("type", NebSyntax("type", interpretType, [type_name_arg, type_parent_arg, type_func_arg])) + +def interpretOr(symbol, args, env, ns): + # or returns true for the first expression that returns true + for arg in args: + ev = evaluate(arg, env, ns) + if not isinstance(ev, Bool): + raise InterpretPanic(symbol, "requires :bool arguments") + if ev.value == True: + return ev + return Bool(False) + +or_arg = Arg("arg", TypeEnum.BOOL) +CORE.register("or", NebSyntax("or", interpretOr, [or_arg, or_arg], or_arg, Type(":bool"))) + +def interpretAnd(symbol, args, env, ns): + # and returns false for the first expression that returns false + for arg in args: + ev = evaluate(arg, env, ns) + if not isinstance(ev, Bool): + raise InterpretPanic(symbol, "requires :bool arguments") + if ev.value == False: + return ev + return Bool(True) + +CORE.register("and", NebSyntax("and", interpretAnd, [or_arg, or_arg], or_arg, Type(":bool"))) + +def interpretBench(symbol, args, env, ns): + before = datetime.now() + ret = evaluate(args[0], env, ns) + after = datetime.now() + print(f"bench [{symbol.line}]: {args[0]} => {after - before}") + return ret + +CORE.register("bench", NebSyntax("bench", interpretBench, [Arg("command", TypeEnum.ANY)], return_type=Type(":any"))) + +def interpretTry(symbol, args, env, ns): + try: + return evaluate(args[0], env, ns) + except NebPanic as e: + new_env = Environment(env) + new_env.register("_panic_", String(f"{e}")) + return evaluate(args[1], new_env, ns) +CORE.register("try", NebSyntax("try", interpretTry, [Arg("expr", TypeEnum.ANY), Arg("except", TypeEnum.ANY)], return_type=Type(":any"))) diff --git a/neb/std/fs.py b/neb/std/fs.py index d20cde9..144df0f 100644 --- a/neb/std/fs.py +++ b/neb/std/fs.py @@ -31,19 +31,6 @@ def interpretUnlink(symbol, args, env, ns): FS.register("unlink", Builtin("unlink", interpretUnlink, [Arg("filename", TypeEnum.STRING)], return_type=Type(":list"))) -def interpretWithWrite(symbol, args, env, ns): - target_file = args[0] - new_env = Environment(env) - target_path = Path(target_file.value).resolve() - ret = Literal([]) - with open(str(target_path), "w") as fil: - new_env.register("_file_", List([fil])) # TODO wrong! - for arg in args[1:]: - ret = evaluate(arg, new_env, ns) - return ret - -FS.register("with-write", Builtin("with-write", interpretWithWrite, [Arg("filename", TypeEnum.STRING)], Arg("exprs", TypeEnum.ANY, lazy=True))) - def interpretWrite(symbol, args, env, ns): string = args[0] handle = args[1] diff --git a/neb/std/functools.py b/neb/std/functools.py index 9e426b8..f83c49d 100644 --- a/neb/std/functools.py +++ b/neb/std/functools.py @@ -34,13 +34,11 @@ def interpretMap(symbol, args, env, ns): FUNCTOOLS.register("map", Builtin("map", interpretMap, [Arg("func", TypeEnum.ANY), Arg("list", TypeEnum.LIST)], return_type=Type(":list"))) +# TODO I think this is wrong 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): + if not isinstance(func, Function): raise InterpretPanic(symbol, "requires a symbol as its first argument", func) - new_expr = Expr([func] + args[1].args) - return evaluate(new_expr, env, ns) - -FUNCTOOLS.register("apply", Builtin("apply", interpretApply, [Arg("func", TypeEnum.ANY, lazy=True), Arg("list", TypeEnum.LIST)])) + return func.call(Expr([func] + args[1].args), env, ns) +FUNCTOOLS.register("apply", Builtin("apply", interpretApply, [Arg("func", TypeEnum.ANY), Arg("list", TypeEnum.LIST)])) diff --git a/neb/std/repl.py b/neb/std/repl.py index 16efb20..6b6be83 100644 --- a/neb/std/repl.py +++ b/neb/std/repl.py @@ -1,10 +1,11 @@ -from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, Function, evaluate, InterpretPanic +from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, Function, evaluate, InterpretPanic, Callable, NebSyntax, lex, parse +from .core import interpretQuote from ..structs import * REPL = Environment() def interpretHowTo(symbol, args, env, ns): - if not isinstance(args[0], Function): + if not isinstance(args[0], Callable): raise InterpretPanic(symbol, "expects a :func", args[0]) print(args[0].describe()) return List([]) @@ -12,19 +13,36 @@ def interpretHowTo(symbol, args, env, ns): REPL.register("howto", Builtin("howto", interpretHowTo, [Arg("symbol", TypeEnum.ANY)], return_type=Type(":list"))) def interpretSymbols(symbol, args, env, ns): - keys = [Symbol(k, -1) for k,v in sorted(env.environment.items())] + keys = [Symbol(k, -1) for k,v in sorted(env.get_all().items())] return List(keys) REPL.register("symbols", Builtin("symbols", interpretSymbols, [], return_type=Type(":list"))) def interpretFuncs(symbol, args, env, ns): - keys = [Symbol(k, -1) for k,v in env.environment.items() if isinstance(v, Builtin)] + keys = [Symbol(k, -1) for k,v in sorted(env.get_all().items()) if isinstance(v, Function)] return List(keys) REPL.register("funcs", Builtin("funcs", interpretFuncs, [], return_type=Type(":list"))) def interpretUserSymbols(symbol, args, env, ns): - keys = [Symbol(k, -1) for k,v in env.environment.items() if isinstance(v, UserFunction) or isinstance(v, Literal)] + keys = [Symbol(k, -1) for k,v in env.get_all().items() if isinstance(v, UserFunction) or isinstance(v, Literal)] return List(keys) REPL.register("user-symbols", Builtin("user-symbols", interpretUserSymbols, [], return_type=Type(":list"))) + +def interpretUserFuncs(symbol, args, env, ns): + keys = [Symbol(k, -1) for k,v in env.get_all().items() if isinstance(v, UserFunction)] + return List(keys) + +REPL.register("user-funcs", Builtin("user-funcs", interpretUserFuncs, [], return_type=Type(":list"))) + +def interpretSyntax(symbol, args, env, ns): + keys = [Symbol(k, -1) for k,v in sorted(env.get_all().items()) if isinstance(v, NebSyntax)] + return List(keys) + +REPL.register("syntax", Builtin("syntax", interpretSyntax, [], return_type=Type(":list"))) + +def interpretParseNeb(symbol, args, env, ns): + return interpretQuote(None, [parse(lex(args[0].value))[0]], env, ns) + +REPL.register("parse-neb", Builtin("parse-neb", interpretParseNeb, [Arg("string", TypeEnum.STRING)], return_type=Type(":any"))) diff --git a/neb/std/sys.py b/neb/std/sys.py index 525895a..f92eb77 100644 --- a/neb/std/sys.py +++ b/neb/std/sys.py @@ -35,15 +35,6 @@ def interpretPrint(symbol, args, env, ns): SYS.register("print", Builtin("print", interpretPrint, [Arg("arg", TypeEnum.STRING)], return_type=Type(":list"))) -def interpretBench(symbol, args, env, ns): - before = datetime.now() - ret = evaluate(args[0], env, ns) - after = datetime.now() - print(f"bench [{symbol.line}]: {args[0]} => {after - before}") - return ret - -SYS.register("bench", Builtin("bench", interpretBench, [Arg("command", TypeEnum.ANY, lazy=True)], return_type=Type(":any"))) - def interpretEnv(symbol, args, env, ns): items = os.environ[args[0].value].split(":") return List([String(item) for item in items]) |
