Commit | Line | Data |
---|---|---|
31690700 JM |
1 | import sys, traceback |
2 | import mal_readline | |
406d1370 JM |
3 | import mal_types as types |
4 | import reader, printer | |
5 | from env import Env | |
6 | import core | |
31690700 JM |
7 | |
8 | # read | |
9 | def READ(str): | |
406d1370 | 10 | return reader.read_str(str) |
31690700 JM |
11 | |
12 | # eval | |
13 | def is_pair(x): | |
406d1370 | 14 | return types._sequential_Q(x) and len(x) > 0 |
31690700 JM |
15 | |
16 | def quasiquote(ast): | |
17 | if not is_pair(ast): | |
406d1370 JM |
18 | return types._list(types._symbol("quote"), |
19 | ast) | |
31690700 JM |
20 | elif ast[0] == 'unquote': |
21 | return ast[1] | |
22 | elif is_pair(ast[0]) and ast[0][0] == 'splice-unquote': | |
406d1370 JM |
23 | return types._list(types._symbol("concat"), |
24 | ast[0][1], | |
25 | quasiquote(ast[1:])) | |
31690700 | 26 | else: |
406d1370 JM |
27 | return types._list(types._symbol("cons"), |
28 | quasiquote(ast[0]), | |
29 | quasiquote(ast[1:])) | |
31690700 JM |
30 | |
31 | def is_macro_call(ast, env): | |
406d1370 JM |
32 | return (types._list_Q(ast) and |
33 | types._symbol_Q(ast[0]) and | |
31690700 JM |
34 | env.find(ast[0]) and |
35 | hasattr(env.get(ast[0]), '_ismacro_')) | |
36 | ||
37 | def macroexpand(ast, env): | |
38 | while is_macro_call(ast, env): | |
39 | mac = env.get(ast[0]) | |
0a9a4685 | 40 | ast = mac(*ast[1:]) |
31690700 JM |
41 | return ast |
42 | ||
43 | def eval_ast(ast, env): | |
406d1370 | 44 | if types._symbol_Q(ast): |
31690700 | 45 | return env.get(ast) |
406d1370 JM |
46 | elif types._list_Q(ast): |
47 | return types._list(*map(lambda a: EVAL(a, env), ast)) | |
48 | elif types._vector_Q(ast): | |
49 | return types._vector(*map(lambda a: EVAL(a, env), ast)) | |
50 | elif types._hash_map_Q(ast): | |
31690700 JM |
51 | keyvals = [] |
52 | for k in ast.keys(): | |
53 | keyvals.append(EVAL(k, env)) | |
54 | keyvals.append(EVAL(ast[k], env)) | |
406d1370 | 55 | return types._hash_map(*keyvals) |
31690700 JM |
56 | else: |
57 | return ast # primitive value, return unchanged | |
58 | ||
59 | def EVAL(ast, env): | |
60 | while True: | |
db4c329a | 61 | #print("EVAL %s" % printer._pr_str(ast)) |
406d1370 | 62 | if not types._list_Q(ast): |
31690700 | 63 | return eval_ast(ast, env) |
406d1370 | 64 | |
31690700 JM |
65 | # apply list |
66 | ast = macroexpand(ast, env) | |
92c7576c DM |
67 | if not types._list_Q(ast): |
68 | return eval_ast(ast, env) | |
31690700 | 69 | if len(ast) == 0: return ast |
406d1370 | 70 | a0 = ast[0] |
31690700 | 71 | |
31690700 JM |
72 | if "def!" == a0: |
73 | a1, a2 = ast[1], ast[2] | |
74 | res = EVAL(a2, env) | |
75 | return env.set(a1, res) | |
76 | elif "let*" == a0: | |
77 | a1, a2 = ast[1], ast[2] | |
78 | let_env = Env(env) | |
79 | for i in range(0, len(a1), 2): | |
80 | let_env.set(a1[i], EVAL(a1[i+1], let_env)) | |
6301e0b6 JM |
81 | ast = a2 |
82 | env = let_env | |
83 | # Continue loop (TCO) | |
31690700 JM |
84 | elif "quote" == a0: |
85 | return ast[1] | |
86 | elif "quasiquote" == a0: | |
6301e0b6 JM |
87 | ast = quasiquote(ast[1]); |
88 | # Continue loop (TCO) | |
31690700 | 89 | elif 'defmacro!' == a0: |
c1da7517 | 90 | func = types._clone(EVAL(ast[2], env)) |
31690700 JM |
91 | func._ismacro_ = True |
92 | return env.set(ast[1], func) | |
93 | elif 'macroexpand' == a0: | |
94 | return macroexpand(ast[1], env) | |
95 | elif "py!*" == a0: | |
a5bdda6d | 96 | exec(compile(ast[1], '', 'single'), globals()) |
31690700 JM |
97 | return None |
98 | elif "py*" == a0: | |
a5bdda6d | 99 | return types.py_to_mal(eval(ast[1])) |
31690700 JM |
100 | elif "." == a0: |
101 | el = eval_ast(ast[2:], env) | |
102 | f = eval(ast[1]) | |
103 | return f(*el) | |
104 | elif "try*" == a0: | |
dd7a4f55 JM |
105 | if len(ast) < 3: |
106 | return EVAL(ast[1], env) | |
31690700 JM |
107 | a1, a2 = ast[1], ast[2] |
108 | if a2[0] == "catch*": | |
dd7a4f55 | 109 | err = None |
31690700 | 110 | try: |
dd7a4f55 JM |
111 | return EVAL(a1, env) |
112 | except types.MalException as exc: | |
113 | err = exc.object | |
31690700 | 114 | except Exception as exc: |
dd7a4f55 JM |
115 | err = exc.args[0] |
116 | catch_env = Env(env, [a2[1]], [err]) | |
117 | return EVAL(a2[2], catch_env) | |
31690700 JM |
118 | else: |
119 | return EVAL(a1, env); | |
120 | elif "do" == a0: | |
121 | eval_ast(ast[1:-1], env) | |
122 | ast = ast[-1] | |
123 | # Continue loop (TCO) | |
124 | elif "if" == a0: | |
125 | a1, a2 = ast[1], ast[2] | |
126 | cond = EVAL(a1, env) | |
127 | if cond is None or cond is False: | |
128 | if len(ast) > 3: ast = ast[3] | |
129 | else: ast = None | |
130 | else: | |
131 | ast = a2 | |
132 | # Continue loop (TCO) | |
133 | elif "fn*" == a0: | |
134 | a1, a2 = ast[1], ast[2] | |
406d1370 | 135 | return types._function(EVAL, Env, a2, env, a1) |
31690700 JM |
136 | else: |
137 | el = eval_ast(ast, env) | |
138 | f = el[0] | |
a34b0200 JM |
139 | if hasattr(f, '__ast__'): |
140 | ast = f.__ast__ | |
141 | env = f.__gen_env__(el[1:]) | |
31690700 JM |
142 | else: |
143 | return f(*el[1:]) | |
144 | ||
145 | ||
146 | def PRINT(exp): | |
406d1370 | 147 | return printer._pr_str(exp) |
31690700 JM |
148 | |
149 | # repl | |
150 | repl_env = Env() | |
151 | def REP(str): | |
152 | return PRINT(EVAL(READ(str), repl_env)) | |
31690700 | 153 | |
8cb5cda4 | 154 | # core.py: defined using python |
b8ee29b2 JM |
155 | for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) |
156 | repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) | |
157 | repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) | |
31690700 | 158 | |
8cb5cda4 | 159 | # core.mal: defined using the language itself |
db4c329a | 160 | REP("(def! *host-language* \"python\")") |
31690700 | 161 | REP("(def! not (fn* (a) (if a false true)))") |
e6d41de4 | 162 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))") |
31690700 | 163 | REP("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") |
31690700 JM |
164 | |
165 | if len(sys.argv) >= 2: | |
166 | REP('(load-file "' + sys.argv[1] + '")') | |
86b689f3 JM |
167 | sys.exit(0) |
168 | ||
169 | # repl loop | |
170 | REP("(println (str \"Mal [\" *host-language* \"]\"))") | |
171 | while True: | |
172 | try: | |
173 | line = mal_readline.readline("user> ") | |
174 | if line == None: break | |
175 | if line == "": continue | |
176 | print(REP(line)) | |
177 | except reader.Blank: continue | |
dd7a4f55 JM |
178 | except types.MalException as e: |
179 | print("Error:", printer._pr_str(e.object)) | |
86b689f3 JM |
180 | except Exception as e: |
181 | print("".join(traceback.format_exception(*sys.exc_info()))) |