f5cf5237 |
1 | import rdstdin, tables, sequtils, os, types, reader, printer, env, core |
2 | |
3 | proc read(str: string): MalType = str.read_str |
4 | |
5 | proc is_pair(x: MalType): bool = |
6 | x.kind in {List, Vector} and x.list.len > 0 |
7 | |
8 | proc quasiquote(ast: MalType): MalType = |
9 | if not ast.is_pair: |
10 | return list(symbol "quote", ast) |
11 | elif ast.list[0] == symbol "unquote": |
12 | return ast.list[1] |
13 | elif ast.list[0].is_pair and ast.list[0].list[0] == symbol "splice-unquote": |
14 | return list(symbol "concat", ast.list[0].list[1], |
3f429bf4 |
15 | quasiquote(list ast.list[1 .. ^1])) |
f5cf5237 |
16 | else: |
3f429bf4 |
17 | return list(symbol "cons", quasiquote(ast.list[0]), quasiquote(list(ast.list[1 .. ^1]))) |
f5cf5237 |
18 | |
19 | proc is_macro_call(ast: MalType, env: Env): bool = |
20 | ast.kind == List and ast.list[0].kind == Symbol and |
21 | env.find(ast.list[0].str) != nil and env.get(ast.list[0].str).macro_q |
22 | |
23 | proc macroexpand(ast: MalType, env: Env): MalType = |
24 | result = ast |
25 | while result.is_macro_call(env): |
26 | let mac = env.get(result.list[0].str) |
3f429bf4 |
27 | result = mac.malfun.fn(result.list[1 .. ^1]).macroexpand(env) |
f5cf5237 |
28 | |
2800f318 |
29 | proc eval(ast: MalType, env: Env): MalType |
f5cf5237 |
30 | |
31 | proc eval_ast(ast: MalType, env: var Env): MalType = |
32 | case ast.kind |
33 | of Symbol: |
34 | result = env.get(ast.str) |
35 | of List: |
36 | result = list ast.list.mapIt(MalType, it.eval(env)) |
37 | of Vector: |
38 | result = vector ast.list.mapIt(MalType, it.eval(env)) |
39 | of HashMap: |
40 | result = hash_map() |
41 | for k, v in ast.hash_map.pairs: |
42 | result.hash_map[k] = v.eval(env) |
43 | else: |
44 | result = ast |
45 | |
2800f318 |
46 | proc eval(ast: MalType, env: Env): MalType = |
47 | var ast = ast |
48 | var env = env |
49 | |
f5cf5237 |
50 | template defaultApply = |
51 | let el = ast.eval_ast(env) |
52 | let f = el.list[0] |
53 | case f.kind |
54 | of MalFun: |
55 | ast = f.malfun.ast |
3f429bf4 |
56 | env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. ^1])) |
f5cf5237 |
57 | else: |
3f429bf4 |
58 | return f.fun(el.list[1 .. ^1]) |
f5cf5237 |
59 | |
f5cf5237 |
60 | while true: |
61 | if ast.kind != List: return ast.eval_ast(env) |
62 | |
63 | ast = ast.macroexpand(env) |
6c94cd3e |
64 | if ast.kind != List: return ast.eval_ast(env) |
f5cf5237 |
65 | if ast.list.len == 0: return ast |
66 | |
67 | let a0 = ast.list[0] |
68 | case a0.kind |
69 | of Symbol: |
70 | case a0.str |
71 | of "def!": |
72 | let |
73 | a1 = ast.list[1] |
74 | a2 = ast.list[2] |
75 | return env.set(a1.str, a2.eval(env)) |
76 | |
77 | of "let*": |
78 | let |
79 | a1 = ast.list[1] |
80 | a2 = ast.list[2] |
4dab2092 |
81 | var let_env = env |
f5cf5237 |
82 | case a1.kind |
83 | of List, Vector: |
84 | for i in countup(0, a1.list.high, 2): |
85 | let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) |
86 | else: raise newException(ValueError, "Illegal kind in let*") |
87 | ast = a2 |
88 | env = let_env |
89 | # Continue loop (TCO) |
90 | |
91 | of "quote": |
92 | return ast.list[1] |
93 | |
94 | of "quasiquote": |
95 | ast = ast.list[1].quasiquote |
96 | # Continue loop (TCO) |
97 | |
98 | of "defmacro!": |
99 | var fun = ast.list[2].eval(env) |
100 | fun.malfun.is_macro = true |
101 | return env.set(ast.list[1].str, fun) |
102 | |
103 | of "macroexpand": |
104 | return ast.list[1].macroexpand(env) |
105 | |
106 | of "do": |
107 | let last = ast.list.high |
4dab2092 |
108 | discard (list ast.list[1 .. <last]).eval_ast(env) |
2800f318 |
109 | ast = ast.list[last] |
f5cf5237 |
110 | # Continue loop (TCO) |
111 | |
112 | of "if": |
113 | let |
114 | a1 = ast.list[1] |
115 | a2 = ast.list[2] |
116 | cond = a1.eval(env) |
117 | |
118 | if cond.kind in {Nil, False}: |
119 | if ast.list.len > 3: ast = ast.list[3] |
120 | else: ast = nilObj |
121 | else: ast = a2 |
122 | |
123 | of "fn*": |
124 | let |
125 | a1 = ast.list[1] |
126 | a2 = ast.list[2] |
127 | var env2 = env |
128 | let fn = proc(a: varargs[MalType]): MalType = |
129 | var newEnv = initEnv(env2, a1, list(a)) |
130 | a2.eval(newEnv) |
2800f318 |
131 | return malfun(fn, a2, a1, env) |
f5cf5237 |
132 | |
133 | else: |
134 | defaultApply() |
135 | |
136 | else: |
137 | defaultApply() |
138 | |
139 | proc print(exp: MalType): string = exp.pr_str |
140 | |
141 | var repl_env = initEnv() |
142 | |
143 | for k, v in ns.items: |
144 | repl_env.set(k, v) |
145 | repl_env.set("eval", fun(proc(xs: varargs[MalType]): MalType = eval(xs[0], repl_env))) |
146 | var ps = commandLineParams() |
147 | repl_env.set("*ARGV*", list((if paramCount() > 1: ps[1..ps.high] else: @[]).map(str))) |
148 | |
149 | |
150 | # core.nim: defined using nim |
151 | proc rep(str: string): string {.discardable.} = |
152 | str.read.eval(repl_env).print |
153 | |
154 | # core.mal: defined using mal itself |
155 | rep "(def! not (fn* (a) (if a false true)))" |
156 | rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" |
157 | 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)))))))" |
158 | rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" |
159 | |
160 | if paramCount() >= 1: |
161 | rep "(load-file \"" & paramStr(1) & "\")" |
162 | quit() |
163 | |
164 | while true: |
165 | try: |
166 | let line = readLineFromStdin("user> ") |
167 | echo line.rep |
168 | except Blank: discard |
169 | except: |
170 | echo getCurrentExceptionMsg() |
171 | echo getCurrentException().getStackTrace() |