Commit | Line | Data |
---|---|---|
23fa1b11 JM |
1 | import sys |
2 | IS_RPYTHON = sys.argv[0].endswith('rpython') | |
3 | ||
4 | if IS_RPYTHON: | |
5 | #from rpython.rlib.debug import fatalerror | |
6 | from rpython.rtyper.lltypesystem import lltype | |
7 | from rpython.rtyper.lltypesystem.lloperation import llop | |
8 | else: | |
9 | import traceback | |
10 | ||
11b4be99 JM |
11 | import mal_readline |
12 | import mal_types as types | |
8855a05a JM |
13 | from mal_types import (MalSym, MalInt, MalStr, |
14 | nil, true, false, _symbol, _keywordu, | |
15 | MalList, _list, MalVector, MalHashMap, MalFunc) | |
11b4be99 JM |
16 | import reader, printer |
17 | from env import Env | |
18 | import core | |
19 | ||
20 | # read | |
21 | def READ(str): | |
22 | return reader.read_str(str) | |
23 | ||
24 | # eval | |
fbfe6784 NB |
25 | def qq_loop(elt, acc): |
26 | if types._list_Q(elt) and len(elt) == 2: | |
27 | fst = elt[0] | |
28 | if isinstance(fst, MalSym) and fst.value == u"splice-unquote": | |
29 | return _list(_symbol(u"concat"), elt[1], acc) | |
30 | return _list(_symbol(u"cons"), quasiquote(elt), acc) | |
31 | ||
32 | def qq_foldr(seq): | |
33 | acc = _list() | |
34 | for elt in reversed(seq): | |
35 | acc = qq_loop (elt, acc) | |
36 | return acc | |
11b4be99 JM |
37 | |
38 | def quasiquote(ast): | |
fbfe6784 NB |
39 | if types._list_Q(ast): |
40 | if len(ast) == 2: | |
41 | fst = ast[0] | |
42 | if isinstance(fst, MalSym) and fst.value == u"unquote": | |
43 | return ast[1] | |
44 | return qq_foldr(ast.values) | |
45 | elif types._vector_Q(ast): | |
46 | return _list(_symbol(u"vec"), qq_foldr(ast.values)) | |
47 | elif types._symbol_Q(ast) or types._hash_map_Q(ast): | |
11b4be99 JM |
48 | return _list(_symbol(u"quote"), ast) |
49 | else: | |
fbfe6784 | 50 | return ast |
11b4be99 JM |
51 | |
52 | def is_macro_call(ast, env): | |
53 | if types._list_Q(ast): | |
54 | a0 = ast[0] | |
55 | if isinstance(a0, MalSym): | |
56 | if not env.find(a0) is None: | |
57 | return env.get(a0).ismacro | |
58 | return False | |
59 | ||
60 | def macroexpand(ast, env): | |
61 | while is_macro_call(ast, env): | |
62 | assert isinstance(ast[0], MalSym) | |
63 | mac = env.get(ast[0]) | |
64 | ast = macroexpand(mac.apply(ast.rest()), env) | |
65 | return ast | |
66 | ||
67 | def eval_ast(ast, env): | |
68 | if types._symbol_Q(ast): | |
69 | assert isinstance(ast, MalSym) | |
70 | return env.get(ast) | |
71 | elif types._list_Q(ast): | |
72 | res = [] | |
73 | for a in ast.values: | |
74 | res.append(EVAL(a, env)) | |
75 | return MalList(res) | |
8855a05a JM |
76 | elif types._vector_Q(ast): |
77 | res = [] | |
78 | for a in ast.values: | |
79 | res.append(EVAL(a, env)) | |
80 | return MalVector(res) | |
81 | elif types._hash_map_Q(ast): | |
82 | new_dct = {} | |
83 | for k in ast.dct.keys(): | |
84 | new_dct[k] = EVAL(ast.dct[k], env) | |
85 | return MalHashMap(new_dct) | |
11b4be99 JM |
86 | else: |
87 | return ast # primitive value, return unchanged | |
88 | ||
89 | def EVAL(ast, env): | |
90 | while True: | |
91 | #print("EVAL %s" % printer._pr_str(ast)) | |
92 | if not types._list_Q(ast): | |
93 | return eval_ast(ast, env) | |
90ca8485 | 94 | if len(ast) == 0: return ast |
11b4be99 JM |
95 | |
96 | # apply list | |
97 | ast = macroexpand(ast, env) | |
44aef1f4 JM |
98 | if not types._list_Q(ast): |
99 | return eval_ast(ast, env) | |
11b4be99 JM |
100 | if len(ast) == 0: return ast |
101 | a0 = ast[0] | |
102 | if isinstance(a0, MalSym): | |
103 | a0sym = a0.value | |
104 | else: | |
105 | a0sym = u"__<*fn*>__" | |
106 | ||
107 | if u"def!" == a0sym: | |
108 | a1, a2 = ast[1], ast[2] | |
109 | res = EVAL(a2, env) | |
110 | return env.set(a1, res) | |
111 | elif u"let*" == a0sym: | |
112 | a1, a2 = ast[1], ast[2] | |
113 | let_env = Env(env) | |
114 | for i in range(0, len(a1), 2): | |
115 | let_env.set(a1[i], EVAL(a1[i+1], let_env)) | |
116 | ast = a2 | |
117 | env = let_env # Continue loop (TCO) | |
118 | elif u"quote" == a0sym: | |
119 | return ast[1] | |
fbfe6784 NB |
120 | elif u"quasiquoteexpand" == a0sym: |
121 | return quasiquote(ast[1]) | |
11b4be99 JM |
122 | elif u"quasiquote" == a0sym: |
123 | ast = quasiquote(ast[1]) # Continue loop (TCO) | |
124 | elif u"defmacro!" == a0sym: | |
125 | func = EVAL(ast[2], env) | |
126 | func.ismacro = True | |
127 | return env.set(ast[1], func) | |
128 | elif u"macroexpand" == a0sym: | |
129 | return macroexpand(ast[1], env) | |
130 | elif u"try*" == a0sym: | |
dd7a4f55 JM |
131 | if len(ast) < 3: |
132 | return EVAL(ast[1], env); | |
11b4be99 JM |
133 | a1, a2 = ast[1], ast[2] |
134 | a20 = a2[0] | |
135 | if isinstance(a20, MalSym): | |
136 | if a20.value == u"catch*": | |
137 | try: | |
138 | return EVAL(a1, env); | |
139 | except types.MalException as exc: | |
140 | exc = exc.object | |
141 | catch_env = Env(env, _list(a2[1]), _list(exc)) | |
142 | return EVAL(a2[2], catch_env) | |
143 | except Exception as exc: | |
144 | exc = MalStr(unicode("%s" % exc)) | |
145 | catch_env = Env(env, _list(a2[1]), _list(exc)) | |
146 | return EVAL(a2[2], catch_env) | |
147 | return EVAL(a1, env); | |
148 | elif u"do" == a0sym: | |
149 | if len(ast) == 0: | |
150 | return nil | |
151 | elif len(ast) > 1: | |
152 | eval_ast(ast.slice2(1, len(ast)-1), env) | |
153 | ast = ast[-1] # Continue loop (TCO) | |
154 | elif u"if" == a0sym: | |
155 | a1, a2 = ast[1], ast[2] | |
156 | cond = EVAL(a1, env) | |
157 | if cond is nil or cond is false: | |
158 | if len(ast) > 3: ast = ast[3] # Continue loop (TCO) | |
159 | else: return nil | |
160 | else: | |
161 | ast = a2 # Continue loop (TCO) | |
162 | elif u"fn*" == a0sym: | |
163 | a1, a2 = ast[1], ast[2] | |
164 | return MalFunc(None, a2, env, a1, EVAL) | |
165 | else: | |
166 | el = eval_ast(ast, env) | |
167 | f = el.values[0] | |
168 | if isinstance(f, MalFunc): | |
169 | if f.ast: | |
170 | ast = f.ast | |
fbfe6784 | 171 | env = f.gen_env(el.rest()) # Continue loop (TCO) |
11b4be99 JM |
172 | else: |
173 | return f.apply(el.rest()) | |
174 | else: | |
175 | raise Exception("%s is not callable" % f) | |
176 | ||
177 | ||
178 | def PRINT(exp): | |
179 | return printer._pr_str(exp) | |
180 | ||
181 | # repl | |
182 | class MalEval(MalFunc): | |
183 | def apply(self, args): | |
184 | return self.EvalFunc(args[0], self.env) | |
185 | ||
186 | def entry_point(argv): | |
187 | repl_env = Env() | |
188 | def REP(str, env): | |
189 | return PRINT(EVAL(READ(str), env)) | |
190 | ||
191 | # core.py: defined using python | |
192 | for k, v in core.ns.items(): | |
193 | repl_env.set(_symbol(unicode(k)), MalFunc(v)) | |
194 | repl_env.set(types._symbol(u'eval'), | |
195 | MalEval(None, env=repl_env, EvalFunc=EVAL)) | |
196 | mal_args = [] | |
197 | if len(argv) >= 3: | |
198 | for a in argv[2:]: mal_args.append(MalStr(unicode(a))) | |
199 | repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args)) | |
200 | ||
201 | # core.mal: defined using the language itself | |
7f714804 | 202 | REP("(def! *host-language* \"rpython\")", repl_env) |
11b4be99 | 203 | REP("(def! not (fn* (a) (if a false true)))", repl_env) |
e6d41de4 | 204 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env) |
11b4be99 | 205 | 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)))))))", repl_env) |
11b4be99 JM |
206 | |
207 | if len(argv) >= 2: | |
208 | REP('(load-file "' + argv[1] + '")', repl_env) | |
209 | return 0 | |
210 | ||
211 | REP("(println (str \"Mal [\" *host-language* \"]\"))", repl_env) | |
212 | while True: | |
213 | try: | |
214 | line = mal_readline.readline("user> ") | |
215 | if line == "": continue | |
216 | print(REP(line, repl_env)) | |
217 | except EOFError as e: | |
218 | break | |
219 | except reader.Blank: | |
220 | continue | |
221 | except types.MalException as e: | |
222 | print(u"Error: %s" % printer._pr_str(e.object, False)) | |
ab02c5bb JM |
223 | except Exception as e: |
224 | print("Error: %s" % e) | |
23fa1b11 JM |
225 | if IS_RPYTHON: |
226 | llop.debug_print_traceback(lltype.Void) | |
227 | else: | |
228 | print("".join(traceback.format_exception(*sys.exc_info()))) | |
11b4be99 JM |
229 | return 0 |
230 | ||
231 | # _____ Define and setup target ___ | |
232 | def target(*args): | |
233 | return entry_point | |
234 | ||
235 | # Just run entry_point if not RPython compilation | |
236 | import sys | |
237 | if not sys.argv[0].endswith('rpython'): | |
238 | entry_point(sys.argv) |