diff options
Diffstat (limited to 'neb/std')
| -rw-r--r-- | neb/std/core.py | 135 | ||||
| -rw-r--r-- | neb/std/repl.py | 4 | 
2 files changed, 80 insertions, 59 deletions
| diff --git a/neb/std/core.py b/neb/std/core.py index f6b5b06..a0f2cf6 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, NebMacro  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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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", NebMacro("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): -        raise InterpretPanic(symbol, f"no such type {args[1]}") -    parent = env.get(args[1].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,10 +236,10 @@ 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", NebMacro("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 @@ -230,8 +251,8 @@ def interpretOr(symbol, args, env, ns):              return ev      return Bool(False) -or_arg = Arg("arg", TypeEnum.BOOL, lazy=True) -CORE.register("or", Builtin("or", interpretOr, [or_arg, or_arg], or_arg, Type(":bool"))) +or_arg = Arg("arg", TypeEnum.BOOL) +CORE.register("or", NebMacro("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 @@ -252,4 +273,4 @@ def interpretBench(symbol, args, env, ns):      print(f"bench [{symbol.line}]: {args[0]} => {after - before}")      return ret -CORE.register("bench", Builtin("bench", interpretBench, [Arg("command", TypeEnum.ANY, lazy=True)], return_type=Type(":any"))) +CORE.register("bench", NebMacro("bench", interpretBench, [Arg("command", TypeEnum.ANY)], return_type=Type(":any"))) diff --git a/neb/std/repl.py b/neb/std/repl.py index 16efb20..73f82d6 100644 --- a/neb/std/repl.py +++ b/neb/std/repl.py @@ -1,10 +1,10 @@ -from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, Function, evaluate, InterpretPanic +from .. import TypeEnum, Environment, Arg, Builtin, UserFunction, Function, evaluate, InterpretPanic, Callable  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([]) | 
