Add python.2 implementation
[jackhill/mal.git] / python.2 / step4_if_fn_do.py
1 import readline
2 from typing import Dict
3
4 import core
5 import reader
6 from env import Env
7 from mal_types import (
8 MalExpression,
9 MalSymbol,
10 MalInvalidArgumentException,
11 MalUnknownSymbolException,
12 MalSyntaxException,
13 )
14 from mal_types import (
15 MalList,
16 MalNil,
17 MalBoolean,
18 MalFunctionCompiled,
19 MalVector,
20 MalHash_map,
21 )
22
23 repl_env = Env(None)
24 for key in core.ns:
25 repl_env.set(key, core.ns[key])
26
27
28 def READ(x: str) -> MalExpression:
29 return reader.read(x)
30
31
32 def eval_ast(ast: MalExpression, env: Env) -> MalExpression:
33 if isinstance(ast, MalSymbol):
34 return env.get(ast)
35 if isinstance(ast, MalList):
36 return MalList([EVAL(x, env) for x in ast.native()])
37 if isinstance(ast, MalVector):
38 return MalVector([EVAL(x, env) for x in ast.native()])
39 if isinstance(ast, MalHash_map):
40 new_dict = {} # type: Dict[str, MalExpression]
41 for key in ast.native():
42 new_dict[key] = EVAL(ast.native()[key], env)
43 return MalHash_map(new_dict)
44 return ast
45
46
47 def EVAL(ast: MalExpression, env: Env) -> MalExpression:
48 # print("EVAL: " + str(ast))
49 if not isinstance(ast, MalList):
50 return eval_ast(ast, env)
51 if len(ast.native()) == 0:
52 return ast
53 first = str(ast.native()[0])
54 rest = ast.native()[1:]
55 if first == "def!":
56 key = str(ast.native()[1])
57 value = EVAL(ast.native()[2], env)
58 return env.set(key, value)
59 if first == "let*":
60 assert len(rest) == 2
61 let_env = Env(env)
62 bindings = rest[0]
63 assert isinstance(bindings, MalList) or isinstance(bindings, MalVector)
64 bindings_list = bindings.native()
65 assert len(bindings_list) % 2 == 0
66 for i in range(0, len(bindings_list), 2):
67 assert isinstance(bindings_list[i], MalSymbol)
68 assert isinstance(bindings_list[i + 1], MalExpression)
69 let_env.set(str(bindings_list[i]), EVAL(bindings_list[i + 1], let_env))
70 expr = rest[1]
71 return EVAL(expr, let_env)
72 if first == "do":
73 for x in range(0, len(rest) - 1):
74 EVAL(rest[x], env)
75 return EVAL(rest[len(rest) - 1], env)
76 if first == "if":
77 condition = EVAL(rest[0], env)
78
79 if isinstance(condition, MalNil) or (
80 isinstance(condition, MalBoolean) and condition.native() is False
81 ):
82 if len(rest) >= 3:
83 return EVAL(rest[2], env)
84 else:
85 return MalNil()
86 else:
87 return EVAL(rest[1], env)
88 if first == "fn*":
89
90 def func_body(x):
91 func_env = Env(outer=env, binds=rest[0].native(), exprs=x)
92 return EVAL(rest[1], func_env)
93
94 return MalFunctionCompiled(func_body)
95
96 evaled_ast = eval_ast(ast, env)
97 f = evaled_ast.native()[0]
98 args = evaled_ast.native()[1:]
99 try:
100 return f.call(args)
101 except AttributeError:
102 raise MalInvalidArgumentException(f, "attribute error")
103
104
105 def PRINT(x: MalExpression) -> str:
106 return str(x)
107
108
109 def rep(x: str) -> str:
110 return PRINT(EVAL(READ(x), repl_env))
111
112
113 if __name__ == "__main__":
114 # repl loop
115 eof: bool = False
116 while not eof:
117 try:
118 line = input("user> ")
119 readline.add_history(line)
120 try:
121 print(rep(line))
122 except MalUnknownSymbolException as e:
123 print("'" + e.func + "' not found")
124 except MalSyntaxException as e:
125 print("ERROR: invalid syntax: " + str(e))
126 except EOFError:
127 eof = True