Merge pull request #156 from omarrayward/explain-regexp-tokenizer
[jackhill/mal.git] / 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 keyword = new MalFunc(
39 a => {
40 if (a[0] is MalString &&
41 ((MalString)a[0]).getValue()[0] == '\u029e') {
42 return a[0];
43 } else {
44 return new MalString("\u029e" + ((MalString)a[0]).getValue());
45 }
46 } );
47
48 static MalFunc keyword_Q = new MalFunc(
49 a => {
50 if (a[0] is MalString &&
51 ((MalString)a[0]).getValue()[0] == '\u029e') {
52 return True;
53 } else {
54 return False;
55 }
56 } );
57
58
59 // Number functions
60 static MalFunc time_ms = new MalFunc(
61 a => new MalInt(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond));
62
63 // String functions
64 static public MalFunc pr_str = new MalFunc(
65 a => new MalString(printer._pr_str_args(a, " ", true)) );
66
67 static public MalFunc str = new MalFunc(
68 a => new MalString(printer._pr_str_args(a, "", false)) );
69
70 static public MalFunc prn = new MalFunc(
71 a => {
72 Console.WriteLine(printer._pr_str_args(a, " ", true));
73 return Nil;
74 } );
75
76 static public MalFunc println = new MalFunc(
77 a => {
78 Console.WriteLine(printer._pr_str_args(a, " ", false));
79 return Nil;
80 } );
81
82 static public MalFunc mal_readline = new MalFunc(
83 a => {
84 var line = readline.Readline(((MalString)a[0]).getValue());
85 if (line == null) { return types.Nil; }
86 else { return new MalString(line); }
87 } );
88
89 static public MalFunc read_string = new MalFunc(
90 a => reader.read_str(((MalString)a[0]).getValue()));
91
92 static public MalFunc slurp = new MalFunc(
93 a => new MalString(File.ReadAllText(
94 ((MalString)a[0]).getValue())));
95
96
97 // List/Vector functions
98 static public MalFunc list_Q = new MalFunc(
99 a => a[0].GetType() == typeof(MalList) ? True : False);
100
101 static public MalFunc vector_Q = new MalFunc(
102 a => a[0].GetType() == typeof(MalVector) ? True : False);
103
104 // HashMap functions
105 static public MalFunc hash_map_Q = new MalFunc(
106 a => a[0].GetType() == typeof(MalHashMap) ? True : False);
107
108 static MalFunc contains_Q = new MalFunc(
109 a => {
110 string key = ((MalString)a[1]).getValue();
111 var dict = ((MalHashMap)a[0]).getValue();
112 return dict.ContainsKey(key) ? True : False;
113 });
114
115 static MalFunc assoc = new MalFunc(
116 a => {
117 var new_hm = ((MalHashMap)a[0]).copy();
118 return new_hm.assoc_BANG((MalList)a.slice(1));
119 });
120
121 static MalFunc dissoc = new MalFunc(
122 a => {
123 var new_hm = ((MalHashMap)a[0]).copy();
124 return new_hm.dissoc_BANG((MalList)a.slice(1));
125 });
126
127 static MalFunc get = new MalFunc(
128 a => {
129 string key = ((MalString)a[1]).getValue();
130 if (a[0] == Nil) {
131 return Nil;
132 } else {
133 var dict = ((MalHashMap)a[0]).getValue();
134 return dict.ContainsKey(key) ? dict[key] : Nil;
135 }
136 });
137
138 static MalFunc keys = new MalFunc(
139 a => {
140 var dict = ((MalHashMap)a[0]).getValue();
141 MalList key_lst = new MalList();
142 foreach (var key in dict.Keys) {
143 key_lst.conj_BANG(new MalString(key));
144 }
145 return key_lst;
146 });
147
148 static MalFunc vals = new MalFunc(
149 a => {
150 var dict = ((MalHashMap)a[0]).getValue();
151 MalList val_lst = new MalList();
152 foreach (var val in dict.Values) {
153 val_lst.conj_BANG(val);
154 }
155 return val_lst;
156 });
157
158 // Sequence functions
159 static public MalFunc sequential_Q = new MalFunc(
160 a => a[0] is MalList ? True : False);
161
162 static MalFunc cons = new MalFunc(
163 a => {
164 var lst = new List<MalVal>();
165 lst.Add(a[0]);
166 lst.AddRange(((MalList)a[1]).getValue());
167 return (MalVal)new MalList(lst);
168 });
169
170 static MalFunc concat = new MalFunc(
171 a => {
172 if (a.size() == 0) { return new MalList(); }
173 var lst = new List<MalVal>();
174 lst.AddRange(((MalList)a[0]).getValue());
175 for(int i=1; i<a.size(); i++) {
176 lst.AddRange(((MalList)a[i]).getValue());
177 }
178 return (MalVal)new MalList(lst);
179 });
180
181 static MalFunc nth = new MalFunc(
182 a => {
183 var idx = (int)((MalInt)a[1]).getValue();
184 if (idx < ((MalList)a[0]).size()) {
185 return ((MalList)a[0])[idx];
186 } else {
187 throw new Mal.types.MalException(
188 "nth: index out of range");
189 }
190 });
191
192 static MalFunc first = new MalFunc(
193 a => ((MalList)a[0])[0]);
194
195 static MalFunc rest = new MalFunc(
196 a => ((MalList)a[0]).rest());
197
198 static MalFunc empty_Q = new MalFunc(
199 a => ((MalList)a[0]).size() == 0 ? True : False);
200
201 static MalFunc count = new MalFunc(
202 a => {
203 return (a[0] == Nil)
204 ? new MalInt(0)
205 :new MalInt(((MalList)a[0]).size());
206 });
207
208 static MalFunc conj = new MalFunc(
209 a => {
210 var src_lst = ((MalList)a[0]).getValue();
211 var new_lst = new List<MalVal>();
212 new_lst.AddRange(src_lst);
213 if (a[0] is MalVector) {
214 for(int i=1; i<a.size(); i++) {
215 new_lst.Add(a[i]);
216 }
217 return new MalVector(new_lst);
218 } else {
219 for(int i=1; i<a.size(); i++) {
220 new_lst.Insert(0, a[i]);
221 }
222 return new MalList(new_lst);
223 }
224 });
225
226
227 // General list related functions
228 static MalFunc apply = new MalFunc(
229 a => {
230 var f = (MalFunc)a[0];
231 var lst = new List<MalVal>();
232 lst.AddRange(a.slice(1,a.size()-1).getValue());
233 lst.AddRange(((MalList)a[a.size()-1]).getValue());
234 return f.apply(new MalList(lst));
235 });
236
237 static MalFunc map = new MalFunc(
238 a => {
239 MalFunc f = (MalFunc) a[0];
240 var src_lst = ((MalList)a[1]).getValue();
241 var new_lst = new List<MalVal>();
242 for(int i=0; i<src_lst.Count; i++) {
243 new_lst.Add(f.apply(new MalList(src_lst[i])));
244 }
245 return new MalList(new_lst);
246 });
247
248
249 // Metadata functions
250 static MalFunc meta = new MalFunc(
251 a => a[0].getMeta());
252
253 static MalFunc with_meta = new MalFunc(
254 a => ((MalVal)a[0]).copy().setMeta(a[1]));
255
256
257 // Atom functions
258 static MalFunc atom_Q = new MalFunc(
259 a => a[0] is MalAtom ? True : False);
260
261 static MalFunc deref = new MalFunc(
262 a => ((MalAtom)a[0]).getValue());
263
264 static MalFunc reset_BANG = new MalFunc(
265 a => ((MalAtom)a[0]).setValue(a[1]));
266
267 static MalFunc swap_BANG = new MalFunc(
268 a => {
269 MalAtom atm = (MalAtom)a[0];
270 MalFunc f = (MalFunc)a[1];
271 var new_lst = new List<MalVal>();
272 new_lst.Add(atm.getValue());
273 new_lst.AddRange(((MalList)a.slice(2)).getValue());
274 return atm.setValue(f.apply(new MalList(new_lst)));
275 });
276
277
278
279 static public Dictionary<string, MalVal> ns =
280 new Dictionary<string, MalVal> {
281 {"=", new MalFunc(
282 a => Mal.types._equal_Q(a[0], a[1]) ? True : False)},
283 {"throw", mal_throw},
284 {"nil?", nil_Q},
285 {"true?", true_Q},
286 {"false?", false_Q},
287 {"symbol", new MalFunc(a => new MalSymbol((MalString)a[0]))},
288 {"symbol?", symbol_Q},
289 {"keyword", keyword},
290 {"keyword?", keyword_Q},
291
292 {"pr-str", pr_str},
293 {"str", str},
294 {"prn", prn},
295 {"println", println},
296 {"readline", mal_readline},
297 {"read-string", read_string},
298 {"slurp", slurp},
299 {"<", new MalFunc(a => (MalInt)a[0] < (MalInt)a[1])},
300 {"<=", new MalFunc(a => (MalInt)a[0] <= (MalInt)a[1])},
301 {">", new MalFunc(a => (MalInt)a[0] > (MalInt)a[1])},
302 {">=", new MalFunc(a => (MalInt)a[0] >= (MalInt)a[1])},
303 {"+", new MalFunc(a => (MalInt)a[0] + (MalInt)a[1])},
304 {"-", new MalFunc(a => (MalInt)a[0] - (MalInt)a[1])},
305 {"*", new MalFunc(a => (MalInt)a[0] * (MalInt)a[1])},
306 {"/", new MalFunc(a => (MalInt)a[0] / (MalInt)a[1])},
307 {"time-ms", time_ms},
308
309 {"list", new MalFunc(a => new MalList(a.getValue()))},
310 {"list?", list_Q},
311 {"vector", new MalFunc(a => new MalVector(a.getValue()))},
312 {"vector?", vector_Q},
313 {"hash-map", new MalFunc(a => new MalHashMap(a))},
314 {"map?", hash_map_Q},
315 {"contains?", contains_Q},
316 {"assoc", assoc},
317 {"dissoc", dissoc},
318 {"get", get},
319 {"keys", keys},
320 {"vals", vals},
321
322 {"sequential?", sequential_Q},
323 {"cons", cons},
324 {"concat", concat},
325 {"nth", nth},
326 {"first", first},
327 {"rest", rest},
328 {"empty?", empty_Q},
329 {"count", count},
330 {"conj", conj},
331 {"apply", apply},
332 {"map", map},
333
334 {"with-meta", with_meta},
335 {"meta", meta},
336 {"atom", new MalFunc(a => new MalAtom(a[0]))},
337 {"atom?", atom_Q},
338 {"deref", deref},
339 {"reset!", reset_BANG},
340 {"swap!", swap_BANG},
341 };
342 }
343 }