def nth(list_: MalExpression, index: MalExpression) -> MalExpression: assert isinstance(list_, MalList) or isinstance(list_, MalVector) assert isinstance(index, MalInt) list_native = list_.native() if index.native() > len(list_native) - 1: raise MalIndexError(index.native()) return list_native[index.native()]
def EVAL(ast: MalExpression, env: Env) -> MalExpression: if not isinstance(ast, MalList): return eval_ast(ast, env) if len(ast.native()) == 0: return ast first = str(ast.native()[0]) rest = ast.native()[1:] if first == "def!": key = str(ast.native()[1]) value = EVAL(ast.native()[2], env) return env.set(key, value) if first == "let*": assert len(rest) == 2 let_env = Env(env) bindings = rest[0] assert isinstance(bindings, MalList) or isinstance(bindings, MalVector) bindings_list = bindings.native() assert len(bindings_list) % 2 == 0 for i in range(0, len(bindings_list), 2): assert isinstance(bindings_list[i], MalSymbol) assert isinstance(bindings_list[i + 1], MalExpression) let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env)) expr = rest[1] return EVAL(expr, let_env) evaled_ast = eval_ast(ast, env) f = evaled_ast.native()[0] args = evaled_ast.native()[1:] try: return f.call(args) except AttributeError: raise MalInvalidArgumentException(f, "attribute error")
def get(map: MalExpression, key: MalExpression) -> MalExpression: if isinstance(map, MalNil): return MalNil() if not isinstance(map, MalHash_map): raise MalInvalidArgumentException(map, "not a hash map") if key.native() in map.native(): return map.native()[key.native()] else: return MalNil()
def map_(func: MalExpression, map_list: MalExpression) -> MalExpression: assert isinstance(func, MalFunctionCompiled) or isinstance( func, MalFunctionRaw) assert isinstance(map_list, MalList) or isinstance(map_list, MalVector) result_list: List[MalExpression] = [] for i in range(len(map_list.native())): elem = map_list.native()[i] result_list.append(func.call([elem])) return MalList(result_list)
def EVAL(ast: MalExpression, env: Env) -> MalExpression: # print("EVAL: " + str(ast)) if not isinstance(ast, MalList): return eval_ast(ast, env) if len(ast.native()) == 0: return ast first = str(ast.native()[0]) rest = ast.native()[1:] if first == "def!": key = str(ast.native()[1]) value = EVAL(ast.native()[2], env) return env.set(key, value) if first == "let*": assert len(rest) == 2 let_env = Env(env) bindings = rest[0] assert isinstance(bindings, MalList) or isinstance(bindings, MalVector) bindings_list = bindings.native() assert len(bindings_list) % 2 == 0 for i in range(0, len(bindings_list), 2): assert isinstance(bindings_list[i], MalSymbol) assert isinstance(bindings_list[i + 1], MalExpression) let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env)) expr = rest[1] return EVAL(expr, let_env) if first == "do": for x in range(0, len(rest) - 1): EVAL(rest[x], env) return EVAL(rest[len(rest) - 1], env) if first == "if": condition = EVAL(rest[0], env) if isinstance(condition, MalNil) or ( isinstance(condition, MalBoolean) and condition.native() is False ): if len(rest) >= 3: return EVAL(rest[2], env) else: return MalNil() else: return EVAL(rest[1], env) if first == "fn*": def func_body(x): func_env = Env(outer=env, binds=rest[0].native(), exprs=x) return EVAL(rest[1], func_env) return MalFunctionCompiled(func_body) evaled_ast = eval_ast(ast, env) f = evaled_ast.native()[0] args = evaled_ast.native()[1:] try: return f.call(args) except AttributeError: raise MalInvalidArgumentException(f, "attribute error")
def macroexpand(ast: MalExpression, env: Env) -> MalExpression: while True: if not is_macro_call(ast, env): return ast assert isinstance(ast, MalList) macro_func = env.get(ast.native()[0].native()) assert isinstance(macro_func, MalFunctionRaw) or isinstance( macro_func, MalFunctionCompiled) ast = macro_func.call(ast.native()[1:]) continue
def eval_ast(ast: MalExpression, env: Env) -> MalExpression: if isinstance(ast, MalSymbol): return env.get(ast) if isinstance(ast, MalList): return MalList([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalVector): return MalVector([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalHash_map): new_dict = {} # type: Dict[str, MalExpression] for key in ast.native(): new_dict[key] = EVAL(ast.native()[key], env) return MalHash_map(new_dict) return ast
def quasiquote(ast: MalExpression) -> MalExpression: if isinstance(ast, MalList): lst = ast.native() if len(lst) == 2: fst = lst[0] if isinstance(fst, MalSymbol) and fst.native() == u'unquote': return lst[1] return qq_foldr(lst) elif isinstance(ast, MalVector): return MalList([MalSymbol("vec"), qq_foldr(ast.native())]) elif isinstance(ast, MalSymbol) or isinstance(ast, MalHash_map): return MalList([MalSymbol("quote"), ast]) else: return ast
def equal(a: MalExpression, b: MalExpression) -> MalBoolean: if (isinstance(a, MalList) or isinstance(a, MalVector)) and (isinstance( b, MalList) or isinstance(b, MalVector)): a_native = a.native() b_native = b.native() if len(a_native) != len(b_native): return MalBoolean(False) for x in range(0, len(a_native)): if not equal(a_native[x], b_native[x]): return MalBoolean(False) return MalBoolean(True) if type(a) == type(b) and a.native() == b.native(): return MalBoolean(True) return MalBoolean(False)
def readline(arg: MalExpression) -> Union[MalString, MalNil]: try: assert isinstance(arg, MalString) line = input(arg.native()) except EOFError: return MalNil() return MalString(line)
def qq_loop(acc: MalList, elt: MalExpression) -> MalList: if isinstance(elt, MalList): lst = elt.native() if len(lst) == 2: fst = lst[0] if isinstance(fst, MalSymbol) and fst.native() == u"splice-unquote": return MalList([MalSymbol(u"concat"), lst[1], acc]) return MalList([MalSymbol(u"cons"), quasiquote(elt), acc])
def eval_ast(ast: MalExpression, env: Dict[str, MalFunctionCompiled]) -> MalExpression: if isinstance(ast, MalSymbol): try: return env[str(ast)] except KeyError: raise MalUnknownSymbolException(str(ast)) if isinstance(ast, MalList): return MalList([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalVector): return MalVector([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalHash_map): new_dict = {} # type: Dict[str, MalExpression] for key in ast.native(): new_dict[key] = EVAL(ast.native()[key], env) return MalHash_map(new_dict) return ast
def EVAL(ast: MalExpression, env: Dict[str, MalFunctionCompiled]) -> MalExpression: if not isinstance(ast, MalList): return eval_ast(ast, env) if len(ast.native()) == 0: return ast eval_result = eval_ast(ast, env) f = eval_result.native()[0] args = eval_result.native()[1:] return f.call(args)
def EVAL(ast: MalExpression, env: Env) -> MalExpression: dbgeval = env.get("DEBUG-EVAL") if (dbgeval is not None and not isinstance(dbgeval, MalNil) and (not isinstance(dbgeval, MalBoolean) or dbgeval.native())): print("EVAL: " + str(ast)) if isinstance(ast, MalSymbol): key = str(ast) val = env.get(key) if val is None: raise MalUnknownSymbolException(key) return val if isinstance(ast, MalVector): return MalVector([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalHash_map): new_dict = {} # type: Dict[str, MalExpression] for key in ast.native(): new_dict[key] = EVAL(ast.native()[key], env) return MalHash_map(new_dict) if not isinstance(ast, MalList): return ast if len(ast.native()) == 0: return ast first = str(ast.native()[0]) rest = ast.native()[1:] if first == "def!": key = str(ast.native()[1]) value = EVAL(ast.native()[2], env) return env.set(key, value) if first == "let*": assert len(rest) == 2 let_env = Env(env) bindings = rest[0] assert isinstance(bindings, MalList) or isinstance(bindings, MalVector) bindings_list = bindings.native() assert len(bindings_list) % 2 == 0 for i in range(0, len(bindings_list), 2): assert isinstance(bindings_list[i], MalSymbol) assert isinstance(bindings_list[i + 1], MalExpression) let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env)) expr = rest[1] return EVAL(expr, let_env) f, *args = (EVAL(form, env) for form in ast.native()) try: return f.call(args) except AttributeError: raise MalInvalidArgumentException(f, "attribute error")
def is_macro_call(ast: MalExpression, env: Env) -> bool: try: x = env.get(ast.native()[0].native()) try: assert isinstance(x, MalFunctionRaw) or isinstance( x, MalFunctionCompiled) except AssertionError: return False return x.is_macro() # type: ignore except (TypeError, MalUnknownSymbolException, AttributeError, IndexError, KeyError): return False
def EVAL(ast: MalExpression, env: Dict[str, MalFunctionCompiled]) -> MalExpression: # print("EVAL: " + str(ast)) if isinstance(ast, MalSymbol): try: return env[str(ast)] except KeyError: raise MalUnknownSymbolException(str(ast)) if isinstance(ast, MalVector): return MalVector([EVAL(x, env) for x in ast.native()]) if isinstance(ast, MalHash_map): new_dict = {} # type: Dict[str, MalExpression] for key in ast.native(): new_dict[key] = EVAL(ast.native()[key], env) return MalHash_map(new_dict) if not isinstance(ast, MalList): return ast if len(ast.native()) == 0: return ast f, *args = (EVAL(form, env) for form in ast.native()) return f.call(args)
def quasiquote(ast: MalExpression) -> MalExpression: if not is_pair(ast): return MalList([MalSymbol("quote"), ast]) elif core.equal(ast.native()[0], MalSymbol("unquote")).native(): return ast.native()[1] elif (is_pair(ast.native()[0]) and core.equal(ast.native()[0].native()[0], MalSymbol("splice-unquote")).native()): return MalList([ MalSymbol("concat"), ast.native()[0].native()[1], quasiquote(MalList(ast.native()[1:])), ]) else: return MalList([ MalSymbol("cons"), quasiquote(ast.native()[0]), quasiquote(MalList(ast.native()[1:])), ])
def EVAL(ast: MalExpression, env: Env) -> MalExpression: while True: ast = macroexpand(ast, env) ast_native = ast.native() if not isinstance(ast, MalList): return eval_ast(ast, env) elif len(ast_native) == 0: return ast first_str = str(ast_native[0]) if first_str == "macroexpand": return macroexpand(ast.native()[1], env) elif first_str == "def!": name: str = str(ast_native[1]) value: MalExpression = EVAL(ast_native[2], env) return env.set(name, value) if first_str == "defmacro!": name = str(ast_native[1]) value = EVAL(ast_native[2], env) assert isinstance(value, MalFunctionCompiled) or isinstance( value, MalFunctionRaw) value.make_macro() return env.set(name, value) elif first_str == "let*": assert len(ast_native) == 3 let_env = Env(env) bindings: MalExpression = ast_native[1] assert isinstance(bindings, MalList) or isinstance( bindings, MalVector) bindings_list: List[MalExpression] = bindings.native() assert len(bindings_list) % 2 == 0 for i in range(0, len(bindings_list), 2): assert isinstance(bindings_list[i], MalSymbol) assert isinstance(bindings_list[i + 1], MalExpression) let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env)) env = let_env ast = ast_native[2] continue elif first_str == "do": for x in range(1, len(ast_native) - 1): EVAL(ast_native[x], env) ast = ast_native[len(ast_native) - 1] continue elif first_str == "if": condition = EVAL(ast_native[1], env) if isinstance(condition, MalNil) or (isinstance(condition, MalBoolean) and condition.native() is False): if len(ast_native) >= 4: ast = ast_native[3] continue else: return MalNil() else: ast = ast_native[2] continue elif first_str == "fn*": raw_ast = ast_native[2] raw_params = ast_native[1] def fn(args: List[MalExpression]) -> MalExpression: f_ast = raw_ast f_env = Env(outer=env, binds=raw_params.native(), exprs=args) return EVAL(f_ast, f_env) return MalFunctionRaw(fn=fn, ast=raw_ast, params=raw_params, env=env) elif first_str == "quote": return (MalList(ast_native[1].native()) if isinstance( ast_native[1], MalVector) else ast_native[1]) elif first_str == "quasiquote": ast = quasiquote(ast_native[1]) continue elif first_str == "try*": try: return EVAL(ast_native[1], env) except MalException as e: if len(ast_native) < 3: raise e catch_block = ast_native[2] assert (isinstance(catch_block, MalList) and isinstance(catch_block.native()[0], MalSymbol) and str(catch_block.native()[0]) == "catch*" and len(catch_block.native()) == 3) exception_symbol = catch_block.native()[1] assert isinstance(exception_symbol, MalSymbol) env = Env(env) env.set(str(exception_symbol), e.native()) ast = catch_block.native()[2] continue else: evaled_ast = eval_ast(ast, env) f = evaled_ast.native()[0] args = evaled_ast.native()[1:] if isinstance(f, MalFunctionRaw): ast = f.ast() env = Env( outer=f.env(), binds=f.params().native(), exprs=evaled_ast.native()[1:], ) continue elif isinstance(f, MalFunctionCompiled): return f.call(args) else: raise MalInvalidArgumentException(f, "not a function")
def is_pair(x: MalExpression) -> bool: if (isinstance(x, MalList) or isinstance(x, MalVector)) and len(x.native()) > 0: return True return False
def not_(expr: MalExpression) -> MalExpression: if isinstance(expr, MalNil) or (isinstance(expr, MalBoolean) and expr.native() is False): return MalBoolean(True) else: return MalBoolean(False)
def true_q(arg: MalExpression) -> MalExpression: return MalBoolean(isinstance(arg, MalBoolean) and arg.native())
def false_q(arg: MalExpression) -> MalExpression: return MalBoolean(isinstance(arg, MalBoolean) and not arg.native())
def read_string(a: MalExpression) -> MalExpression: if isinstance(a, MalString): result = reader.read(a.native()) return result raise MalInvalidArgumentException(a, "not a string")
def less_equal(a: MalExpression, b: MalExpression) -> MalBoolean: if not isinstance(a, MalInt): raise MalInvalidArgumentException(a, "not an int") if not isinstance(b, MalInt): raise MalInvalidArgumentException(b, "not an int") return MalBoolean(a.native() <= b.native())
def keyword_q(arg: MalExpression) -> MalExpression: return MalBoolean(isinstance(arg, MalString) and arg.is_keyword())
def count(x: MalExpression) -> MalInt: if isinstance(x, MalList) or isinstance(x, MalVector): return MalInt(len(x.native())) elif isinstance(x, MalNil): return MalInt(0) raise MalInvalidArgumentException(x, "not a list")
def keyword(arg: MalExpression) -> MalExpression: assert isinstance(arg, MalString) if arg.is_keyword(): return arg else: return MalString(arg.unreadable_str(), keyword=True)
def symbol(arg: MalExpression) -> MalExpression: assert isinstance(arg, MalString) return MalSymbol(arg.unreadable_str())
def EVAL(ast: MalExpression, env: Env) -> MalExpression: while True: dbgeval = env.get("DEBUG-EVAL") if (dbgeval is not None and not isinstance(dbgeval, MalNil) and (not isinstance(dbgeval, MalBoolean) or dbgeval.native())): print("EVAL: " + str(ast)) ast_native = ast.native() if isinstance(ast, MalSymbol): key = str(ast) val = env.get(key) if val is None: raise MalUnknownSymbolException(key) return val if isinstance(ast, MalVector): return MalVector([EVAL(x, env) for x in ast_native]) if isinstance(ast, MalHash_map): new_dict = {} # type: Dict[str, MalExpression] for key in ast_native: new_dict[key] = EVAL(ast_native[key], env) return MalHash_map(new_dict) if not isinstance(ast, MalList): return ast elif len(ast_native) == 0: return ast first_str = str(ast_native[0]) if first_str == "def!": name: str = str(ast_native[1]) value: MalExpression = EVAL(ast_native[2], env) return env.set(name, value) elif first_str == "let*": assert len(ast_native) == 3 let_env = Env(env) bindings: MalExpression = ast_native[1] assert isinstance(bindings, MalList) or isinstance( bindings, MalVector) bindings_list: List[MalExpression] = bindings.native() assert len(bindings_list) % 2 == 0 for i in range(0, len(bindings_list), 2): assert isinstance(bindings_list[i], MalSymbol) assert isinstance(bindings_list[i + 1], MalExpression) let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env)) env = let_env ast = ast_native[2] continue elif first_str == "do": for x in range(1, len(ast_native) - 1): EVAL(ast_native[x], env) ast = ast_native[len(ast_native) - 1] continue elif first_str == "if": condition = EVAL(ast_native[1], env) if isinstance(condition, MalNil) or (isinstance(condition, MalBoolean) and condition.native() is False): if len(ast_native) >= 4: ast = ast_native[3] continue else: return MalNil() else: ast = ast_native[2] continue elif first_str == "fn*": raw_ast = ast_native[2] raw_params = ast_native[1] def fn(args: List[MalExpression]) -> MalExpression: f_ast = raw_ast f_env = Env(outer=env, binds=raw_params.native(), exprs=args) return EVAL(f_ast, f_env) return MalFunctionRaw(fn=fn, ast=raw_ast, params=raw_params, env=env) elif first_str == "quote": return (MalList(ast_native[1].native()) if isinstance( ast_native[1], MalVector) else ast_native[1]) elif first_str == "quasiquote": ast = quasiquote(ast_native[1]) continue else: f, *args = (EVAL(form, env) for form in ast_native) if isinstance(f, MalFunctionRaw): ast = f.ast() env = Env( outer=f.env(), binds=f.params().native(), exprs=args, ) continue elif isinstance(f, MalFunctionCompiled): return f.call(args) else: raise MalInvalidArgumentException(f, "not a function")
def empty_q(x: MalExpression) -> MalBoolean: if sequential_q(x): return MalBoolean(len(x.native()) == 0) raise MalInvalidArgumentException(x, "not a list")