5dfcbb6b |
1 | import rdstdin, tables, sequtils, types, reader, printer, env, core |
2 | |
3 | proc read(str: string): MalType = str.read_str |
4 | |
fc7f8a4b |
5 | proc eval(ast: MalType, env: Env): MalType |
5dfcbb6b |
6 | |
7 | proc eval_ast(ast: MalType, env: var Env): MalType = |
8 | case ast.kind |
9 | of Symbol: |
3603af96 |
10 | result = env.get(ast.str) |
5dfcbb6b |
11 | of List: |
41558f01 |
12 | result = list ast.list.mapIt(it.eval(env)) |
5dfcbb6b |
13 | of Vector: |
41558f01 |
14 | result = vector ast.list.mapIt(it.eval(env)) |
5dfcbb6b |
15 | of HashMap: |
16 | result = hash_map() |
17 | for k, v in ast.hash_map.pairs: |
18 | result.hash_map[k] = v.eval(env) |
19 | else: |
20 | result = ast |
21 | |
fc7f8a4b |
22 | proc eval(ast: MalType, env: Env): MalType = |
2800f318 |
23 | var ast = ast |
fc7f8a4b |
24 | var env = env |
2800f318 |
25 | |
5dfcbb6b |
26 | template defaultApply = |
27 | let el = ast.eval_ast(env) |
28 | let f = el.list[0] |
29 | case f.kind |
30 | of MalFun: |
31 | ast = f.malfun.ast |
3f429bf4 |
32 | env = initEnv(f.malfun.env, f.malfun.params, list(el.list[1 .. ^1])) |
5dfcbb6b |
33 | else: |
3f429bf4 |
34 | return f.fun(el.list[1 .. ^1]) |
5dfcbb6b |
35 | |
5dfcbb6b |
36 | while true: |
2800f318 |
37 | if ast.kind != List: return ast.eval_ast(env) |
920963d6 |
38 | if ast.list.len == 0: return ast |
5dfcbb6b |
39 | |
40 | let a0 = ast.list[0] |
41 | case a0.kind |
42 | of Symbol: |
3603af96 |
43 | case a0.str |
5dfcbb6b |
44 | of "def!": |
45 | let |
46 | a1 = ast.list[1] |
47 | a2 = ast.list[2] |
3603af96 |
48 | return env.set(a1.str, a2.eval(env)) |
5dfcbb6b |
49 | |
50 | of "let*": |
51 | let |
52 | a1 = ast.list[1] |
53 | a2 = ast.list[2] |
fc7f8a4b |
54 | var let_env = initEnv(env) |
5dfcbb6b |
55 | case a1.kind |
56 | of List, Vector: |
57 | for i in countup(0, a1.list.high, 2): |
f5cf5237 |
58 | let_env.set(a1.list[i].str, a1.list[i+1].eval(let_env)) |
59 | else: raise newException(ValueError, "Illegal kind in let*") |
60 | ast = a2 |
61 | env = let_env |
5dfcbb6b |
62 | # Continue loop (TCO) |
63 | |
64 | of "do": |
65 | let last = ast.list.high |
41558f01 |
66 | discard (list ast.list[1 ..< last]).eval_ast(env) |
2800f318 |
67 | ast = ast.list[last] |
5dfcbb6b |
68 | # Continue loop (TCO) |
69 | |
70 | of "if": |
71 | let |
72 | a1 = ast.list[1] |
73 | a2 = ast.list[2] |
74 | cond = a1.eval(env) |
75 | |
76 | if cond.kind in {Nil, False}: |
77 | if ast.list.len > 3: ast = ast.list[3] |
78 | else: ast = nilObj |
79 | else: ast = a2 |
80 | |
81 | of "fn*": |
82 | let |
83 | a1 = ast.list[1] |
84 | a2 = ast.list[2] |
85 | var env2 = env |
86 | let fn = proc(a: varargs[MalType]): MalType = |
87 | var newEnv = initEnv(env2, a1, list(a)) |
88 | a2.eval(newEnv) |
2800f318 |
89 | return malfun(fn, a2, a1, env) |
5dfcbb6b |
90 | |
91 | else: |
92 | defaultApply() |
93 | |
94 | else: |
95 | defaultApply() |
96 | |
97 | proc print(exp: MalType): string = exp.pr_str |
98 | |
99 | var repl_env = initEnv() |
100 | |
101 | for k, v in ns.items: |
102 | repl_env.set(k, v) |
103 | |
104 | # core.nim: defined using nim |
105 | proc rep(str: string): string = |
106 | str.read.eval(repl_env).print |
107 | |
108 | # core.mal: defined using mal itself |
109 | discard rep "(def! not (fn* (a) (if a false true)))" |
110 | |
111 | while true: |
112 | try: |
113 | let line = readLineFromStdin("user> ") |
114 | echo line.rep |
115 | except: |
116 | echo getCurrentExceptionMsg() |
117 | echo getCurrentException().getStackTrace() |