Commit | Line | Data |
---|---|---|
3934e3f8 HT |
1 | import 'dart:io'; |
2 | ||
3 | import 'printer.dart'; | |
4 | import 'reader.dart' as reader; | |
5 | import 'types.dart'; | |
6 | ||
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); | |
12 | }), | |
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); | |
17 | }), | |
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); | |
22 | }), | |
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); | |
27 | }), | |
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); | |
35 | }), | |
36 | new MalSymbol('count'): new MalBuiltin((List<MalType> args) { | |
37 | var a = args.first as MalIterable; | |
38 | return new MalInt(a.elements.length); | |
39 | }), | |
40 | new MalSymbol('='): new MalBuiltin((List<MalType> args) { | |
41 | var a = args[0]; | |
42 | var b = args[1]; | |
43 | return new MalBool(a == b); | |
44 | }), | |
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); | |
49 | }), | |
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); | |
54 | }), | |
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); | |
59 | }), | |
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); | |
64 | }), | |
65 | new MalSymbol('pr-str'): new MalBuiltin((List<MalType> args) { | |
66 | return new MalString( | |
67 | args.map((a) => pr_str(a, print_readably: true)).join(' ')); | |
68 | }), | |
69 | new MalSymbol('str'): new MalBuiltin((List<MalType> args) { | |
70 | return new MalString( | |
71 | args.map((a) => pr_str(a, print_readably: false)).join()); | |
72 | }), | |
73 | new MalSymbol('prn'): new MalBuiltin((List<MalType> args) { | |
74 | print(args.map((a) => pr_str(a, print_readably: true)).join(' ')); | |
75 | return new MalNil(); | |
76 | }), | |
77 | new MalSymbol('println'): new MalBuiltin((List<MalType> args) { | |
78 | print(args.map((a) => pr_str(a, print_readably: false)).join(' ')); | |
79 | return new MalNil(); | |
80 | }), | |
81 | new MalSymbol('read-string'): new MalBuiltin((List<MalType> args) { | |
82 | var code = args.single as MalString; | |
83 | return reader.read_str(code.value); | |
84 | }), | |
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()); | |
89 | }), | |
90 | new MalSymbol('atom'): new MalBuiltin((List<MalType> args) { | |
91 | var value = args.single; | |
92 | return new MalAtom(value); | |
93 | }), | |
94 | new MalSymbol('atom?'): new MalBuiltin((List<MalType> args) { | |
95 | var value = args.single; | |
96 | return new MalBool(value is MalAtom); | |
97 | }), | |
98 | new MalSymbol('deref'): new MalBuiltin((List<MalType> args) { | |
99 | var atom = args.single as MalAtom; | |
100 | return atom.value; | |
101 | }), | |
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; | |
106 | return newValue; | |
107 | }), | |
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); | |
113 | atom.value = result; | |
114 | return result; | |
115 | }), | |
116 | new MalSymbol('cons'): new MalBuiltin((List<MalType> args) { | |
117 | var x = args[0]; | |
118 | var xs = args[1] as MalIterable; | |
119 | return new MalList([x]..addAll(xs)); | |
120 | }), | |
121 | new MalSymbol('concat'): new MalBuiltin((List<MalType> args) { | |
122 | var results = <MalType>[]; | |
123 | for (MalIterable element in args) { | |
124 | results.addAll(element); | |
125 | } | |
126 | return new MalList(results); | |
127 | }), | |
128 | new MalSymbol('nth'): new MalBuiltin((List<MalType> args) { | |
129 | var indexable = args[0] as MalIterable; | |
130 | var index = args[1] as MalInt; | |
131 | try { | |
132 | return indexable[index.value]; | |
133 | } on RangeError catch (e) { | |
134 | throw new MalNativeException(e); | |
135 | } | |
136 | }), | |
137 | new MalSymbol('first'): new MalBuiltin((List<MalType> args) { | |
138 | var list = args.first as MalIterable; | |
139 | if (list.isEmpty) return new MalNil(); | |
140 | return list.first; | |
141 | }), | |
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)); | |
146 | }), | |
147 | new MalSymbol('throw'): new MalBuiltin((List<MalType> args) { | |
148 | throw new MalException(args.first); | |
149 | }), | |
150 | new MalSymbol('nil?'): new MalBuiltin((List<MalType> args) { | |
151 | return new MalBool(args.first is MalNil); | |
152 | }), | |
153 | new MalSymbol('true?'): new MalBuiltin((List<MalType> args) { | |
154 | return new MalBool(args.first is MalBool && (args.first as MalBool).value); | |
155 | }), | |
156 | new MalSymbol('false?'): new MalBuiltin((List<MalType> args) { | |
157 | return new MalBool(args.first is MalBool && !(args.first as MalBool).value); | |
158 | }), | |
159 | new MalSymbol('symbol'): new MalBuiltin((List<MalType> args) { | |
160 | return new MalSymbol((args.first as MalString).value); | |
161 | }), | |
162 | new MalSymbol('symbol?'): new MalBuiltin((List<MalType> args) { | |
163 | return new MalBool(args.first is MalSymbol); | |
164 | }), | |
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); | |
168 | }), | |
169 | new MalSymbol('keyword?'): new MalBuiltin((List<MalType> args) { | |
170 | return new MalBool(args.first is MalKeyword); | |
171 | }), | |
172 | new MalSymbol('vector'): new MalBuiltin((List<MalType> args) { | |
173 | return new MalVector(args); | |
174 | }), | |
175 | new MalSymbol('vector?'): new MalBuiltin((List<MalType> args) { | |
176 | return new MalBool(args.first is MalVector); | |
177 | }), | |
178 | new MalSymbol('hash-map'): new MalBuiltin((List<MalType> args) { | |
179 | return new MalHashMap.fromSequence(args); | |
180 | }), | |
181 | new MalSymbol('map?'): new MalBuiltin((List<MalType> args) { | |
182 | return new MalBool(args.first is MalHashMap); | |
183 | }), | |
184 | new MalSymbol('assoc'): new MalBuiltin((List<MalType> args) { | |
185 | var map = args.first as MalHashMap; | |
186 | var assoc = new MalHashMap.fromSequence(args.skip(1).toList()); | |
187 | var newMap = new Map<MalType, MalType>.from(map.value); | |
188 | newMap.addAll(assoc.value); | |
189 | return new MalHashMap(newMap); | |
190 | }), | |
191 | new MalSymbol('dissoc'): new MalBuiltin((List<MalType> args) { | |
192 | var map = args.first as MalHashMap; | |
193 | var newMap = new Map<MalType, MalType>.from(map.value); | |
194 | for (var key in args.skip(1)) { | |
195 | newMap.remove(key); | |
196 | } | |
197 | return new MalHashMap(newMap); | |
198 | }), | |
199 | new MalSymbol('get'): new MalBuiltin((List<MalType> args) { | |
200 | if (args[0] is MalNil) return new MalNil(); | |
201 | var map = args[0] as MalHashMap; | |
202 | var key = args[1]; | |
203 | return map.value[key] ?? new MalNil(); | |
204 | }), | |
205 | new MalSymbol('contains?'): new MalBuiltin((List<MalType> args) { | |
206 | var map = args[0] as MalHashMap; | |
207 | var key = args[1]; | |
208 | return new MalBool(map.value.containsKey(key)); | |
209 | }), | |
210 | new MalSymbol('keys'): new MalBuiltin((List<MalType> args) { | |
211 | return new MalList((args.first as MalHashMap).value.keys.toList()); | |
212 | }), | |
213 | new MalSymbol('vals'): new MalBuiltin((List<MalType> args) { | |
214 | return new MalList((args.first as MalHashMap).value.values.toList()); | |
215 | }), | |
216 | new MalSymbol('sequential?'): new MalBuiltin((List<MalType> args) { | |
217 | return new MalBool(args.first is MalList || args.first is MalVector); | |
218 | }), | |
219 | new MalSymbol('readline'): new MalBuiltin((List<MalType> args) { | |
220 | var message = args.first as MalString; | |
221 | stdout.write(message.value); | |
222 | var input = stdin.readLineSync(); | |
223 | if (input == null) return new MalNil(); | |
224 | return new MalString(input); | |
225 | }), | |
226 | new MalSymbol('time-ms'): new MalBuiltin((List<MalType> args) { | |
227 | assert(args.isEmpty); | |
228 | return new MalInt(new DateTime.now().millisecondsSinceEpoch); | |
229 | }), | |
230 | new MalSymbol('conj'): new MalBuiltin((List<MalType> args) { | |
231 | var collection = args.first; | |
232 | var elements = args.sublist(1); | |
233 | if (collection is MalList) { | |
234 | return new MalList( | |
235 | elements.reversed.toList()..addAll(collection.elements)); | |
236 | } | |
237 | if (collection is MalVector) { | |
238 | return new MalVector(collection.elements.toList()..addAll(elements)); | |
239 | } | |
240 | throw new MalException(new MalString('"conj" takes a list or vector')); | |
241 | }), | |
242 | new MalSymbol('string?'): new MalBuiltin((List<MalType> args) { | |
243 | return new MalBool(args.first is MalString); | |
244 | }), | |
245 | new MalSymbol('seq'): new MalBuiltin((List<MalType> args) { | |
246 | var arg = args.first; | |
247 | if (arg is MalIterable && arg.isEmpty) return new MalNil(); | |
248 | if (arg is MalString && arg.value.isEmpty) return new MalNil(); | |
249 | ||
250 | if (arg is MalNil || arg is MalList) return arg; | |
251 | if (arg is MalVector) return new MalList(arg.elements.toList()); | |
252 | if (arg is MalString) { | |
253 | var chars = <MalString>[]; | |
254 | for (var i = 0; i < arg.value.length; i++) { | |
255 | chars.add(new MalString(arg.value[i])); | |
256 | } | |
257 | return new MalList(chars); | |
258 | } | |
259 | throw new MalException(new MalString('bad argument to "seq"')); | |
260 | }), | |
261 | new MalSymbol('map'): new MalBuiltin((List<MalType> args) { | |
262 | var fn = args[0] as MalCallable; | |
263 | var list = args[1] as MalIterable; | |
264 | var newList = <MalType>[]; | |
265 | for (var element in list) { | |
266 | newList.add(fn.call([element])); | |
267 | } | |
268 | return new MalList(newList); | |
269 | }), | |
270 | new MalSymbol('apply'): new MalBuiltin((List<MalType> args) { | |
271 | var func = args.first as MalCallable; | |
272 | var argList = args.last as MalIterable; | |
273 | var newArgs = args.sublist(1, args.length - 1); | |
274 | newArgs.addAll(argList); | |
275 | return func.call(newArgs); | |
276 | }), | |
277 | new MalSymbol('meta'): new MalBuiltin((List<MalType> args) { | |
278 | var arg = args.first; | |
279 | return arg.meta ?? new MalNil(); | |
280 | }), | |
281 | new MalSymbol('with-meta'): new MalBuiltin((List<MalType> args) { | |
282 | var evaled = args.first; | |
283 | var evaledWithMeta = evaled.clone(); | |
284 | evaledWithMeta.meta = args[1]; | |
285 | return evaledWithMeta; | |
286 | }), | |
287 | }; |