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