4 import 'reader.dart' as reader;
7 Map<MalSymbol, MalBuiltin> ns = <MalSymbol, MalBuiltin>{
8 new MalSymbol('+'): new MalBuiltin((List<MalType> args) {
9 var a = args[0] as MalInt;
10 var b = args[1] as MalInt;
11 return new MalInt(a.value + b.value);
13 new MalSymbol('-'): new MalBuiltin((List<MalType> args) {
14 var a = args[0] as MalInt;
15 var b = args[1] as MalInt;
16 return new MalInt(a.value - b.value);
18 new MalSymbol('*'): new MalBuiltin((List<MalType> args) {
19 var a = args[0] as MalInt;
20 var b = args[1] as MalInt;
21 return new MalInt(a.value * b.value);
23 new MalSymbol('/'): new MalBuiltin((List<MalType> args) {
24 var a = args[0] as MalInt;
25 var b = args[1] as MalInt;
26 return new MalInt(a.value ~/ b.value);
28 new MalSymbol('list'):
29 new MalBuiltin((List<MalType> args) => new MalList(args.toList())),
30 new MalSymbol('list?'): new MalBuiltin(
31 (List<MalType> args) => new MalBool(args.single is MalList)),
32 new MalSymbol('empty?'): new MalBuiltin((List<MalType> args) {
33 var a = args.single as MalIterable;
34 return new MalBool(a.elements.isEmpty);
36 new MalSymbol('count'): new MalBuiltin((List<MalType> args) {
37 var a = args.first as MalIterable;
38 return new MalInt(a.elements.length);
40 new MalSymbol('='): new MalBuiltin((List<MalType> args) {
43 return new MalBool(a == b);
45 new MalSymbol('<'): new MalBuiltin((List<MalType> args) {
46 var a = args[0] as MalInt;
47 var b = args[1] as MalInt;
48 return new MalBool(a.value < b.value);
50 new MalSymbol('<='): new MalBuiltin((List<MalType> args) {
51 var a = args[0] as MalInt;
52 var b = args[1] as MalInt;
53 return new MalBool(a.value <= b.value);
55 new MalSymbol('>'): new MalBuiltin((List<MalType> args) {
56 var a = args[0] as MalInt;
57 var b = args[1] as MalInt;
58 return new MalBool(a.value > b.value);
60 new MalSymbol('>='): new MalBuiltin((List<MalType> args) {
61 var a = args[0] as MalInt;
62 var b = args[1] as MalInt;
63 return new MalBool(a.value >= b.value);
65 new MalSymbol('pr-str'): new MalBuiltin((List<MalType> args) {
67 args.map((a) => pr_str(a, print_readably: true)).join(' '));
69 new MalSymbol('str'): new MalBuiltin((List<MalType> args) {
71 args.map((a) => pr_str(a, print_readably: false)).join());
73 new MalSymbol('prn'): new MalBuiltin((List<MalType> args) {
74 print(args.map((a) => pr_str(a, print_readably: true)).join(' '));
77 new MalSymbol('println'): new MalBuiltin((List<MalType> args) {
78 print(args.map((a) => pr_str(a, print_readably: false)).join(' '));
81 new MalSymbol('read-string'): new MalBuiltin((List<MalType> args) {
82 var code = args.single as MalString;
83 return reader.read_str(code.value);
85 new MalSymbol('slurp'): new MalBuiltin((List<MalType> args) {
86 var fileName = args.single as MalString;
87 var file = new File(fileName.value);
88 return new MalString(file.readAsStringSync());
90 new MalSymbol('atom'): new MalBuiltin((List<MalType> args) {
91 var value = args.single;
92 return new MalAtom(value);
94 new MalSymbol('atom?'): new MalBuiltin((List<MalType> args) {
95 var value = args.single;
96 return new MalBool(value is MalAtom);
98 new MalSymbol('deref'): new MalBuiltin((List<MalType> args) {
99 var atom = args.single as MalAtom;
102 new MalSymbol('reset!'): new MalBuiltin((List<MalType> args) {
103 var atom = args[0] as MalAtom;
104 var newValue = args[1];
105 atom.value = newValue;
108 new MalSymbol('swap!'): new MalBuiltin((List<MalType> args) {
109 var atom = args[0] as MalAtom;
110 var func = args[1] as MalCallable;
111 var fnArgs = [atom.value]..addAll(args.sublist(2));
112 var result = func.call(fnArgs);
116 new MalSymbol('cons'): new MalBuiltin((List<MalType> args) {
118 var xs = args[1] as MalIterable;
119 return new MalList([x]..addAll(xs));
121 new MalSymbol('concat'): new MalBuiltin((List<MalType> args) {
122 var results = <MalType>[];
123 for (MalIterable element in args) {
124 results.addAll(element);
126 return new MalList(results);
128 new MalSymbol('nth'): new MalBuiltin((List<MalType> args) {
129 var indexable = args[0] as MalIterable;
130 var index = args[1] as MalInt;
132 return indexable[index.value];
133 } on RangeError catch (e) {
134 throw new MalException(new MalString(e.toString()));
137 new MalSymbol('first'): new MalBuiltin((List<MalType> args) {
138 var list = args.first as MalIterable;
139 if (list.isEmpty) return new MalNil();
142 new MalSymbol('rest'): new MalBuiltin((List<MalType> args) {
143 var list = args.first as MalIterable;
144 if (list.isEmpty) return new MalList(<MalType>[]);
145 return new MalList(list.sublist(1));
147 new MalSymbol('throw'): new MalBuiltin((List<MalType> args) {
148 throw new MalException(args.first);
150 new MalSymbol('nil?'): new MalBuiltin((List<MalType> args) {
151 return new MalBool(args.first is MalNil);
153 new MalSymbol('true?'): new MalBuiltin((List<MalType> args) {
154 return new MalBool(args.first is MalBool && (args.first as MalBool).value);
156 new MalSymbol('false?'): new MalBuiltin((List<MalType> args) {
157 return new MalBool(args.first is MalBool && !(args.first as MalBool).value);
159 new MalSymbol('symbol'): new MalBuiltin((List<MalType> args) {
160 return new MalSymbol((args.first as MalString).value);
162 new MalSymbol('symbol?'): new MalBuiltin((List<MalType> args) {
163 return new MalBool(args.first is MalSymbol);
165 new MalSymbol('keyword'): new MalBuiltin((List<MalType> args) {
166 if (args.first is MalKeyword) return args.first;
167 return new MalKeyword((args.first as MalString).value);
169 new MalSymbol('keyword?'): new MalBuiltin((List<MalType> args) {
170 return new MalBool(args.first is MalKeyword);
172 new MalSymbol('number?'): new MalBuiltin((List<MalType> args) {
173 return new MalBool(args.first is MalInt);
175 new MalSymbol('fn?'): new MalBuiltin((List<MalType> args) {
176 return new MalBool(args.first is MalCallable && !(args.first.isMacro));
178 new MalSymbol('macro?'): new MalBuiltin((List<MalType> args) {
179 return new MalBool(args.first is MalCallable && args.first.isMacro);
181 new MalSymbol('vector'): new MalBuiltin((List<MalType> args) {
182 return new MalVector(args);
184 new MalSymbol('vector?'): new MalBuiltin((List<MalType> args) {
185 return new MalBool(args.first is MalVector);
187 new MalSymbol('hash-map'): new MalBuiltin((List<MalType> args) {
188 return new MalHashMap.fromSequence(args);
190 new MalSymbol('map?'): new MalBuiltin((List<MalType> args) {
191 return new MalBool(args.first is MalHashMap);
193 new MalSymbol('assoc'): new MalBuiltin((List<MalType> args) {
194 var map = args.first as MalHashMap;
195 var assoc = new MalHashMap.fromSequence(args.skip(1).toList());
196 var newMap = new Map<MalType, MalType>.from(map.value);
197 newMap.addAll(assoc.value);
198 return new MalHashMap(newMap);
200 new MalSymbol('dissoc'): new MalBuiltin((List<MalType> args) {
201 var map = args.first as MalHashMap;
202 var newMap = new Map<MalType, MalType>.from(map.value);
203 for (var key in args.skip(1)) {
206 return new MalHashMap(newMap);
208 new MalSymbol('get'): new MalBuiltin((List<MalType> args) {
209 if (args[0] is MalNil) return new MalNil();
210 var map = args[0] as MalHashMap;
212 return map.value[key] ?? new MalNil();
214 new MalSymbol('contains?'): new MalBuiltin((List<MalType> args) {
215 var map = args[0] as MalHashMap;
217 return new MalBool(map.value.containsKey(key));
219 new MalSymbol('keys'): new MalBuiltin((List<MalType> args) {
220 return new MalList((args.first as MalHashMap).value.keys.toList());
222 new MalSymbol('vals'): new MalBuiltin((List<MalType> args) {
223 return new MalList((args.first as MalHashMap).value.values.toList());
225 new MalSymbol('sequential?'): new MalBuiltin((List<MalType> args) {
226 return new MalBool(args.first is MalList || args.first is MalVector);
228 new MalSymbol('readline'): new MalBuiltin((List<MalType> args) {
229 var message = args.first as MalString;
230 stdout.write(message.value);
231 var input = stdin.readLineSync();
232 if (input == null) return new MalNil();
233 return new MalString(input);
235 new MalSymbol('time-ms'): new MalBuiltin((List<MalType> args) {
236 assert(args.isEmpty);
237 return new MalInt(new DateTime.now().millisecondsSinceEpoch);
239 new MalSymbol('conj'): new MalBuiltin((List<MalType> args) {
240 var collection = args.first;
241 var elements = args.sublist(1);
242 if (collection is MalList) {
244 elements.reversed.toList()..addAll(collection.elements));
246 if (collection is MalVector) {
247 return new MalVector(collection.elements.toList()..addAll(elements));
249 throw new MalException(new MalString('"conj" takes a list or vector'));
251 new MalSymbol('string?'): new MalBuiltin((List<MalType> args) {
252 return new MalBool(args.first is MalString);
254 new MalSymbol('seq'): new MalBuiltin((List<MalType> args) {
255 var arg = args.first;
256 if (arg is MalIterable && arg.isEmpty) return new MalNil();
257 if (arg is MalString && arg.value.isEmpty) return new MalNil();
259 if (arg is MalNil || arg is MalList) return arg;
260 if (arg is MalVector) return new MalList(arg.elements.toList());
261 if (arg is MalString) {
262 var chars = <MalString>[];
263 for (var i = 0; i < arg.value.length; i++) {
264 chars.add(new MalString(arg.value[i]));
266 return new MalList(chars);
268 throw new MalException(new MalString('bad argument to "seq"'));
270 new MalSymbol('map'): new MalBuiltin((List<MalType> args) {
271 var fn = args[0] as MalCallable;
272 var list = args[1] as MalIterable;
273 var newList = <MalType>[];
274 for (var element in list) {
275 newList.add(fn.call([element]));
277 return new MalList(newList);
279 new MalSymbol('apply'): new MalBuiltin((List<MalType> args) {
280 var func = args.first as MalCallable;
281 var argList = args.last as MalIterable;
282 var newArgs = args.sublist(1, args.length - 1);
283 newArgs.addAll(argList);
284 return func.call(newArgs);
286 new MalSymbol('meta'): new MalBuiltin((List<MalType> args) {
287 var arg = args.first;
288 return arg.meta ?? new MalNil();
290 new MalSymbol('with-meta'): new MalBuiltin((List<MalType> args) {
291 var evaled = args.first;
292 var evaledWithMeta = evaled.clone();
293 evaledWithMeta.meta = args[1];
294 return evaledWithMeta;