aboutsummaryrefslogtreecommitdiff
path: root/neb
diff options
context:
space:
mode:
authormryouse2022-07-20 02:52:14 +0000
committermryouse2022-07-20 02:52:14 +0000
commit8a3642042e75294707e543135767dcdfb85a620a (patch)
tree36ae82322eb64922f95bf639363ff43032ca5f07 /neb
parent5ece218b4e1461a8ce9c1770aba0e47c829e8362 (diff)
WIP multifunc
Diffstat (limited to 'neb')
-rw-r--r--neb/__init__.py148
1 files changed, 143 insertions, 5 deletions
diff --git a/neb/__init__.py b/neb/__init__.py
index a50652d..73b6199 100644
--- a/neb/__init__.py
+++ b/neb/__init__.py
@@ -61,6 +61,64 @@ def evaluate(expr, env, ns=None):
raise InterpretPanic(expr.args[0], "unable to evaluate")
+# THINGS
+# - functions/syntax can have multiple arity definitions
+# - functions can have multiple same-arity, different-type definitions
+
+# callable
+# - name
+# - implementations
+# - signature (arguments (name and type), return type)
+
+
+class Signature:
+
+ def __init__(self, *args, many=None, return_type=None):
+ self.args = args
+ self.many = many
+ if return_type is None:
+ self.return_type = Type(":any")
+
+ def compatable_arity(self, arity):
+ if arity < len(self.args):
+ return False
+ elif self.many is None and arity > len(self.args):
+ return False
+ else:
+ return True
+
+ def arity_str(self):
+ out = f"{len(self.args)}"
+ if self.many is not None:
+ out += "+"
+ return out
+
+ def get_type_by_idx(self, idx):
+ #print(f"getting type by idx: {idx}")
+ if idx < len(self.args):
+ return self.args[idx]
+ else:
+ return self.many
+
+ def __str__(self):
+ out = ""
+ for arg in self.args:
+ out = f"{out}{arg}"
+ if self.many is not None:
+ out = f"{out}{self.many}"
+ return out
+
+
+
+
+
+
+
+
+
+
+
+
class Callable:
def __init__(self, name, params, body, args=None, many=None):
@@ -73,6 +131,11 @@ class Callable:
self.type_ = Type(":any") # TODO no it's not
self.return_type = Type(":any")
+ if args is None:
+ self.sig = Signature(many=many)
+ else:
+ self.sig = Signature(*args, many=many)
+
def describe(self, name=None):
if name is None:
name = self.name
@@ -97,6 +160,74 @@ class Callable:
def call(self, expr, env):
pass
+class MultiFunction(Callable):
+
+ def __init__(self, name):
+ super().__init__(name, None, None)
+ self.impls = {}
+
+ def describe(self):
+ return [i.describe() for i in self.impls]
+
+ def register(self, impl):
+ self.impls[f"{impl.sig}"] = impl
+
+ def call(self, expr, env, ns):
+ symbol = expr.args[0]
+ params = expr.args[1:]
+
+ #print("in resolve!")
+ #print(f"{self.impls}")
+ #for f in self.impls.values():
+ # print(f"{type(f)}")
+
+ # get compatable arities
+ compatable_arities = [k for k,v in self.impls.items() if v.sig.compatable_arity(len(params))]
+ if len(compatable_arities) == 0:
+ fmt = "|".join(f"{v.sig.arity_str()}" for v in self.impls.values())
+ raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(params)}")
+
+ #print(f"compatable arities: {compatable_arities}")
+ ret = []
+ prev_candidates = []
+ current_types = []
+ next_candidates = compatable_arities
+ for param_idx, param in enumerate(params):
+ # evaluate the parameter
+ ev = evaluate(param, env, ns)
+
+ # reset the types we may be looking for
+ current_types = []
+ prev_candidates = next_candidates[:]
+ next_candidates = []
+
+ # loop through candidate functions
+ for candidate in prev_candidates:
+ func = self.impls[candidate]
+ exp = func.sig.get_type_by_idx(param_idx)
+ expected_type = evaluate(exp.type_, env, ns)
+ #print(f"expected type: {expected_type}")
+ current_types.append(expected_type)
+
+ valid = expected_type.validate_type(ev, env, ns)
+ if valid.value:
+ next_candidates.append(candidate)
+
+ # if we have no more good functions, panic
+ if len(next_candidates) == 0:
+ fmt = "|".join(f"{t}" for t in current_types)
+ rec = f"{ev.type_}"
+ raise InterpretPanic(symbol, f"received {rec}, expected [{fmt}]", ev)
+ else:
+ ret.append(ev)
+
+ if len(next_candidates) != 1:
+ raise InterpretPanic(symbol, "ambiguous definition!")
+
+ #return(f"returning!")
+
+ return self.impls[next_candidates[0]].call(Expr([symbol] + ret), env, ns)
+
class Special(Callable):
def __init__(self, name, params, body, args=None, many=None):
@@ -113,7 +244,7 @@ class NebSyntax(Special):
return f"syntax function {self.name}"
def call(self, expr, env, ns):
- self.arity_check(expr.args[0], expr.args[1:])
+ #self.arity_check(expr.args[0], expr.args[1:])
return self.body(expr.args[0], expr.args[1:], env, ns)
@@ -141,7 +272,8 @@ class Function(Callable):
return ret
-class Builtin(Function):
+#class Builtin(Function):
+class Builtin(Callable):
def __init__(self, name, callable_, args=None, many=None, return_type=None):
super().__init__(name, None, callable_, args, many)
@@ -152,12 +284,17 @@ class Builtin(Function):
return f"builtin function {self.name}"
def call(self, expr, env, ns):
+ '''
self.arity_check(expr.args[0], expr.args[1:])
evaluated_args = self.precall(expr.args[0], expr.args[1:], env, ns)
return self.body(expr.args[0], evaluated_args, env, ns)
+ '''
+ #print(type(expr.args[1]))
+ return self.body(expr.args[0], expr.args[1:], env, ns)
-class UserFunction(Function):
+#class UserFunction(Function):
+class UserFunction(Callable):
def __init__(self, name, params, body):
newparams, args, many = self.process_params(name, params)
@@ -204,8 +341,9 @@ class UserFunction(Function):
return newparams, args, many
def call(self, expr, env, ns):
- self.arity_check(expr.args[0], expr.args[1:])
- evaluated_args = self.precall(expr.args[0], expr.args[1:], env, ns)
+ #self.arity_check(expr.args[0], expr.args[1:])
+ #evaluated_args = self.precall(expr.args[0], expr.args[1:], env, ns)
+ evaluated_args = expr.args[1:]
this_env = Environment(env)
for idx, param in enumerate(self.params):
this_env.register(param.name, evaluated_args[idx])