DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / objc / core.m
1 #import <Foundation/Foundation.h>
2
3 #import "mal_readline.h"
4 #import "types.h"
5 #import "reader.h"
6 #import "printer.h"
7 #import "malfunc.h"
8 #import "core.h"
9 #import <objc/runtime.h>
10
11 NSObject * wrap_tf(BOOL val) {
12 return val ? [MalTrue alloc] : [MalFalse alloc];
13 }
14
15 @implementation Core
16
17 + (NSDictionary *)ns {
18 return @{
19 @"=": ^(NSArray *args){
20 return wrap_tf(equal_Q(args[0], args[1]));
21 },
22 @"throw": ^(NSArray *args){
23 @throw args[0];
24 },
25
26 @"nil?": ^(NSArray *args){
27 return wrap_tf([args[0] isKindOfClass:[NSNull class]]);
28 },
29 @"true?": ^(NSArray *args){
30 return wrap_tf([args[0] isKindOfClass:[MalTrue class]]);
31 },
32 @"false?": ^(NSArray *args){
33 return wrap_tf([args[0] isKindOfClass:[MalFalse class]]);
34 },
35 @"string?": ^(NSArray *args){
36 return wrap_tf(string_Q(args[0]));
37 },
38 @"symbol": ^(NSArray *args){
39 return [MalSymbol stringWithString:args[0]];
40 },
41 @"symbol?": ^(NSArray *args){
42 return wrap_tf([args[0] isKindOfClass:[MalSymbol class]]);
43 },
44 @"keyword": ^(NSArray *args){
45 return [NSString stringWithFormat:@"\u029e%@", args[0]];
46 },
47 @"keyword?": ^(NSArray *args){
48 return wrap_tf([args[0] isKindOfClass:[NSString class]] &&
49 ![args[0] isKindOfClass:[MalSymbol class]] &&
50 !string_Q(args[0]));
51 },
52 @"number?": ^(NSArray *args){
53 return wrap_tf([args[0] isKindOfClass:[NSNumber class]]);
54 },
55 @"fn?": ^(NSArray *args){
56 return wrap_tf(block_Q(args[0]) ||
57 ([args[0] isKindOfClass:[MalFunc class]] && ![(MalFunc *)args[0] isMacro]));
58 },
59 @"macro?": ^(NSArray *args){
60 return wrap_tf([args[0] isKindOfClass:[MalFunc class]] && [(MalFunc *)args[0] isMacro]);
61 },
62
63 @"pr-str": ^(NSArray *args){
64 NSMutableArray * res = [NSMutableArray array];
65 for (id e in args) { [res addObject:_pr_str(e,true)]; }
66 return [res componentsJoinedByString:@" "];
67 },
68 @"str": ^(NSArray *args){
69 NSMutableArray * res = [NSMutableArray array];
70 for (id e in args) { [res addObject:_pr_str(e,false)]; }
71 return [res componentsJoinedByString:@""];
72 },
73 @"prn": ^(NSArray *args){
74 NSMutableArray * res = [NSMutableArray array];
75 for (id e in args) { [res addObject:_pr_str(e,true)]; }
76 printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
77 fflush(stdout);
78 return [NSNull alloc];
79 },
80 @"println": ^(NSArray *args){
81 NSMutableArray * res = [NSMutableArray array];
82 for (id e in args) { [res addObject:_pr_str(e,false)]; }
83 printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
84 fflush(stdout);
85 return [NSNull alloc];
86 },
87 @"read-string": ^(NSArray *args){
88 return read_str(args[0]);
89 },
90 @"readline": ^(NSArray *args){
91 char * rawline = _readline((char *)[(NSString *)args[0] UTF8String]);
92 if (rawline) {
93 return (NSObject *)[NSString stringWithUTF8String:rawline];
94 } else {
95 return (NSObject *)[NSNull alloc];
96 }
97 },
98 @"slurp": ^(NSArray *args){
99 return [NSString stringWithContentsOfFile:args[0]
100 encoding: NSUTF8StringEncoding
101 error: NULL];
102 },
103
104 @"<": ^(NSArray *args){
105 return wrap_tf([args[0] intValue] < [args[1] intValue]);
106 },
107 @"<=": ^(NSArray *args){
108 return wrap_tf([args[0] intValue] <= [args[1] intValue]);
109 },
110 @">": ^(NSArray *args){
111 return wrap_tf([args[0] intValue] > [args[1] intValue]);
112 },
113 @">=": ^(NSArray *args){
114 return wrap_tf([args[0] intValue] >= [args[1] intValue]);
115 },
116 @"+": ^(NSArray *args){
117 return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
118 },
119 @"-": ^(NSArray *args){
120 return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];
121 },
122 @"*": ^(NSArray *args){
123 return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];
124 },
125 @"/": ^(NSArray *args){
126 return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
127 },
128 @"time-ms": ^(NSArray *args){
129 long long ms = [[NSDate date] timeIntervalSince1970] * 1000;
130 return [NSNumber numberWithUnsignedInteger:ms];
131 },
132
133 @"list": ^(NSArray *args){
134 return args;
135 },
136 @"list?": ^(NSArray *args){
137 return wrap_tf(list_Q(args[0]));
138 },
139 @"vector": ^(NSArray *args){
140 return [MalVector fromArray:args];
141 },
142 @"vector?": ^(NSArray *args){
143 return wrap_tf([args[0] isKindOfClass:[MalVector class]]);
144 },
145 @"hash-map": ^(NSArray *args){
146 return hash_map(args);
147 },
148 @"map?": ^(NSArray *args){
149 return wrap_tf([args[0] isKindOfClass:[NSDictionary class]]);
150 },
151 @"assoc": ^(NSArray *args){
152 NSDictionary * dict = args[0];
153 NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
154 initWithDictionary:dict
155 copyItems:NO];
156 return assoc_BANG(new_dict, _rest(args));
157 },
158 @"dissoc": ^(NSArray *args){
159 NSDictionary * dict = args[0];
160 NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
161 initWithDictionary:dict
162 copyItems:NO];
163 for (NSString * key in _rest(args)) {
164 [new_dict removeObjectForKey:key];
165 }
166 return new_dict;
167 },
168 @"get": ^(NSArray *args){
169 if ([args[0] isKindOfClass:[NSNull class]]) {
170 return (NSObject *)[NSNull alloc];
171 }
172 NSObject * res = ((NSDictionary *)args[0])[args[1]];
173 return res ? res : [NSNull alloc];
174 },
175 @"contains?": ^(NSArray *args){
176 if ([args[0] isKindOfClass:[NSNull class]]) {
177 return wrap_tf(false);
178 }
179 return wrap_tf(((NSDictionary *)args[0])[args[1]] != nil);
180 },
181 @"keys": ^(NSArray *args){
182 return [(NSDictionary *)args[0] allKeys];
183 },
184 @"vals": ^(NSArray *args){
185 return [(NSDictionary *)args[0] allValues];
186 },
187
188 @"sequential?": ^(NSArray *args){
189 return wrap_tf([args[0] isKindOfClass:[NSArray class]]);
190 },
191 @"cons": ^(NSArray *args){
192 NSMutableArray * res = [NSMutableArray array];
193 [res addObject:args[0]];
194 [res addObjectsFromArray:args[1]];
195 return res;
196 },
197 @"concat": ^(NSArray *args){
198 NSMutableArray * res = [NSMutableArray array];
199 for (NSArray * arr in args) {
200 [res addObjectsFromArray:arr];
201 }
202 return res;
203 },
204 @"nth": ^(NSArray *args){
205 NSArray * lst = (NSArray *)args[0];
206 int idx = [(NSNumber *)args[1] intValue];
207 if (idx < [lst count]) {
208 return lst[idx];
209 } else {
210 @throw @"nth: index out of range";
211 }
212 },
213 @"first": ^(NSArray *args){
214 if ([args[0] isKindOfClass:[NSNull class]]) {
215 return (NSObject *)[NSNull alloc];
216 }
217 NSArray * lst = (NSArray *)args[0];
218 if ([lst count] > 0) {
219 return (NSObject *)lst[0];
220 } else {
221 return (NSObject *)[NSNull alloc];
222 }
223 },
224 @"rest": ^(NSArray *args){
225 if ([args[0] isKindOfClass:[NSNull class]]) {
226 return @[];
227 }
228 NSArray * lst = (NSArray *)args[0];
229 if ([lst count] > 1) {
230 return _rest(lst);
231 } else {
232 return @[];
233 }
234 },
235 @"empty?": ^(NSArray *args){
236 if ([args[0] isKindOfClass:[NSNull class]]) {
237 return wrap_tf(true);
238 } else {
239 return wrap_tf([args[0] count] == 0);
240 }
241 },
242 @"count": ^(NSArray *args){
243 if ([args[0] isKindOfClass:[NSNull class]]) {
244 return @0;
245 } else {
246 return [NSNumber numberWithInt:[args[0] count]];
247 }
248 },
249 @"apply": ^(NSArray *args){
250 NSObject * (^ f)(NSArray *) = args[0];
251 NSMutableArray * fargs = [NSMutableArray array];
252 if ([args count] > 1) {
253 NSRange r = NSMakeRange(1, [args count]-2);
254 [fargs addObjectsFromArray:[args subarrayWithRange:r]];
255 }
256 [fargs addObjectsFromArray:(NSArray *)[args lastObject]];
257 return apply(f, fargs);
258 },
259 @"map": ^(NSArray *args){
260 NSObject * (^ f)(NSArray *) = args[0];
261 NSMutableArray * res = [NSMutableArray array];
262 for (NSObject * x in (NSArray *)args[1]) {
263 [res addObject:apply(f, @[x])];
264 }
265 return res;
266 },
267 @"conj": ^(NSArray *args){
268 NSMutableArray * res = [NSMutableArray array];
269 if ([args[0] isKindOfClass:[MalVector class]]) {
270 [res addObjectsFromArray:args[0]];
271 [res addObjectsFromArray:_rest(args)];
272 return (NSObject *)[MalVector fromArray:res];
273 } else {
274 [res addObjectsFromArray:[[_rest(args) reverseObjectEnumerator]
275 allObjects]];
276 [res addObjectsFromArray:args[0]];
277 return (NSObject *)res;
278 }
279 },
280 @"seq": ^(NSArray *args){
281 if (list_Q(args[0])) {
282 if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
283 return (NSObject *)args[0];
284 } else if ([args[0] isKindOfClass:[MalVector class]]) {
285 if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
286 return (NSObject *)[NSArray arrayWithArray:args[0]];
287 } else if (string_Q(args[0])) {
288 NSString * str = args[0];
289 if ([str length] == 0) { return (NSObject *)[NSNull alloc]; }
290 NSMutableArray * res = [NSMutableArray array];
291 for (int i=0; i < [str length]; i++) {
292 char c = [str characterAtIndex:i];
293 [res addObject:[NSString stringWithFormat:@"%c", c]];
294 }
295 return (NSObject *)res;
296 } else if ([args[0] isKindOfClass:[NSNull class]]) {
297 return (NSObject *)args[0];
298 } else {
299 @throw @"seq: called on non-sequence";
300 }
301 },
302
303 @"meta": ^id (NSArray *args){
304 if ([args[0] isKindOfClass:[MalFunc class]]) {
305 return [(MalFunc *)args[0] meta];
306 } else {
307 id res = objc_getAssociatedObject(args[0], @"meta");
308 return res ? res : (NSObject *)[NSNull alloc];
309 }
310 },
311 @"with-meta": ^id (NSArray *args){
312 if ([args[0] isKindOfClass:[MalFunc class]]) {
313 MalFunc * cmf = [(MalFunc *)args[0] copy];
314 cmf.meta = args[1];
315 return cmf;
316 } else if (!block_Q(args[0])) {
317 id res = [args[0] copy];
318 objc_setAssociatedObject(res, @"meta", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
319 return res;
320 } else {
321 id (^blk)(NSArray *args) = args[0];
322 id (^wrapBlock)(NSArray *args) = ^id (NSArray *args) { return blk(args); };
323 id (^res)(NSArray *args) = [wrapBlock copy]; // under mrc: copy to get a malloc block instead of a stack block.
324 objc_setAssociatedObject(res, @"meta", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
325 return res;
326 }
327 },
328 @"atom": ^(NSArray *args){
329 return [MalAtom fromObject:args[0]];
330 },
331 @"atom?": ^(NSArray *args){
332 return wrap_tf(atom_Q(args[0]));
333 },
334 @"deref": ^(NSArray *args){
335 return [(MalAtom *)args[0] val];
336 },
337 @"reset!": ^(NSArray *args){
338 MalAtom * atm = (MalAtom *)args[0];
339 return atm.val = args[1];
340 },
341 @"swap!": ^(NSArray *args){
342 MalAtom * atm = (MalAtom *)args[0];
343 NSObject * (^ f)(NSArray *) = args[1];
344 NSMutableArray * fargs = [NSMutableArray array];
345 [fargs addObject:atm.val];
346 if ([args count] > 2) {
347 NSRange r = NSMakeRange(2, [args count]-2);
348 [fargs addObjectsFromArray:[args subarrayWithRange:r]];
349 }
350 return atm.val = apply(f, fargs);
351 },
352 };
353 }
354
355 @end