Commit | Line | Data |
---|---|---|
219f5239 JM |
1 | import sys, traceback |
2 | import mal_readline | |
3 | import mal_types as types | |
8855a05a JM |
4 | from mal_types import (MalSym, MalInt, MalStr, |
5 | nil, true, false, _symbol, _keywordu, | |
6 | MalList, _list, MalVector, MalHashMap, MalFunc) | |
219f5239 JM |
7 | import reader, printer |
8 | from env import Env | |
9 | import core | |
10 | ||
11 | # read | |
12 | def READ(str): | |
13 | return reader.read_str(str) | |
14 | ||
15 | # eval | |
16 | def eval_ast(ast, env): | |
17 | if types._symbol_Q(ast): | |
18 | assert isinstance(ast, MalSym) | |
19 | return env.get(ast) | |
20 | elif types._list_Q(ast): | |
21 | res = [] | |
22 | for a in ast.values: | |
23 | res.append(EVAL(a, env)) | |
24 | return MalList(res) | |
8855a05a JM |
25 | elif types._vector_Q(ast): |
26 | res = [] | |
27 | for a in ast.values: | |
28 | res.append(EVAL(a, env)) | |
29 | return MalVector(res) | |
30 | elif types._hash_map_Q(ast): | |
31 | new_dct = {} | |
32 | for k in ast.dct.keys(): | |
33 | new_dct[k] = EVAL(ast.dct[k], env) | |
34 | return MalHashMap(new_dct) | |
219f5239 JM |
35 | else: |
36 | return ast # primitive value, return unchanged | |
37 | ||
38 | def EVAL(ast, env): | |
39 | while True: | |
40 | #print("EVAL %s" % printer._pr_str(ast)) | |
41 | if not types._list_Q(ast): | |
42 | return eval_ast(ast, env) | |
43 | ||
44 | # apply list | |
45 | if len(ast) == 0: return ast | |
46 | a0 = ast[0] | |
47 | if isinstance(a0, MalSym): | |
48 | a0sym = a0.value | |
49 | else: | |
50 | a0sym = u"__<*fn*>__" | |
51 | ||
52 | if u"def!" == a0sym: | |
53 | a1, a2 = ast[1], ast[2] | |
54 | res = EVAL(a2, env) | |
55 | return env.set(a1, res) | |
56 | elif u"let*" == a0sym: | |
57 | a1, a2 = ast[1], ast[2] | |
58 | let_env = Env(env) | |
59 | for i in range(0, len(a1), 2): | |
60 | let_env.set(a1[i], EVAL(a1[i+1], let_env)) | |
61 | ast = a2 | |
62 | env = let_env # Continue loop (TCO) | |
63 | elif u"do" == a0sym: | |
64 | if len(ast) == 0: | |
65 | return nil | |
66 | elif len(ast) > 1: | |
67 | eval_ast(ast.slice2(1, len(ast)-1), env) | |
68 | ast = ast[-1] # Continue loop (TCO) | |
69 | elif u"if" == a0sym: | |
70 | a1, a2 = ast[1], ast[2] | |
71 | cond = EVAL(a1, env) | |
72 | if cond is nil or cond is false: | |
73 | if len(ast) > 3: ast = ast[3] # Continue loop (TCO) | |
74 | else: return nil | |
75 | else: | |
76 | ast = a2 # Continue loop (TCO) | |
77 | elif u"fn*" == a0sym: | |
78 | a1, a2 = ast[1], ast[2] | |
79 | return MalFunc(None, a2, env, a1, EVAL) | |
80 | else: | |
81 | el = eval_ast(ast, env) | |
82 | f = el.values[0] | |
83 | if isinstance(f, MalFunc): | |
84 | if f.ast: | |
85 | ast = f.ast | |
86 | env = f.gen_env(el.rest()) # Continue loop (TCO) | |
87 | else: | |
88 | return f.apply(el.rest()) | |
89 | else: | |
90 | raise Exception("%s is not callable" % f) | |
91 | ||
92 | ||
93 | def PRINT(exp): | |
94 | return printer._pr_str(exp) | |
95 | ||
96 | # repl | |
97 | class MalEval(MalFunc): | |
98 | def apply(self, args): | |
99 | return self.EvalFunc(args[0], self.env) | |
100 | ||
101 | def entry_point(argv): | |
102 | repl_env = Env() | |
103 | def REP(str, env): | |
104 | return PRINT(EVAL(READ(str), env)) | |
105 | ||
106 | # core.py: defined using python | |
107 | for k, v in core.ns.items(): | |
108 | repl_env.set(_symbol(unicode(k)), MalFunc(v)) | |
109 | repl_env.set(types._symbol(u'eval'), | |
110 | MalEval(None, env=repl_env, EvalFunc=EVAL)) | |
111 | mal_args = [] | |
112 | if len(argv) >= 3: | |
113 | for a in argv[2:]: mal_args.append(MalStr(unicode(a))) | |
114 | repl_env.set(_symbol(u'*ARGV*'), MalList(mal_args)) | |
115 | ||
116 | # core.mal: defined using the language itself | |
117 | REP("(def! not (fn* (a) (if a false true)))", repl_env) | |
118 | REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", repl_env) | |
119 | ||
120 | if len(argv) >= 2: | |
121 | REP('(load-file "' + argv[1] + '")', repl_env) | |
122 | return 0 | |
123 | ||
124 | while True: | |
125 | try: | |
126 | line = mal_readline.readline("user> ") | |
127 | if line == "": continue | |
128 | print(REP(line, repl_env)) | |
129 | except EOFError as e: | |
130 | break | |
131 | except reader.Blank: | |
132 | continue | |
ab02c5bb JM |
133 | except types.MalException as e: |
134 | print(u"Error: %s" % printer._pr_str(e.object, False)) | |
219f5239 | 135 | except Exception as e: |
ab02c5bb | 136 | print("Error: %s" % e) |
219f5239 JM |
137 | #print("".join(traceback.format_exception(*sys.exc_info()))) |
138 | return 0 | |
139 | ||
140 | # _____ Define and setup target ___ | |
141 | def target(*args): | |
142 | return entry_point | |
143 | ||
144 | # Just run entry_point if not RPython compilation | |
145 | import sys | |
146 | if not sys.argv[0].endswith('rpython'): | |
147 | entry_point(sys.argv) |