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