Commit | Line | Data |
---|---|---|
034e82ad DM |
1 | def READ(str string) MalVal { |
2 | return read_str(str) | |
3 | } | |
4 | ||
5 | def eval_ast(ast MalVal, env Env) MalVal { | |
6 | if ast is MalSymbol { | |
7 | return env.get(ast as MalSymbol) | |
8 | } else if ast is MalList { | |
9 | return MalList.new((ast as MalList).val.map<MalVal>(e => EVAL(e, env))) | |
10 | } else if ast is MalVector { | |
11 | return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env))) | |
12 | } else if ast is MalHashMap { | |
13 | var result List<MalVal> = [] | |
14 | (ast as MalHashMap).val.each((k string, v MalVal) => { | |
15 | result.append(EVAL(MalVal.fromHashKey(k), env)) | |
16 | result.append(EVAL(v, env)) | |
17 | }) | |
18 | return MalHashMap.fromList(result) | |
19 | } else { | |
20 | return ast | |
21 | } | |
22 | } | |
23 | ||
24 | def EVAL(ast MalVal, env Env) MalVal { | |
25 | while true { | |
26 | if !(ast is MalList) { return eval_ast(ast, env) } | |
27 | const astList = ast as MalList | |
28 | if astList.isEmpty { return ast } | |
29 | const a0sym = astList[0] as MalSymbol | |
30 | if a0sym.val == "def!" { | |
31 | return env.set(astList[1] as MalSymbol, EVAL(astList[2], env)) | |
32 | } else if a0sym.val == "let*" { | |
33 | var letenv = Env.new(env) | |
34 | const assigns = astList[1] as MalSequential | |
35 | for i = 0; i < assigns.count; i += 2 { | |
36 | letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv)) | |
37 | } | |
38 | ast = astList[2] | |
39 | env = letenv | |
40 | continue # TCO | |
41 | } else if a0sym.val == "do" { | |
42 | const parts = astList.val.slice(1) | |
43 | eval_ast(MalList.new(parts.slice(0, parts.count - 1)), env) | |
44 | ast = parts[parts.count - 1] | |
45 | continue # TCO | |
46 | } else if a0sym.val == "if" { | |
47 | const condRes = EVAL(astList[1], env) | |
48 | if condRes is MalNil || condRes is MalFalse { | |
49 | ast = astList.count > 3 ? astList[3] : gNil | |
50 | } else { | |
51 | ast = astList[2] | |
52 | } | |
53 | continue # TCO | |
54 | } else if a0sym.val == "fn*" { | |
55 | const argsNames = astList[1] as MalSequential | |
56 | return MalFunc.new(astList[2], argsNames, env, (args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames.val, args))) | |
57 | } else { | |
58 | const evaledList = eval_ast(ast, env) as MalList | |
59 | const fn = evaledList[0] | |
60 | const callArgs = evaledList.val.slice(1) | |
61 | if fn is MalNativeFunc { | |
62 | return (fn as MalNativeFunc).call(callArgs) | |
63 | } else if fn is MalFunc { | |
64 | const f = fn as MalFunc | |
65 | ast = f.ast | |
66 | env = Env.new(f.env, f.params.val, callArgs) | |
67 | continue # TCO | |
68 | } else { | |
69 | throw MalError.new("Expected function as head of list") | |
70 | } | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | def PRINT(exp MalVal) string { | |
76 | return exp?.print(true) | |
77 | } | |
78 | ||
79 | var repl_env = Env.new(null) | |
80 | ||
81 | def RE(str string) MalVal { | |
82 | return EVAL(READ(str), repl_env) | |
83 | } | |
84 | ||
85 | def REP(str string) string { | |
86 | return PRINT(RE(str)) | |
87 | } | |
88 | ||
89 | @entry | |
90 | def main { | |
91 | # core.sk: defined using Skew | |
92 | ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func))) | |
93 | repl_env.set(MalSymbol.new("*ARGV*"), MalList.new(argv.isEmpty ? [] : argv.slice(1).map<MalVal>(e => MalString.new(e)))) | |
94 | ||
95 | # core.mal: defined using the language itself | |
96 | RE("(def! not (fn* (a) (if a false true)))") | |
e6d41de4 | 97 | RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))") |
034e82ad DM |
98 | |
99 | if argv.count > 0 { | |
100 | RE("(load-file \"" + argv[0] + "\")") | |
101 | return | |
102 | } | |
103 | ||
104 | var line string | |
105 | while (line = readLine("user> ")) != null { | |
106 | if line == "" { continue } | |
107 | try { | |
108 | printLn(REP(line)) | |
109 | } | |
110 | catch e MalError { | |
111 | printLn("Error: \(e.message)") | |
112 | } | |
113 | catch e Error { | |
114 | printLn("Error: \(e.message)") | |
115 | } | |
116 | } | |
117 | } |