2016-03-04 08:08:47 +03:00
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
|
2016-03-04 10:56:54 +03:00
|
|
|
#import "mal_readline.h"
|
2016-03-04 08:08:47 +03:00
|
|
|
#import "types.h"
|
2016-03-04 10:56:54 +03:00
|
|
|
#import "reader.h"
|
2016-03-04 08:08:47 +03:00
|
|
|
#import "printer.h"
|
2016-03-04 10:56:54 +03:00
|
|
|
#import "malfunc.h"
|
2016-03-04 08:08:47 +03:00
|
|
|
#import "core.h"
|
2018-12-18 09:05:56 +03:00
|
|
|
#import <objc/runtime.h>
|
2016-03-04 08:08:47 +03:00
|
|
|
|
|
|
|
NSObject * wrap_tf(BOOL val) {
|
|
|
|
return val ? [MalTrue alloc] : [MalFalse alloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
@implementation Core
|
|
|
|
|
|
|
|
+ (NSDictionary *)ns {
|
|
|
|
return @{
|
|
|
|
@"=": ^(NSArray *args){
|
|
|
|
return wrap_tf(equal_Q(args[0], args[1]));
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"throw": ^(NSArray *args){
|
|
|
|
@throw args[0];
|
|
|
|
},
|
|
|
|
|
|
|
|
@"nil?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[NSNull class]]);
|
|
|
|
},
|
|
|
|
@"true?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[MalTrue class]]);
|
|
|
|
},
|
|
|
|
@"false?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[MalFalse class]]);
|
|
|
|
},
|
|
|
|
@"string?": ^(NSArray *args){
|
|
|
|
return wrap_tf(string_Q(args[0]));
|
|
|
|
},
|
|
|
|
@"symbol": ^(NSArray *args){
|
|
|
|
return [MalSymbol stringWithString:args[0]];
|
|
|
|
},
|
|
|
|
@"symbol?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[MalSymbol class]]);
|
|
|
|
},
|
|
|
|
@"keyword": ^(NSArray *args){
|
2024-08-27 13:10:42 +03:00
|
|
|
if (string_Q(args[0])) {
|
|
|
|
return [NSString stringWithFormat:@"\u029e%@", args[0]];
|
|
|
|
} else {
|
|
|
|
return args[0];
|
|
|
|
}
|
2016-03-04 10:56:54 +03:00
|
|
|
},
|
|
|
|
@"keyword?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[NSString class]] &&
|
|
|
|
![args[0] isKindOfClass:[MalSymbol class]] &&
|
|
|
|
!string_Q(args[0]));
|
|
|
|
},
|
2017-10-24 16:09:47 +03:00
|
|
|
@"number?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[NSNumber class]]);
|
|
|
|
},
|
|
|
|
@"fn?": ^(NSArray *args){
|
|
|
|
return wrap_tf(block_Q(args[0]) ||
|
|
|
|
([args[0] isKindOfClass:[MalFunc class]] && ![(MalFunc *)args[0] isMacro]));
|
|
|
|
},
|
|
|
|
@"macro?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[MalFunc class]] && [(MalFunc *)args[0] isMacro]);
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
|
|
|
|
@"pr-str": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (id e in args) { [res addObject:_pr_str(e,true)]; }
|
|
|
|
return [res componentsJoinedByString:@" "];
|
|
|
|
},
|
|
|
|
@"str": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (id e in args) { [res addObject:_pr_str(e,false)]; }
|
|
|
|
return [res componentsJoinedByString:@""];
|
|
|
|
},
|
|
|
|
@"prn": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (id e in args) { [res addObject:_pr_str(e,true)]; }
|
|
|
|
printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
|
|
|
|
fflush(stdout);
|
|
|
|
return [NSNull alloc];
|
|
|
|
},
|
|
|
|
@"println": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (id e in args) { [res addObject:_pr_str(e,false)]; }
|
|
|
|
printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
|
|
|
|
fflush(stdout);
|
|
|
|
return [NSNull alloc];
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"read-string": ^(NSArray *args){
|
|
|
|
return read_str(args[0]);
|
|
|
|
},
|
|
|
|
@"readline": ^(NSArray *args){
|
|
|
|
char * rawline = _readline((char *)[(NSString *)args[0] UTF8String]);
|
|
|
|
if (rawline) {
|
|
|
|
return (NSObject *)[NSString stringWithUTF8String:rawline];
|
|
|
|
} else {
|
|
|
|
return (NSObject *)[NSNull alloc];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
@"slurp": ^(NSArray *args){
|
|
|
|
return [NSString stringWithContentsOfFile:args[0]
|
|
|
|
encoding: NSUTF8StringEncoding
|
|
|
|
error: NULL];
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
|
|
|
|
@"<": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] intValue] < [args[1] intValue]);
|
|
|
|
},
|
|
|
|
@"<=": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] intValue] <= [args[1] intValue]);
|
|
|
|
},
|
|
|
|
@">": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] intValue] > [args[1] intValue]);
|
|
|
|
},
|
|
|
|
@">=": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] intValue] >= [args[1] intValue]);
|
|
|
|
},
|
|
|
|
@"+": ^(NSArray *args){
|
|
|
|
return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
|
|
|
|
},
|
|
|
|
@"-": ^(NSArray *args){
|
|
|
|
return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];
|
|
|
|
},
|
|
|
|
@"*": ^(NSArray *args){
|
|
|
|
return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];
|
|
|
|
},
|
|
|
|
@"/": ^(NSArray *args){
|
|
|
|
return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"time-ms": ^(NSArray *args){
|
|
|
|
long long ms = [[NSDate date] timeIntervalSince1970] * 1000;
|
|
|
|
return [NSNumber numberWithUnsignedInteger:ms];
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
|
|
|
|
@"list": ^(NSArray *args){
|
|
|
|
return args;
|
|
|
|
},
|
|
|
|
@"list?": ^(NSArray *args){
|
|
|
|
return wrap_tf(list_Q(args[0]));
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"vector": ^(NSArray *args){
|
|
|
|
return [MalVector fromArray:args];
|
|
|
|
},
|
|
|
|
@"vector?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[MalVector class]]);
|
|
|
|
},
|
|
|
|
@"hash-map": ^(NSArray *args){
|
|
|
|
return hash_map(args);
|
|
|
|
},
|
|
|
|
@"map?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[NSDictionary class]]);
|
|
|
|
},
|
|
|
|
@"assoc": ^(NSArray *args){
|
|
|
|
NSDictionary * dict = args[0];
|
|
|
|
NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
|
|
|
|
initWithDictionary:dict
|
|
|
|
copyItems:NO];
|
|
|
|
return assoc_BANG(new_dict, _rest(args));
|
|
|
|
},
|
|
|
|
@"dissoc": ^(NSArray *args){
|
|
|
|
NSDictionary * dict = args[0];
|
|
|
|
NSMutableDictionary * new_dict = [[NSMutableDictionary alloc]
|
|
|
|
initWithDictionary:dict
|
|
|
|
copyItems:NO];
|
|
|
|
for (NSString * key in _rest(args)) {
|
|
|
|
[new_dict removeObjectForKey:key];
|
|
|
|
}
|
|
|
|
return new_dict;
|
|
|
|
},
|
|
|
|
@"get": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return (NSObject *)[NSNull alloc];
|
|
|
|
}
|
|
|
|
NSObject * res = ((NSDictionary *)args[0])[args[1]];
|
|
|
|
return res ? res : [NSNull alloc];
|
|
|
|
},
|
|
|
|
@"contains?": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return wrap_tf(false);
|
|
|
|
}
|
|
|
|
return wrap_tf(((NSDictionary *)args[0])[args[1]] != nil);
|
|
|
|
},
|
|
|
|
@"keys": ^(NSArray *args){
|
|
|
|
return [(NSDictionary *)args[0] allKeys];
|
|
|
|
},
|
|
|
|
@"vals": ^(NSArray *args){
|
|
|
|
return [(NSDictionary *)args[0] allValues];
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
|
2016-03-04 10:56:54 +03:00
|
|
|
@"sequential?": ^(NSArray *args){
|
|
|
|
return wrap_tf([args[0] isKindOfClass:[NSArray class]]);
|
|
|
|
},
|
|
|
|
@"cons": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
[res addObject:args[0]];
|
|
|
|
[res addObjectsFromArray:args[1]];
|
|
|
|
return res;
|
|
|
|
},
|
|
|
|
@"concat": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (NSArray * arr in args) {
|
|
|
|
[res addObjectsFromArray:arr];
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
},
|
2020-07-21 19:01:48 +03:00
|
|
|
@"vec": ^(NSArray *args){
|
|
|
|
return [MalVector fromArray:args[0]];
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"nth": ^(NSArray *args){
|
|
|
|
NSArray * lst = (NSArray *)args[0];
|
|
|
|
int idx = [(NSNumber *)args[1] intValue];
|
|
|
|
if (idx < [lst count]) {
|
|
|
|
return lst[idx];
|
|
|
|
} else {
|
|
|
|
@throw @"nth: index out of range";
|
|
|
|
}
|
|
|
|
},
|
|
|
|
@"first": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return (NSObject *)[NSNull alloc];
|
|
|
|
}
|
|
|
|
NSArray * lst = (NSArray *)args[0];
|
|
|
|
if ([lst count] > 0) {
|
|
|
|
return (NSObject *)lst[0];
|
|
|
|
} else {
|
|
|
|
return (NSObject *)[NSNull alloc];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
@"rest": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
NSArray * lst = (NSArray *)args[0];
|
|
|
|
if ([lst count] > 1) {
|
|
|
|
return _rest(lst);
|
|
|
|
} else {
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
@"empty?": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return wrap_tf(true);
|
|
|
|
} else {
|
|
|
|
return wrap_tf([args[0] count] == 0);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
@"count": ^(NSArray *args){
|
|
|
|
if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return @0;
|
|
|
|
} else {
|
|
|
|
return [NSNumber numberWithInt:[args[0] count]];
|
|
|
|
}
|
|
|
|
},
|
2016-03-04 10:56:54 +03:00
|
|
|
@"apply": ^(NSArray *args){
|
|
|
|
NSObject * (^ f)(NSArray *) = args[0];
|
|
|
|
NSMutableArray * fargs = [NSMutableArray array];
|
|
|
|
if ([args count] > 1) {
|
|
|
|
NSRange r = NSMakeRange(1, [args count]-2);
|
|
|
|
[fargs addObjectsFromArray:[args subarrayWithRange:r]];
|
|
|
|
}
|
|
|
|
[fargs addObjectsFromArray:(NSArray *)[args lastObject]];
|
|
|
|
return apply(f, fargs);
|
|
|
|
},
|
|
|
|
@"map": ^(NSArray *args){
|
|
|
|
NSObject * (^ f)(NSArray *) = args[0];
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (NSObject * x in (NSArray *)args[1]) {
|
|
|
|
[res addObject:apply(f, @[x])];
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
},
|
|
|
|
@"conj": ^(NSArray *args){
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
if ([args[0] isKindOfClass:[MalVector class]]) {
|
|
|
|
[res addObjectsFromArray:args[0]];
|
|
|
|
[res addObjectsFromArray:_rest(args)];
|
2018-12-18 09:05:56 +03:00
|
|
|
return (NSObject *)[MalVector fromArray:res];
|
2016-03-04 10:56:54 +03:00
|
|
|
} else {
|
|
|
|
[res addObjectsFromArray:[[_rest(args) reverseObjectEnumerator]
|
|
|
|
allObjects]];
|
|
|
|
[res addObjectsFromArray:args[0]];
|
|
|
|
return (NSObject *)res;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
@"seq": ^(NSArray *args){
|
|
|
|
if (list_Q(args[0])) {
|
|
|
|
if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
|
|
|
|
return (NSObject *)args[0];
|
|
|
|
} else if ([args[0] isKindOfClass:[MalVector class]]) {
|
|
|
|
if ([args[0] count] == 0) { return (NSObject *)[NSNull alloc]; }
|
|
|
|
return (NSObject *)[NSArray arrayWithArray:args[0]];
|
|
|
|
} else if (string_Q(args[0])) {
|
|
|
|
NSString * str = args[0];
|
|
|
|
if ([str length] == 0) { return (NSObject *)[NSNull alloc]; }
|
|
|
|
NSMutableArray * res = [NSMutableArray array];
|
|
|
|
for (int i=0; i < [str length]; i++) {
|
|
|
|
char c = [str characterAtIndex:i];
|
|
|
|
[res addObject:[NSString stringWithFormat:@"%c", c]];
|
|
|
|
}
|
|
|
|
return (NSObject *)res;
|
|
|
|
} else if ([args[0] isKindOfClass:[NSNull class]]) {
|
|
|
|
return (NSObject *)args[0];
|
|
|
|
} else {
|
|
|
|
@throw @"seq: called on non-sequence";
|
|
|
|
}
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
|
2018-12-18 09:05:56 +03:00
|
|
|
@"meta": ^id (NSArray *args){
|
2016-03-04 10:56:54 +03:00
|
|
|
if ([args[0] isKindOfClass:[MalFunc class]]) {
|
|
|
|
return [(MalFunc *)args[0] meta];
|
|
|
|
} else {
|
2018-12-18 09:05:56 +03:00
|
|
|
id res = objc_getAssociatedObject(args[0], @"meta");
|
|
|
|
return res ? res : (NSObject *)[NSNull alloc];
|
2016-03-04 10:56:54 +03:00
|
|
|
}
|
|
|
|
},
|
2018-12-18 09:05:56 +03:00
|
|
|
@"with-meta": ^id (NSArray *args){
|
2016-03-04 10:56:54 +03:00
|
|
|
if ([args[0] isKindOfClass:[MalFunc class]]) {
|
|
|
|
MalFunc * cmf = [(MalFunc *)args[0] copy];
|
|
|
|
cmf.meta = args[1];
|
|
|
|
return cmf;
|
2018-12-18 09:05:56 +03:00
|
|
|
} else if (!block_Q(args[0])) {
|
|
|
|
id res = [args[0] copy];
|
|
|
|
objc_setAssociatedObject(res, @"meta", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
return res;
|
2016-03-04 10:56:54 +03:00
|
|
|
} else {
|
2018-12-18 09:05:56 +03:00
|
|
|
id (^blk)(NSArray *args) = args[0];
|
|
|
|
id (^wrapBlock)(NSArray *args) = ^id (NSArray *args) { return blk(args); };
|
|
|
|
id (^res)(NSArray *args) = [wrapBlock copy]; // under mrc: copy to get a malloc block instead of a stack block.
|
|
|
|
objc_setAssociatedObject(res, @"meta", args[1], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
|
|
return res;
|
2016-03-04 10:56:54 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
@"atom": ^(NSArray *args){
|
|
|
|
return [MalAtom fromObject:args[0]];
|
|
|
|
},
|
|
|
|
@"atom?": ^(NSArray *args){
|
|
|
|
return wrap_tf(atom_Q(args[0]));
|
|
|
|
},
|
|
|
|
@"deref": ^(NSArray *args){
|
|
|
|
return [(MalAtom *)args[0] val];
|
|
|
|
},
|
|
|
|
@"reset!": ^(NSArray *args){
|
|
|
|
MalAtom * atm = (MalAtom *)args[0];
|
|
|
|
return atm.val = args[1];
|
|
|
|
},
|
|
|
|
@"swap!": ^(NSArray *args){
|
|
|
|
MalAtom * atm = (MalAtom *)args[0];
|
|
|
|
NSObject * (^ f)(NSArray *) = args[1];
|
|
|
|
NSMutableArray * fargs = [NSMutableArray array];
|
|
|
|
[fargs addObject:atm.val];
|
|
|
|
if ([args count] > 2) {
|
|
|
|
NSRange r = NSMakeRange(2, [args count]-2);
|
|
|
|
[fargs addObjectsFromArray:[args subarrayWithRange:r]];
|
|
|
|
}
|
|
|
|
return atm.val = apply(f, fargs);
|
|
|
|
},
|
2016-03-04 08:08:47 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|