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 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 test_step8_is_macro(self): self.assertEqual(False, MalFunctionCompiled(lambda a: MalInt(1)).is_macro()) self.assertEqual( False, MalFunctionRaw(core.ns["+"], MalInt(1), MalList([]), Env(None)).is_macro(), )