Created
February 11, 2013 18:27
-
-
Save atg/4756439 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Created by Alex Gordon on 11/02/2013. | |
#import <Foundation/Foundation.h> | |
#pragma mark Objects | |
static BOOL respondsTo(id x, SEL s) { | |
if (!x || !s) | |
return NO; | |
return [x respondsToSelector:s]; | |
} | |
static BOOL isKind(id x, Class cl) { | |
if (!x || !cl) | |
return NO; | |
return [x isKindOfClass:cl]; | |
} | |
static BOOL eq(id a, id b) { | |
if (a == b) return YES; | |
if (a == nil || b == nil) return NO; | |
return [a isEqual:b]; | |
} | |
static BOOL truthy(id x) { | |
if (!x) return NO; | |
if (x == [NSNull null]) return NO; | |
if (respondsTo(x, @selector(length)) && [x length] == 0) return NO; | |
if (respondsTo(x, @selector(count)) && [x count] == 0) return NO; | |
return YES; | |
} | |
static BOOL falsy(id x) { | |
return !truthy(x); | |
} | |
#pragma mark - Charsets | |
static NSCharacterSet* charset(NSString* str) { | |
if (!str) | |
return nil; | |
return [NSCharacterSet characterSetWithCharactersInString:str]; | |
} | |
#pragma mark - Functions | |
typedef id (^Mapping)(id); | |
typedef BOOL (^Predicate)(id); | |
Mapping byFunction(id(*fptr)(id)) { | |
return [^ id (id x) { | |
return fptr(x); | |
} copy]; | |
} | |
Predicate byPredicate(BOOL(*fptr)(id)) { | |
return [^ BOOL (id x) { | |
return fptr(x); | |
} copy]; | |
} | |
#pragma mark - Iterables | |
typedef id<NSObject, NSFastEnumeration> Iter; | |
typedef void (^FMapAdder)(id); | |
typedef void (^FMapCallback)(id, FMapAdder); | |
// Super general array building | |
static NSArray* fmap(Iter it, FMapCallback callback) { | |
NSMutableArray* arr = [NSMutableArray array]; | |
FMapAdder addNew = ^ (id newObject) { | |
if (newObject) | |
[arr addObject:newObject]; | |
}; | |
for (id x in it) { | |
callback(x, addNew); | |
} | |
return arr; | |
} | |
static NSArray* map(Iter it, id(^callback)(id x)) { | |
return fmap(it, ^(id old, FMapAdder addNew) { | |
addNew(callback(old)); | |
}); | |
} | |
static NSArray* filter(Iter it, BOOL(^callback)(id x)) { | |
return fmap(it, ^(id old, FMapAdder addNew) { | |
if (callback(old)) | |
addNew(old); | |
}); | |
} | |
static NSArray* truthies(Iter it) { | |
return filter(it, byPredicate(truthy)); | |
} | |
static NSArray* falsies(Iter it) { | |
return filter(it, byPredicate(falsy)); | |
} | |
static NSString* join(Iter it, NSString* str) { | |
if ([it isKindOfClass:[NSArray class]]) { | |
return [(NSArray*)it componentsJoinedByString:str]; | |
} | |
else { | |
NSMutableArray* arr = nil; | |
if (respondsTo(it, @selector(count))) | |
arr = [[NSMutableArray alloc] initWithCapacity:[(NSArray*)it count]]; | |
else | |
arr = [[NSMutableArray alloc] initWithCapacity:50]; | |
for (id x in it) { | |
[arr addObject:x]; | |
} | |
return [arr componentsJoinedByString:str]; | |
} | |
} | |
#pragma mark - Strings | |
static long len(NSString* str) { | |
return [str length]; | |
} | |
#pragma mark (building) | |
#ifndef fmt | |
#define fmt(...) [NSString stringWithFormat:__VA_ARGS__] | |
#endif | |
static NSString* cat(NSString* a, NSString* b) { | |
a = a ? a : @""; | |
b = b ? b : @""; | |
return [a stringByAppendingString:b]; | |
} | |
static NSString* codepoint(uint32_t c) { | |
if (c == 0) | |
return @""; | |
unichar chars[2] = {0}; | |
long length = CFStringGetSurrogatePairForLongCharacter(c, chars) ? 2 : 1; | |
return [NSString stringWithCharacters:chars length:length]; | |
} | |
static NSString* repeated(NSString* str, long n) { | |
if (!str) | |
return nil; | |
if (n <= 0 || [str length] == 0) | |
return @""; | |
return [@"" stringByPaddingToLength:[str length] * n withString:str startingAtIndex:0]; | |
} | |
#pragma mark (heads and tails) | |
BOOL hasPrefix(NSString* haystack, NSString* needle) { | |
if ([haystack length] < [needle length]) return NO; | |
if (![needle length]) return YES; | |
if (![haystack length]) return YES; | |
if (!haystack) haystack = @""; | |
if (!needle) needle = @""; | |
return [haystack hasPrefix:needle]; | |
} | |
BOOL hasSuffix(NSString* haystack, NSString* needle) { | |
if ([haystack length] < [needle length]) return NO; | |
if (![needle length]) return YES; | |
if (![haystack length]) return YES; | |
if (!haystack) haystack = @""; | |
if (!needle) needle = @""; | |
return [haystack hasSuffix:needle]; | |
} | |
static NSString* ltake(NSString* str, long amount) { | |
if (!str) return nil; | |
if (amount <= 0) return @""; | |
long n = [str length]; | |
if (amount >= n) | |
return str; | |
return [str substringWithRange:NSMakeRange(0, amount)]; | |
} | |
static NSString* ldrop(NSString* str, long amount) { | |
if (!str) return nil; | |
if (amount <= 0) return str; | |
long n = [str length]; | |
if (amount >= n) | |
return @""; | |
return [str substringWithRange:NSMakeRange(amount, n - amount)]; | |
} | |
static NSString* rtake(NSString* str, long amount) { | |
if (!str) return nil; | |
if (amount <= 0) return @""; | |
long n = [str length]; | |
if (amount >= n) | |
return str; | |
return [str substringWithRange:NSMakeRange(n - amount, amount)]; | |
} | |
static NSString* rdrop(NSString* str, long amount) { | |
if (!str) return nil; | |
if (amount <= 0) return str; | |
long n = [str length]; | |
if (amount >= n) | |
return @""; | |
return [str substringWithRange:NSMakeRange(0, n - amount)]; | |
} | |
#pragma (transforms) | |
static NSString* toUpper(NSString* str) { | |
return [str uppercaseString]; | |
} | |
static NSString* toLower(NSString* str) { | |
return [str lowercaseString]; | |
} | |
static NSString* replace(NSString* haystack, NSString* needle, NSString* replacement) { | |
if (!haystack) | |
return nil; | |
if (![haystack length]) | |
return @""; | |
return [haystack stringByReplacingOccurrencesOfString:needle withString:replacement ?: @""]; | |
} | |
static NSArray* split(NSString* haystack, id needle) { | |
if (!needle) | |
return nil; | |
if (!haystack) | |
return nil; | |
if (![haystack length]) | |
return @[]; | |
if ([needle isKindOfClass:[NSString class]]) | |
return [haystack componentsSeparatedByString:(NSString *)needle]; | |
if ([needle isKindOfClass:[NSCharacterSet class]]) | |
return [haystack componentsSeparatedByCharactersInSet:(NSCharacterSet*)needle]; | |
return nil; | |
} | |
#pragma mark (trimming) | |
static NSString* stripWhitespace(NSString* str) { | |
static NSCharacterSet* whitespace; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
whitespace = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r\f"]; | |
}); | |
return [str stringByTrimmingCharactersInSet:whitespace]; | |
} | |
static NSArray* splitAndStrip(NSString* str, NSString* by) { | |
if (!str) return nil; | |
return filter(map(split(str, by), ^id(id x) { | |
return stripWhitespace(x); | |
}), ^BOOL(id x) { | |
return [(NSString*)x length] > 0; | |
}); | |
} | |
static NSString* trim(NSString* str, NSCharacterSet* charset) { | |
return [str stringByTrimmingCharactersInSet:charset]; | |
} | |
static NSString* ltrim(NSString* str, NSCharacterSet* charset) { | |
if (!str) | |
return nil; | |
charset = [charset invertedSet]; | |
NSRange r = [str rangeOfCharacterFromSet:charset]; | |
if (r.location == NSNotFound) | |
return @""; | |
if (r.location == 0) | |
return str; | |
return ldrop(str, r.location); | |
} | |
static NSString* rtrim(NSString* str, NSCharacterSet* charset) { | |
if (!str) | |
return nil; | |
charset = [charset invertedSet]; | |
NSRange r = [str rangeOfCharacterFromSet:charset options:NSBackwardsSearch]; | |
if (r.location == NSNotFound) | |
return @""; | |
if (NSMaxRange(r) == [str length]) | |
return str; | |
return ltake(str, NSMaxRange(r)); | |
} | |
#define TEST_STR_ABBR | |
#ifdef TEST_STR_ABBR | |
int main(int argc, const char * argv[]) { @autoreleasepool { | |
int successes = 0; | |
int failures = 0; | |
#define ass(x) do { BOOL b = !!(x); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s) should be TRUE\n", __LINE__, #x); } } while (0) | |
#define nass(x) do { BOOL b = !(x); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s) should be FALSE\n", __LINE__, #x); } } while (0) | |
#define asseq(x, y) do { BOOL b = eq(x, y); if (b) successes++; else { failures++; NSLog(@"Test failure on line %d: (%s != %s), that is [%@ != %@]\n", __LINE__, #x, #y, x, y); } } while (0) | |
NSArray* abcd = @[@"a", @"b", @"c", @"d"]; | |
NSArray* ABCD = @[@"A", @"B", @"C", @"D"]; | |
NSArray* a_c_ = @[@"a", @"", @"c", @""]; | |
NSArray* ac = @[@"a", @"c"]; | |
ass(!respondsTo(nil, NULL)); | |
ass(!respondsTo(@"", NULL)); | |
ass(!respondsTo(nil, @selector(class))); | |
ass(!respondsTo(@"", @selector(awijdoajwd))); | |
ass(respondsTo(@[], @selector(count))); | |
ass(!respondsTo(@"", @selector(count))); | |
ass(eq(@"", @"")); | |
ass(!eq(nil, @"")); | |
ass(!eq(@"", nil)); | |
ass(eq(nil, nil)); | |
ass(!eq(@"", @[])); | |
ass(eq(@[], @[])); | |
ass(!eq(@"a", @"b")); | |
ass(len(nil) == 0); | |
ass(len(@"") == 0); | |
ass(len(@"a") == 1); | |
ass(len(@"waffles") == 7); | |
asseq(fmt(@""), @""); | |
asseq(fmt(@"%d", 1), @"1"); | |
asseq(fmt(@"%.1lf", 0.7), @"0.7"); | |
asseq(cat(@"a", @"b"), @"ab"); | |
asseq(cat(@"", @"b"), @"b"); | |
asseq(cat(@"a", @""), @"a"); | |
asseq(cat(@"", @""), @""); | |
asseq(cat(nil, @"b"), @"b"); | |
asseq(cat(@"a", nil), @"a"); | |
asseq(cat(nil, nil), @""); | |
asseq(codepoint('a'), @"a"); | |
asseq(codepoint(0), @""); | |
asseq(repeated(@"abc", -1), @""); | |
asseq(repeated(@"abc", 0), @""); | |
asseq(repeated(@"abc", 1), @"abc"); | |
asseq(repeated(@"abc", 4), @"abcabcabcabc"); | |
asseq(stripWhitespace(nil), nil); | |
asseq(stripWhitespace(@""), @""); | |
asseq(stripWhitespace(@" \t \f\t\n\ra b c \t \f\t\n\r"), @"a b c"); | |
asseq(splitAndStrip(nil, @","), nil); | |
asseq(splitAndStrip(@"", @","), @[]); | |
asseq(splitAndStrip(@" \t \f\t\n\ra \t \f\t\n\r, \t \f\t\n\r b \t \f\t\n\r, c,d \t \f\t\n\r", @","), abcd); | |
asseq(trim(nil, charset(@" \t")), nil); | |
asseq(trim(@"", charset(@" \t")), @""); | |
asseq(trim(@" \t a b c \t ", charset(@" \t")), @"a b c"); | |
asseq(ltrim(nil, charset(@" \t")), nil); | |
asseq(ltrim(@"", charset(@" \t")), @""); | |
asseq(ltrim(@" \t a b c \t ", charset(@" \t")), @"a b c \t "); | |
asseq(ltrim(@"xyxaxbxcxyx", charset(@"xy")), @"axbxcxyx"); | |
asseq(rtrim(nil, charset(@" \t")), nil); | |
asseq(rtrim(@"", charset(@" \t")), @""); | |
asseq(rtrim(@" \t a b c \t ", charset(@" \t")), @" \t a b c"); | |
ass(hasPrefix(nil, nil)); | |
ass(hasPrefix(nil, @"")); | |
ass(!hasPrefix(nil, @"a")); | |
ass(hasPrefix(@"", nil)); | |
ass(hasPrefix(@"", @"")); | |
ass(!hasPrefix(@"", @"a")); | |
ass(hasPrefix(@"a", nil)); | |
ass(hasPrefix(@"a", @"")); | |
ass(hasPrefix(@"a", @"a")); | |
ass(hasPrefix(@"abc", nil)); | |
ass(hasPrefix(@"abc", @"")); | |
ass(hasPrefix(@"abc", @"a")); | |
ass(hasPrefix(@"cba", nil)); | |
ass(hasPrefix(@"cba", @"")); | |
ass(!hasPrefix(@"cba", @"a")); | |
asseq(ltake(nil, -1), nil); | |
asseq(ltake(nil, 0), nil); | |
asseq(ltake(nil, 1), nil); | |
asseq(ltake(@"", -1), @""); | |
asseq(ltake(@"", 0), @""); | |
asseq(ltake(@"", 1), @""); | |
asseq(ltake(@"a", -1), @""); | |
asseq(ltake(@"a", 0), @""); | |
asseq(ltake(@"a", 1), @"a"); | |
asseq(ltake(@"a", 2), @"a"); | |
asseq(ltake(@"abc", -1), @""); | |
asseq(ltake(@"abc", 0), @""); | |
asseq(ltake(@"abc", 1), @"a"); | |
asseq(ltake(@"abc", 2), @"ab"); | |
asseq(ltake(@"abc", 3), @"abc"); | |
asseq(ltake(@"abc", 4), @"abc"); | |
asseq(rtake(@"abc", -1), @""); | |
asseq(rtake(@"abc", 0), @""); | |
asseq(rtake(@"abc", 1), @"c"); | |
asseq(rtake(@"abc", 2), @"bc"); | |
asseq(rtake(@"abc", 3), @"abc"); | |
asseq(rtake(@"abc", 4), @"abc"); | |
asseq(ldrop(@"abc", -1), @"abc"); | |
asseq(ldrop(@"abc", 0), @"abc"); | |
asseq(ldrop(@"abc", 1), @"bc"); | |
asseq(ldrop(@"abc", 2), @"c"); | |
asseq(ldrop(@"abc", 3), @""); | |
asseq(ldrop(@"abc", 4), @""); | |
asseq(rdrop(@"abc", -1), @"abc"); | |
asseq(rdrop(@"abc", 0), @"abc"); | |
asseq(rdrop(@"abc", 1), @"ab"); | |
asseq(rdrop(@"abc", 2), @"a"); | |
asseq(rdrop(@"abc", 3), @""); | |
asseq(rdrop(@"abc", 4), @""); | |
asseq(toUpper(nil), nil); | |
asseq(toUpper(@""), @""); | |
asseq(toUpper(@"a"), @"A"); | |
asseq(toUpper(@"A"), @"A"); | |
asseq(toUpper(@"aA_"), @"AA_"); | |
asseq(toLower(nil), nil); | |
asseq(toLower(@""), @""); | |
asseq(toLower(@"a"), @"a"); | |
asseq(toLower(@"A"), @"a"); | |
asseq(toLower(@"aA_"), @"aa_"); | |
asseq(replace(nil, nil, nil), nil); | |
asseq(replace(@"", @"", @""), @""); | |
asseq(replace(@"a", @"a", @"a"), @"a"); | |
asseq(replace(@"abcabcabc", @"b", @"X"), @"aXcaXcaXc"); | |
asseq(replace(@"abcabcabc", @"b", @""), @"acacac"); | |
asseq(split(nil, @","), nil); | |
asseq(split(@"", @","), @[]); | |
asseq(split(@"a,b,c,d", @","), abcd); | |
asseq(map(abcd, byFunction(toUpper)), ABCD); | |
asseq(filter(a_c_, ^ BOOL (id x) { return len(x) > 0; }), ac); | |
asseq(join(abcd, @""), @"abcd"); | |
printf("\n%d failures, out of %d tests (%d%% pass rate)\n", failures, successes + failures, (int)floor(100.0 * (((double)successes) / (successes + failures)))); | |
} return 0; } | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment