Commit | Line | Data |
---|---|---|
5a159ae7 JM |
1 | using System; |
2 | using System.IO; | |
3 | using System.Collections; | |
4 | using System.Collections.Generic; | |
5 | using Mal; | |
6 | using MalVal = Mal.types.MalVal; | |
7 | using MalString = Mal.types.MalString; | |
8 | using MalSymbol = Mal.types.MalSymbol; | |
c3b508af | 9 | using MalInt = Mal.types.MalInt; |
5a159ae7 JM |
10 | using MalList = Mal.types.MalList; |
11 | using MalVector = Mal.types.MalVector; | |
12 | using MalHashMap = Mal.types.MalHashMap; | |
c3b508af | 13 | using MalFunc = Mal.types.MalFunc; |
5a159ae7 JM |
14 | using Env = Mal.env.Env; |
15 | ||
16 | namespace Mal { | |
8cb5cda4 | 17 | class step7_quote { |
5a159ae7 JM |
18 | // read |
19 | static MalVal READ(string str) { | |
20 | return reader.read_str(str); | |
21 | } | |
22 | ||
23 | // eval | |
24 | public static bool is_pair(MalVal x) { | |
25 | return x is MalList && ((MalList)x).size() > 0; | |
26 | } | |
27 | ||
28 | public static MalVal quasiquote(MalVal ast) { | |
29 | if (!is_pair(ast)) { | |
30 | return new MalList(new MalSymbol("quote"), ast); | |
31 | } else { | |
32 | MalVal a0 = ((MalList)ast)[0]; | |
33 | if ((a0 is MalSymbol) && | |
34 | (((MalSymbol)a0).getName() == "unquote")) { | |
35 | return ((MalList)ast)[1]; | |
36 | } else if (is_pair(a0)) { | |
37 | MalVal a00 = ((MalList)a0)[0]; | |
38 | if ((a00 is MalSymbol) && | |
39 | (((MalSymbol)a00).getName() == "splice-unquote")) { | |
40 | return new MalList(new MalSymbol("concat"), | |
41 | ((MalList)a0)[1], | |
42 | quasiquote(((MalList)ast).rest())); | |
43 | } | |
44 | } | |
45 | return new MalList(new MalSymbol("cons"), | |
46 | quasiquote(a0), | |
47 | quasiquote(((MalList)ast).rest())); | |
48 | } | |
49 | } | |
50 | ||
51 | static MalVal eval_ast(MalVal ast, Env env) { | |
52 | if (ast is MalSymbol) { | |
b8ee29b2 | 53 | return env.get((MalSymbol)ast); |
5a159ae7 JM |
54 | } else if (ast is MalList) { |
55 | MalList old_lst = (MalList)ast; | |
56 | MalList new_lst = ast.list_Q() ? new MalList() | |
57 | : (MalList)new MalVector(); | |
58 | foreach (MalVal mv in old_lst.getValue()) { | |
59 | new_lst.conj_BANG(EVAL(mv, env)); | |
60 | } | |
61 | return new_lst; | |
62 | } else if (ast is MalHashMap) { | |
63 | var new_dict = new Dictionary<string, MalVal>(); | |
64 | foreach (var entry in ((MalHashMap)ast).getValue()) { | |
65 | new_dict.Add(entry.Key, EVAL((MalVal)entry.Value, env)); | |
66 | } | |
67 | return new MalHashMap(new_dict); | |
68 | } else { | |
69 | return ast; | |
70 | } | |
71 | } | |
72 | ||
73 | ||
74 | static MalVal EVAL(MalVal orig_ast, Env env) { | |
75 | MalVal a0, a1, a2, res; | |
76 | MalList el; | |
77 | ||
78 | while (true) { | |
79 | ||
b8ee29b2 | 80 | //Console.WriteLine("EVAL: " + printer._pr_str(orig_ast, true)); |
5a159ae7 JM |
81 | if (!orig_ast.list_Q()) { |
82 | return eval_ast(orig_ast, env); | |
83 | } | |
84 | ||
85 | // apply list | |
86 | MalList ast = (MalList)orig_ast; | |
87 | if (ast.size() == 0) { return ast; } | |
88 | a0 = ast[0]; | |
89 | ||
90 | String a0sym = a0 is MalSymbol ? ((MalSymbol)a0).getName() | |
91 | : "__<*fn*>__"; | |
92 | ||
93 | switch (a0sym) { | |
94 | case "def!": | |
95 | a1 = ast[1]; | |
96 | a2 = ast[2]; | |
97 | res = EVAL(a2, env); | |
b8ee29b2 | 98 | env.set((MalSymbol)a1, res); |
5a159ae7 JM |
99 | return res; |
100 | case "let*": | |
101 | a1 = ast[1]; | |
102 | a2 = ast[2]; | |
103 | MalSymbol key; | |
104 | MalVal val; | |
105 | Env let_env = new Env(env); | |
106 | for(int i=0; i<((MalList)a1).size(); i+=2) { | |
107 | key = (MalSymbol)((MalList)a1)[i]; | |
108 | val = ((MalList)a1)[i+1]; | |
b8ee29b2 | 109 | let_env.set(key, EVAL(val, let_env)); |
5a159ae7 | 110 | } |
6301e0b6 JM |
111 | orig_ast = a2; |
112 | env = let_env; | |
113 | break; | |
5a159ae7 JM |
114 | case "quote": |
115 | return ast[1]; | |
116 | case "quasiquote": | |
6301e0b6 JM |
117 | orig_ast = quasiquote(ast[1]); |
118 | break; | |
5a159ae7 JM |
119 | case "do": |
120 | eval_ast(ast.slice(1, ast.size()-1), env); | |
121 | orig_ast = ast[ast.size()-1]; | |
122 | break; | |
123 | case "if": | |
124 | a1 = ast[1]; | |
125 | MalVal cond = EVAL(a1, env); | |
126 | if (cond == Mal.types.Nil || cond == Mal.types.False) { | |
127 | // eval false slot form | |
128 | if (ast.size() > 3) { | |
129 | orig_ast = ast[3]; | |
130 | } else { | |
131 | return Mal.types.Nil; | |
132 | } | |
133 | } else { | |
134 | // eval true slot form | |
135 | orig_ast = ast[2]; | |
136 | } | |
137 | break; | |
138 | case "fn*": | |
139 | MalList a1f = (MalList)ast[1]; | |
140 | MalVal a2f = ast[2]; | |
141 | Env cur_env = env; | |
c3b508af | 142 | return new MalFunc(a2f, env, a1f, |
5a159ae7 JM |
143 | args => EVAL(a2f, new Env(cur_env, a1f, args)) ); |
144 | default: | |
145 | el = (MalList)eval_ast(ast, env); | |
c3b508af | 146 | var f = (MalFunc)el[0]; |
5a159ae7 JM |
147 | MalVal fnast = f.getAst(); |
148 | if (fnast != null) { | |
149 | orig_ast = fnast; | |
150 | env = f.genEnv(el.rest()); | |
151 | } else { | |
152 | return f.apply(el.rest()); | |
153 | } | |
154 | break; | |
155 | } | |
156 | ||
157 | } | |
158 | } | |
159 | ||
160 | ||
161 | static string PRINT(MalVal exp) { | |
162 | return printer._pr_str(exp, true); | |
163 | } | |
164 | ||
86b689f3 | 165 | // repl |
5a159ae7 | 166 | static void Main(string[] args) { |
c3b508af JM |
167 | var repl_env = new Mal.env.Env(null); |
168 | Func<string, MalVal> RE = (string str) => EVAL(READ(str), repl_env); | |
5a159ae7 | 169 | |
8cb5cda4 | 170 | // core.cs: defined using C# |
8cb5cda4 | 171 | foreach (var entry in core.ns) { |
b8ee29b2 | 172 | repl_env.set(new MalSymbol(entry.Key), entry.Value); |
5a159ae7 | 173 | } |
b8ee29b2 JM |
174 | repl_env.set(new MalSymbol("eval"), new MalFunc( |
175 | a => EVAL(a[0], repl_env))); | |
dbbac62f | 176 | int fileIdx = 0; |
aaba2493 JM |
177 | if (args.Length > 0 && args[0] == "--raw") { |
178 | Mal.readline.mode = Mal.readline.Mode.Raw; | |
dbbac62f | 179 | fileIdx = 1; |
aaba2493 | 180 | } |
86b689f3 | 181 | MalList _argv = new MalList(); |
dbbac62f | 182 | for (int i=fileIdx+1; i < args.Length; i++) { |
86b689f3 JM |
183 | _argv.conj_BANG(new MalString(args[i])); |
184 | } | |
b8ee29b2 | 185 | repl_env.set(new MalSymbol("*ARGV*"), _argv); |
5a159ae7 | 186 | |
8cb5cda4 | 187 | // core.mal: defined using the language itself |
c3b508af JM |
188 | RE("(def! not (fn* (a) (if a false true)))"); |
189 | RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); | |
5a159ae7 | 190 | |
5a159ae7 | 191 | if (args.Length > fileIdx) { |
c3b508af | 192 | RE("(load-file \"" + args[fileIdx] + "\")"); |
5a159ae7 JM |
193 | return; |
194 | } | |
c3b508af | 195 | |
86b689f3 | 196 | // repl loop |
5a159ae7 JM |
197 | while (true) { |
198 | string line; | |
199 | try { | |
c3b508af | 200 | line = Mal.readline.Readline("user> "); |
5a159ae7 | 201 | if (line == null) { break; } |
c3b508af | 202 | if (line == "") { continue; } |
5a159ae7 JM |
203 | } catch (IOException e) { |
204 | Console.WriteLine("IOException: " + e.Message); | |
205 | break; | |
206 | } | |
207 | try { | |
c3b508af | 208 | Console.WriteLine(PRINT(RE(line))); |
5a159ae7 JM |
209 | } catch (Mal.types.MalContinue) { |
210 | continue; | |
5a159ae7 JM |
211 | } catch (Exception e) { |
212 | Console.WriteLine("Error: " + e.Message); | |
213 | Console.WriteLine(e.StackTrace); | |
214 | continue; | |
215 | } | |
216 | } | |
217 | } | |
218 | } | |
219 | } |