diff options
| author | mryouse | 2022-07-20 02:52:14 +0000 |
|---|---|---|
| committer | mryouse | 2022-07-20 02:52:14 +0000 |
| commit | 8a3642042e75294707e543135767dcdfb85a620a (patch) | |
| tree | 36ae82322eb64922f95bf639363ff43032ca5f07 /neb | |
| parent | 5ece218b4e1461a8ce9c1770aba0e47c829e8362 (diff) | |
WIP multifunc
Diffstat (limited to 'neb')
| -rw-r--r-- | neb/__init__.py | 148 |
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]) |
