from .lexer import lex from .parser import parse from .exceptions import * from .typeclass import TypeEnum, is_subtype_of from .structs import * from copy import deepcopy def interpret(exprs, env, ns=None): ret = None for expr in exprs: ret = evaluate(expr, env, ns) return ret def evaluate(expr, env, ns=None): if isinstance(expr, Literal) or isinstance(expr, Function) or isinstance(expr, TypeWrap) or isinstance(expr, List): return expr elif isinstance(expr, Symbol) or isinstance(expr, Type): if env.contains(expr.name): if isinstance(expr, Type) and expr.inner is not None: typecopy = deepcopy(env.get(expr.name)) inner = env.get(f"{expr.inner}") typecopy.name.inner = inner return evaluate(typecopy, env, ns) else: return evaluate(env.get(expr.name), env, ns) elif ns is not None and env.contains(f"{ns}/{expr.name}"): if isinstance(expr, Type) and expr.inner is not None: typecopy = deepcopy(env.get(f"{ns}/{expr.name}")) inner = env.get(f"{expr.inner}") typecopy.name.inner = inner return evaluate(typecopy, env, ns) else: return evaluate(env.get(f"{ns}/{expr.name}"), env, ns) else: if isinstance(expr, Symbol): raise NebPanic(f"no such symbol: {expr}") else: raise NebPanic(f"no such type {expr}") # if it's an empty list, return it elif len(expr.args) == 0: return expr if not isinstance(expr.args[0], Symbol): raise NebPanic("can't evaluate without a symbol") name = expr.args[0].name if env.contains(name): return env.get(name).call(expr, env, ns) elif ns is not None and env.contains(f"{ns}/{name}"): return env.get(f"{ns}/{name}").call(expr, env, ns) else: raise InterpretPanic(expr.args[0], "unable to evaluate") class Function: def __init__(self, name, params, body, args=None, many=None): self.name = name self.params = params self.body = body self.args = args self.many = many #self.type_ = TypeEnum.ANY # TODO no it's not self.type_ = Type(":any") # TODO no it's not self.return_type = Type(":any") def describe(self, name=None): if name is None: name = self.name out = [f"({name}"] out.append(string_args(self.args, self.many)) return " ".join(out).strip() + f") => {self.return_type}" def arity_check(self, symbol, params): min_arity = len([a for a in self.args if not a.optional]) max_arity = -1 if self.many is not None else len(self.args) if len(params) < min_arity or (max_arity >= 0 and len(params) > max_arity): if max_arity < 0: fmt = f"{min_arity}+" elif min_arity != max_arity: fmt = f"{min_arity}-{max_arity}" else: fmt = f"{min_arity}" raise InterpretPanic(symbol, f"expected [{fmt}] arguments, received {len(params)}") return True def evaluate_args(self, symbol, params, env, ns): ret = [] for idx, param in enumerate(params): if idx < len(self.args): arg = self.args[idx] else: arg = self.many if arg.lazy: ret.append(param) continue ev = evaluate(param, env, ns) expected_type = evaluate(arg.type_, env, ns) valid = expected_type.validate_type(ev, env, ns) if not valid.value: exp = f"{arg.type_}" rec = f"{ev.type_}" raise InterpretPanic(symbol, f"received {rec}, expected {exp}", ev) ret.append(ev) return ret def call(self, expr, env): pass class Builtin(Function): def __init__(self, name, callable_, args=None, many=None, return_type=None): super().__init__(name, None, callable_, args, many) if return_type is not None: self.return_type = return_type def __str__(self): return f"builtin function {self.name}" def call(self, expr, env, ns): self.arity_check(expr.args[0], expr.args[1:]) evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env, ns) return self.body(expr.args[0], evaluated_args, env, ns) class UserFunction(Function): def __init__(self, name, params, body): newparams, args, many = self.process_params(name, params) super().__init__(name, newparams, body, args, many) def __str__(self): out = f"(func {self.name} {self.return_type} (" args_list = [f"{a.name} {a.type_}" for a in self.args] if self.many: args_list.append(f"{self.many.name} {self.many.type_}") out = out + " ".join(args_list) + ") " for expr in self.body: out = out + f"{expr} " return out.strip() + ")" def process_params(self, name, params): newparams = [] args = [] many = None prev_type = False first = True for param in params: if isinstance(param, Symbol): if many is not None: raise NebPanic("& must be last argument") if param.name == "&": many = Arg(param.name, TypeEnum.ANY) else: newparams.append(param) args.append(Arg(param.name, TypeEnum.ANY)) prev_type = False elif isinstance(param, Type) and not prev_type and not first: if many is None: #args[-1].type_ = param.name args[-1].type_ = param else: #many.type_ = param.name many.type_ = param prev_type = True else: raise NebPanic("invalid :func signature", param) first = False return newparams, args, many def call(self, expr, env, ns): self.arity_check(expr.args[0], expr.args[1:]) evaluated_args = self.evaluate_args(expr.args[0], expr.args[1:], env, ns) this_env = Environment(env) for idx, param in enumerate(self.params): this_env.register(param.name, evaluated_args[idx]) # if we got "many", wrap the rest in a list if self.many: this_env.register(self.many.name, List(evaluated_args[len(self.params):])) return interpret(self.body, env=this_env, ns=ns) class TypeWrap: def __init__(self, name, parent, is_func): self.name = ALL_TYPES[name] self.parent = parent self.is_func = is_func def validate_type(self, target, env, ns): # if it's an any type, it's valid if self.parent is None: return Bool(True) if isinstance(self.is_func, Function): valid = self.is_func.call(Expr([self.name, target]), env, ns) else: valid = self.is_func(self.name, [target], env, ns) if valid.value == True: return valid parent_type = env.get(target.type_.name) while valid.value != True and parent_type.parent is not None: parent_type = env.get(parent_type.parent.name.name) valid = Bool(self.name == parent_type.name) # TODO wrong return valid def __str__(self): return f"{self.name}" class NebType(TypeWrap): pass class UserType(TypeWrap): def __init__(self, name, parent, is_func): if name in ALL_TYPES: raise NebPanic(f"already a type called {name}") ALL_TYPES[name] = Type(name) super().__init__(name, parent, is_func)