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