| 1 | import sys, traceback |
| 2 | import mal_readline |
| 3 | import mal_types as types |
| 4 | from mal_types import (MalSym, MalInt, MalStr, |
| 5 | nil, true, false, _symbol, _keywordu, |
| 6 | MalList, _list, MalVector, MalHashMap, MalFunc) |
| 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) |
| 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) |
| 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 | # print |
| 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 |
| 133 | except types.MalException as e: |
| 134 | print(u"Error: %s" % printer._pr_str(e.object, False)) |
| 135 | except Exception as e: |
| 136 | print("Error: %s" % e) |
| 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) |