Commit | Line | Data |
---|---|---|
57350ed7 JM |
1 | #import <Foundation/Foundation.h> |
2 | ||
7cae6e6f | 3 | #import "mal_readline.h" |
57350ed7 | 4 | #import "types.h" |
7cae6e6f | 5 | #import "reader.h" |
57350ed7 | 6 | #import "printer.h" |
7cae6e6f | 7 | #import "malfunc.h" |
57350ed7 | 8 | #import "core.h" |
24c6bbf7 | 9 | #import <objc/runtime.h> |
57350ed7 JM |
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 | }, | |
7cae6e6f JM |
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 | }, | |
677cfe0c DM |
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 | }, | |
57350ed7 JM |
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 | }, | |
7cae6e6f JM |
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 | }, | |
57350ed7 JM |
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 | }, | |
7cae6e6f JM |
128 | @"time-ms": ^(NSArray *args){ |
129 | long long ms = [[NSDate date] timeIntervalSince1970] * 1000; | |
130 | return [NSNumber numberWithUnsignedInteger:ms]; | |
131 | }, | |
57350ed7 JM |
132 | |
133 | @"list": ^(NSArray *args){ | |
134 | return args; | |
135 | }, | |
136 | @"list?": ^(NSArray *args){ | |
137 | return wrap_tf(list_Q(args[0])); | |
138 | }, | |
7cae6e6f JM |
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 | }, | |
57350ed7 | 187 | |
7cae6e6f JM |
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 | }, | |
57350ed7 JM |
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 | }, | |
7cae6e6f JM |
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)]; | |
24c6bbf7 | 272 | return (NSObject *)[MalVector fromArray:res]; |
7cae6e6f JM |
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 | }, | |
57350ed7 | 302 | |
24c6bbf7 | 303 | @"meta": ^id (NSArray *args){ |
7cae6e6f JM |
304 | if ([args[0] isKindOfClass:[MalFunc class]]) { |
305 | return [(MalFunc *)args[0] meta]; | |
306 | } else { | |
24c6bbf7 | 307 | id res = objc_getAssociatedObject(args[0], @"meta"); |
308 | return res ? res : (NSObject *)[NSNull alloc]; | |
7cae6e6f JM |
309 | } |
310 | }, | |
24c6bbf7 | 311 | @"with-meta": ^id (NSArray *args){ |
7cae6e6f JM |
312 | if ([args[0] isKindOfClass:[MalFunc class]]) { |
313 | MalFunc * cmf = [(MalFunc *)args[0] copy]; | |
314 | cmf.meta = args[1]; | |
315 | return cmf; | |
24c6bbf7 | 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; | |
7cae6e6f | 320 | } else { |
24c6bbf7 | 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; | |
7cae6e6f JM |
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 | }, | |
57350ed7 JM |
352 | }; |
353 | } | |
354 | ||
355 | @end |