Change quasiquote algorithm
[jackhill/mal.git] / impls / cs / core.cs
1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4 using MalVal = Mal.types.MalVal;
5 using MalConstant = Mal.types.MalConstant;
6 using MalInt = Mal.types.MalInt;
7 using MalSymbol = Mal.types.MalSymbol;
8 using MalString = Mal.types.MalString;
9 using MalList = Mal.types.MalList;
10 using MalVector = Mal.types.MalVector;
11 using MalHashMap = Mal.types.MalHashMap;
12 using MalAtom = Mal.types.MalAtom;
13 using MalFunc = Mal.types.MalFunc;
14
15 namespace Mal {
16 public class core {
17 static MalConstant Nil = Mal.types.Nil;
18 static MalConstant True = Mal.types.True;
19 static MalConstant False = Mal.types.False;
20
21 // Errors/Exceptions
22 static public MalFunc mal_throw = new MalFunc(
23 a => { throw new Mal.types.MalException(a[0]); });
24
25 // Scalar functions
26 static MalFunc nil_Q = new MalFunc(
27 a => a[0] == Nil ? True : False);
28
29 static MalFunc true_Q = new MalFunc(
30 a => a[0] == True ? True : False);
31
32 static MalFunc false_Q = new MalFunc(
33 a => a[0] == False ? True : False);
34
35 static MalFunc symbol_Q = new MalFunc(
36 a => a[0] is MalSymbol ? True : False);
37
38 static MalFunc string_Q = new MalFunc(
39 a => {
40 if (a[0] is MalString) {
41 var s = ((MalString)a[0]).getValue();
42 return (s.Length == 0 || s[0] != '\u029e') ? True : False;
43 } else {
44 return False;
45 }
46 } );
47
48 static MalFunc keyword = new MalFunc(
49 a => {
50 if (a[0] is MalString &&
51 ((MalString)a[0]).getValue()[0] == '\u029e') {
52 return a[0];
53 } else {
54 return new MalString("\u029e" + ((MalString)a[0]).getValue());
55 }
56 } );
57
58 static MalFunc keyword_Q = new MalFunc(
59 a => {
60 if (a[0] is MalString) {
61 var s = ((MalString)a[0]).getValue();
62 return (s.Length > 0 && s[0] == '\u029e') ? True : False;
63 } else {
64 return False;
65 }
66 } );
67
68 static MalFunc number_Q = new MalFunc(
69 a => a[0] is MalInt ? True : False);
70
71 static MalFunc function_Q = new MalFunc(
72 a => a[0] is MalFunc && !((MalFunc)a[0]).isMacro() ? True : False);
73
74 static MalFunc macro_Q = new MalFunc(
75 a => a[0] is MalFunc && ((MalFunc)a[0]).isMacro() ? True : False);
76
77
78 // Number functions
79 static MalFunc time_ms = new MalFunc(
80 a => new MalInt(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond));
81
82 // String functions
83 static public MalFunc pr_str = new MalFunc(
84 a => new MalString(printer._pr_str_args(a, " ", true)) );
85
86 static public MalFunc str = new MalFunc(
87 a => new MalString(printer._pr_str_args(a, "", false)) );
88
89 static public MalFunc prn = new MalFunc(
90 a => {
91 Console.WriteLine(printer._pr_str_args(a, " ", true));
92 return Nil;
93 } );
94
95 static public MalFunc println = new MalFunc(
96 a => {
97 Console.WriteLine(printer._pr_str_args(a, " ", false));
98 return Nil;
99 } );
100
101 static public MalFunc mal_readline = new MalFunc(
102 a => {
103 var line = readline.Readline(((MalString)a[0]).getValue());
104 if (line == null) { return types.Nil; }
105 else { return new MalString(line); }
106 } );
107
108 static public MalFunc read_string = new MalFunc(
109 a => reader.read_str(((MalString)a[0]).getValue()));
110
111 static public MalFunc slurp = new MalFunc(
112 a => new MalString(File.ReadAllText(
113 ((MalString)a[0]).getValue())));
114
115
116 // List/Vector functions
117 static public MalFunc list_Q = new MalFunc(
118 a => a[0].GetType() == typeof(MalList) ? True : False);
119
120 static public MalFunc vector_Q = new MalFunc(
121 a => a[0].GetType() == typeof(MalVector) ? True : False);
122
123 // HashMap functions
124 static public MalFunc hash_map_Q = new MalFunc(
125 a => a[0].GetType() == typeof(MalHashMap) ? True : False);
126
127 static MalFunc contains_Q = new MalFunc(
128 a => {
129 string key = ((MalString)a[1]).getValue();
130 var dict = ((MalHashMap)a[0]).getValue();
131 return dict.ContainsKey(key) ? True : False;
132 });
133
134 static MalFunc assoc = new MalFunc(
135 a => {
136 var new_hm = ((MalHashMap)a[0]).copy();
137 return new_hm.assoc_BANG((MalList)a.slice(1));
138 });
139
140 static MalFunc dissoc = new MalFunc(
141 a => {
142 var new_hm = ((MalHashMap)a[0]).copy();
143 return new_hm.dissoc_BANG((MalList)a.slice(1));
144 });
145
146 static MalFunc get = new MalFunc(
147 a => {
148 string key = ((MalString)a[1]).getValue();
149 if (a[0] == Nil) {
150 return Nil;
151 } else {
152 var dict = ((MalHashMap)a[0]).getValue();
153 return dict.ContainsKey(key) ? dict[key] : Nil;
154 }
155 });
156
157 static MalFunc keys = new MalFunc(
158 a => {
159 var dict = ((MalHashMap)a[0]).getValue();
160 MalList key_lst = new MalList();
161 foreach (var key in dict.Keys) {
162 key_lst.conj_BANG(new MalString(key));
163 }
164 return key_lst;
165 });
166
167 static MalFunc vals = new MalFunc(
168 a => {
169 var dict = ((MalHashMap)a[0]).getValue();
170 MalList val_lst = new MalList();
171 foreach (var val in dict.Values) {
172 val_lst.conj_BANG(val);
173 }
174 return val_lst;
175 });
176
177 // Sequence functions
178 static public MalFunc sequential_Q = new MalFunc(
179 a => a[0] is MalList ? True : False);
180
181 static MalFunc cons = new MalFunc(
182 a => {
183 var lst = new List<MalVal>();
184 lst.Add(a[0]);
185 lst.AddRange(((MalList)a[1]).getValue());
186 return (MalVal)new MalList(lst);
187 });
188
189 static MalFunc concat = new MalFunc(
190 a => {
191 if (a.size() == 0) { return new MalList(); }
192 var lst = new List<MalVal>();
193 lst.AddRange(((MalList)a[0]).getValue());
194 for(int i=1; i<a.size(); i++) {
195 lst.AddRange(((MalList)a[i]).getValue());
196 }
197 return (MalVal)new MalList(lst);
198 });
199
200 static MalFunc nth = new MalFunc(
201 a => {
202 var idx = (int)((MalInt)a[1]).getValue();
203 if (idx < ((MalList)a[0]).size()) {
204 return ((MalList)a[0])[idx];
205 } else {
206 throw new Mal.types.MalException(
207 "nth: index out of range");
208 }
209 });
210
211 static MalFunc first = new MalFunc(
212 a => a[0] == Nil ? Nil : ((MalList)a[0])[0]);
213
214 static MalFunc rest = new MalFunc(
215 a => a[0] == Nil ? new MalList() : ((MalList)a[0]).rest());
216
217 static MalFunc empty_Q = new MalFunc(
218 a => ((MalList)a[0]).size() == 0 ? True : False);
219
220 static MalFunc count = new MalFunc(
221 a => {
222 return (a[0] == Nil)
223 ? new MalInt(0)
224 :new MalInt(((MalList)a[0]).size());
225 });
226
227 static MalFunc conj = new MalFunc(
228 a => {
229 var src_lst = ((MalList)a[0]).getValue();
230 var new_lst = new List<MalVal>();
231 new_lst.AddRange(src_lst);
232 if (a[0] is MalVector) {
233 for(int i=1; i<a.size(); i++) {
234 new_lst.Add(a[i]);
235 }
236 return new MalVector(new_lst);
237 } else {
238 for(int i=1; i<a.size(); i++) {
239 new_lst.Insert(0, a[i]);
240 }
241 return new MalList(new_lst);
242 }
243 });
244
245
246 static MalFunc seq = new MalFunc(
247 a => {
248 if (a[0] == Nil) {
249 return Nil;
250 } else if (a[0] is MalVector) {
251 return (((MalVector)a[0]).size() == 0)
252 ? (MalVal)Nil
253 : new MalList(((MalVector)a[0]).getValue());
254 } else if (a[0] is MalList) {
255 return (((MalList)a[0]).size() == 0)
256 ? Nil
257 : a[0];
258 } else if (a[0] is MalString) {
259 var s = ((MalString)a[0]).getValue();
260 if (s.Length == 0) {
261 return Nil;
262 }
263 var chars_list = new List<MalVal>();
264 foreach (var c in s) {
265 chars_list.Add(new MalString(c.ToString()));
266 }
267 return new MalList(chars_list);
268 }
269 return Nil;
270 });
271
272 // General list related functions
273 static MalFunc apply = new MalFunc(
274 a => {
275 var f = (MalFunc)a[0];
276 var lst = new List<MalVal>();
277 lst.AddRange(a.slice(1,a.size()-1).getValue());
278 lst.AddRange(((MalList)a[a.size()-1]).getValue());
279 return f.apply(new MalList(lst));
280 });
281
282 static MalFunc map = new MalFunc(
283 a => {
284 MalFunc f = (MalFunc) a[0];
285 var src_lst = ((MalList)a[1]).getValue();
286 var new_lst = new List<MalVal>();
287 for(int i=0; i<src_lst.Count; i++) {
288 new_lst.Add(f.apply(new MalList(src_lst[i])));
289 }
290 return new MalList(new_lst);
291 });
292
293
294 // Metadata functions
295 static MalFunc meta = new MalFunc(
296 a => a[0].getMeta());
297
298 static MalFunc with_meta = new MalFunc(
299 a => ((MalVal)a[0]).copy().setMeta(a[1]));
300
301
302 // Atom functions
303 static MalFunc atom_Q = new MalFunc(
304 a => a[0] is MalAtom ? True : False);
305
306 static MalFunc deref = new MalFunc(
307 a => ((MalAtom)a[0]).getValue());
308
309 static MalFunc reset_BANG = new MalFunc(
310 a => ((MalAtom)a[0]).setValue(a[1]));
311
312 static MalFunc swap_BANG = new MalFunc(
313 a => {
314 MalAtom atm = (MalAtom)a[0];
315 MalFunc f = (MalFunc)a[1];
316 var new_lst = new List<MalVal>();
317 new_lst.Add(atm.getValue());
318 new_lst.AddRange(((MalList)a.slice(2)).getValue());
319 return atm.setValue(f.apply(new MalList(new_lst)));
320 });
321
322
323
324 static public Dictionary<string, MalVal> ns =
325 new Dictionary<string, MalVal> {
326 {"=", new MalFunc(
327 a => Mal.types._equal_Q(a[0], a[1]) ? True : False)},
328 {"throw", mal_throw},
329 {"nil?", nil_Q},
330 {"true?", true_Q},
331 {"false?", false_Q},
332 {"symbol", new MalFunc(a => new MalSymbol((MalString)a[0]))},
333 {"symbol?", symbol_Q},
334 {"string?", string_Q},
335 {"keyword", keyword},
336 {"keyword?", keyword_Q},
337 {"number?", number_Q},
338 {"fn?", function_Q},
339 {"macro?", macro_Q},
340
341 {"pr-str", pr_str},
342 {"str", str},
343 {"prn", prn},
344 {"println", println},
345 {"readline", mal_readline},
346 {"read-string", read_string},
347 {"slurp", slurp},
348 {"<", new MalFunc(a => (MalInt)a[0] < (MalInt)a[1])},
349 {"<=", new MalFunc(a => (MalInt)a[0] <= (MalInt)a[1])},
350 {">", new MalFunc(a => (MalInt)a[0] > (MalInt)a[1])},
351 {">=", new MalFunc(a => (MalInt)a[0] >= (MalInt)a[1])},
352 {"+", new MalFunc(a => (MalInt)a[0] + (MalInt)a[1])},
353 {"-", new MalFunc(a => (MalInt)a[0] - (MalInt)a[1])},
354 {"*", new MalFunc(a => (MalInt)a[0] * (MalInt)a[1])},
355 {"/", new MalFunc(a => (MalInt)a[0] / (MalInt)a[1])},
356 {"time-ms", time_ms},
357
358 {"list", new MalFunc(a => new MalList(a.getValue()))},
359 {"list?", list_Q},
360 {"vector", new MalFunc(a => new MalVector(a.getValue()))},
361 {"vector?", vector_Q},
362 {"hash-map", new MalFunc(a => new MalHashMap(a))},
363 {"map?", hash_map_Q},
364 {"contains?", contains_Q},
365 {"assoc", assoc},
366 {"dissoc", dissoc},
367 {"get", get},
368 {"keys", keys},
369 {"vals", vals},
370
371 {"sequential?", sequential_Q},
372 {"cons", cons},
373 {"concat", concat},
374 {"vec", new MalFunc(a => new MalVector(((MalList)a[0]).getValue()))},
375 {"nth", nth},
376 {"first", first},
377 {"rest", rest},
378 {"empty?", empty_Q},
379 {"count", count},
380 {"conj", conj},
381 {"seq", seq},
382 {"apply", apply},
383 {"map", map},
384
385 {"with-meta", with_meta},
386 {"meta", meta},
387 {"atom", new MalFunc(a => new MalAtom(a[0]))},
388 {"atom?", atom_Q},
389 {"deref", deref},
390 {"reset!", reset_BANG},
391 {"swap!", swap_BANG},
392 };
393 }
394 }