├── .gitignore ├── GeneratorTests.m ├── LICENSE ├── MAGenerator.h ├── MAGenerator.m ├── MAGenerator.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | DerivedData 4 | xcuserdata 5 | -------------------------------------------------------------------------------- /GeneratorTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // GeneratorTests.m 3 | // MAGenerator 4 | // 5 | // Created by Michael Ash on 10/21/09. 6 | // 7 | 8 | #import "MAGenerator.h" 9 | 10 | GENERATOR_DECL(int, Primes(void), (void)); 11 | GENERATOR(int, Primes(void), (void)) 12 | { 13 | __block int n; 14 | __block int i; 15 | GENERATOR_BEGIN(void) 16 | { 17 | for(n = 2; ; n++) 18 | { 19 | for(i = 2; i < n; i++) 20 | if(n % i == 0) 21 | break; 22 | if(i == n) 23 | GENERATOR_YIELD(n); 24 | } 25 | } 26 | GENERATOR_END 27 | } 28 | 29 | GENERATOR_DECL(NSArray *, ArrayBuilder(void), (id obj)); 30 | GENERATOR(NSArray *, ArrayBuilder(void), (id obj)) 31 | { 32 | __block NSMutableArray *array = nil; 33 | GENERATOR_BEGIN(id obj) 34 | { 35 | array = [[NSMutableArray alloc] init]; 36 | for(;;) 37 | if(obj) 38 | { 39 | [array addObject: obj]; 40 | GENERATOR_YIELD((NSArray *)array); 41 | } 42 | } 43 | GENERATOR_CLEANUP 44 | { 45 | NSLog(@"Cleaning up"); 46 | } 47 | GENERATOR_END 48 | } 49 | 50 | GENERATOR_DECL(NSString *, WordParser(void), (unichar ch)); 51 | GENERATOR(NSString *, WordParser(void), (unichar ch)) 52 | { 53 | NSMutableString *buffer = [NSMutableString string]; 54 | NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 55 | GENERATOR_BEGIN(unichar ch) 56 | { 57 | for(;;) 58 | { 59 | if(ch == 0 || [whitespace characterIsMember: ch]) 60 | { 61 | GENERATOR_YIELD([buffer length] ? (NSString *)buffer : nil); 62 | [buffer setString: @""]; 63 | } 64 | else 65 | { 66 | [buffer appendFormat: @"%C", ch]; 67 | GENERATOR_YIELD((NSString *)nil); 68 | } 69 | } 70 | } 71 | GENERATOR_END 72 | } 73 | 74 | GENERATOR_DECL(int, Counter(int start, int end), (void)); 75 | GENERATOR(int, Counter(int start, int end), (void)) 76 | { 77 | __block int n; 78 | GENERATOR_BEGIN(void) 79 | { 80 | for(n = start; n <= end; n++) 81 | GENERATOR_YIELD(n); 82 | for(;;) 83 | GENERATOR_YIELD(-1); 84 | } 85 | GENERATOR_END 86 | } 87 | 88 | GENERATOR_DECL(id, FileFinder(NSString *path, NSString *extension), (void)); 89 | GENERATOR(id, FileFinder(NSString *path, NSString *extension), (void)) 90 | { 91 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath: path]; 92 | __block NSString *subpath; 93 | GENERATOR_BEGIN(void) 94 | { 95 | while((subpath = [enumerator nextObject])) 96 | { 97 | if([[subpath pathExtension] isEqualToString: extension]) 98 | GENERATOR_YIELD((id)[path stringByAppendingPathComponent: subpath]); 99 | } 100 | } 101 | GENERATOR_END 102 | } 103 | 104 | GENERATOR_DECL(int, RLEDecoder(void (^emit)(char)), (unsigned char byte)); 105 | GENERATOR(int, RLEDecoder(void (^emit)(char)), (unsigned char byte)) 106 | { 107 | __block unsigned char count; 108 | GENERATOR_BEGIN(unsigned char byte) 109 | { 110 | while(1) 111 | { 112 | count = byte; 113 | GENERATOR_YIELD(0); 114 | 115 | while(count--) 116 | emit(byte); 117 | GENERATOR_YIELD(0); 118 | } 119 | } 120 | GENERATOR_END 121 | } 122 | 123 | static void AppendByte(NSMutableData *data, char byte); 124 | void AppendByte(NSMutableData *data, char byte) 125 | { 126 | [data appendBytes: &byte length: 1]; 127 | } 128 | 129 | static NSString *SafeUTF8String(NSData *data); 130 | NSString *SafeUTF8String(NSData *data) 131 | { 132 | NSString *str = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; 133 | if(!str) 134 | str = [[NSString alloc] initWithData: data encoding: NSISOLatin1StringEncoding]; 135 | if(!str) 136 | str = [[NSString alloc] initWithData: data encoding: NSMacOSRomanStringEncoding]; 137 | return str; 138 | } 139 | 140 | GENERATOR_DECL(int, HTTPParser(void (^responseCallback)(NSString *), void (^headerCallback)(NSDictionary *), 141 | void (^bodyCallback)(NSData *), void (^errorCallback)(NSString *)), (int byte)); 142 | GENERATOR(int, HTTPParser(void (^responseCallback)(NSString *), void (^headerCallback)(NSDictionary *), 143 | void (^bodyCallback)(NSData *), void (^errorCallback)(NSString *)), (int byte)) 144 | { 145 | NSMutableData *responseData = [NSMutableData data]; 146 | 147 | NSMutableDictionary *headers = [NSMutableDictionary dictionary]; 148 | __block NSMutableData *currentHeaderData = nil; 149 | __block NSString *currentHeaderKey = nil; 150 | 151 | NSMutableData *bodyData = [NSMutableData data]; 152 | 153 | GENERATOR_BEGIN(char byte) 154 | { 155 | // read response line 156 | while(byte != '\r') 157 | { 158 | AppendByte(responseData, byte); 159 | GENERATOR_YIELD(0); 160 | } 161 | responseCallback(SafeUTF8String(responseData)); 162 | GENERATOR_YIELD(0); // eat the \r 163 | if(byte != '\n') 164 | errorCallback(@"bad CRLF after response line"); 165 | GENERATOR_YIELD(0); // eat the \n 166 | 167 | // read headers 168 | while(1) 169 | { 170 | currentHeaderData = [[NSMutableData alloc] init]; 171 | while(byte != ':' && byte != '\r') 172 | { 173 | AppendByte(currentHeaderData, byte); 174 | GENERATOR_YIELD(0); 175 | } 176 | 177 | // empty line means we're done with headers 178 | if(byte == '\r' && [currentHeaderData length] == 0) 179 | break; 180 | else if(byte == '\r') 181 | errorCallback(@"No colon found in header line"); 182 | else 183 | { 184 | GENERATOR_YIELD(0); 185 | if(byte == ' ') 186 | GENERATOR_YIELD(0); 187 | 188 | currentHeaderKey = [SafeUTF8String(currentHeaderData) copy]; 189 | 190 | currentHeaderData = [[NSMutableData alloc] init]; 191 | while(byte != '\r') 192 | { 193 | AppendByte(currentHeaderData, byte); 194 | GENERATOR_YIELD(0); 195 | } 196 | 197 | NSString *currentHeaderValue = SafeUTF8String(currentHeaderData); 198 | 199 | [headers setObject: currentHeaderValue forKey: currentHeaderKey]; 200 | } 201 | GENERATOR_YIELD(0); 202 | if(byte != '\n') 203 | errorCallback(@"bad CRLF after header line"); 204 | GENERATOR_YIELD(0); // eat the \n 205 | } 206 | headerCallback(headers); 207 | 208 | // read body 209 | while(byte != -1) 210 | { 211 | AppendByte(bodyData, byte); 212 | GENERATOR_YIELD(0); 213 | } 214 | bodyCallback(bodyData); 215 | } 216 | GENERATOR_CLEANUP 217 | { 218 | } 219 | GENERATOR_END 220 | } 221 | 222 | static void TestHTTP(void); 223 | void TestHTTP(void) 224 | { 225 | NSInputStream *is; 226 | NSOutputStream *os; 227 | [NSStream getStreamsToHost: [NSHost hostWithName: @"www.google.com"] port: 80 inputStream: &is outputStream: &os]; 228 | 229 | [is open]; 230 | [os open]; 231 | 232 | char *writeBytes = "GET / HTTP/1.0\r\n\r\n"; 233 | NSInteger toWrite = strlen(writeBytes); 234 | while(toWrite) 235 | { 236 | NSInteger written = [os write: (uint8_t *)writeBytes maxLength: toWrite]; 237 | if(written < 0) 238 | { 239 | perror("write"); 240 | exit(1); 241 | } 242 | toWrite -= written; 243 | writeBytes += written; 244 | } 245 | 246 | int (^parser)(int) = HTTPParser( 247 | ^(NSString *response) { 248 | NSLog(@"Got response: %@", response); 249 | }, 250 | ^(NSDictionary *headers) { 251 | NSLog(@"Got headers: %@", headers); 252 | }, 253 | ^(NSData *body) { 254 | NSLog(@"Got %ld bytes of body", (long)[body length]); 255 | }, 256 | ^(NSString *error) { 257 | NSLog(@"Got error %@", error); 258 | }); 259 | 260 | uint8_t byte; 261 | NSInteger amt; 262 | while((amt = [is read: &byte maxLength: 1])) 263 | { 264 | if(amt < 0) 265 | { 266 | perror("read"); 267 | exit(1); 268 | } 269 | parser(byte); 270 | } 271 | parser(-1); 272 | } 273 | 274 | int main(int argc, char **argv) 275 | { 276 | @autoreleasepool 277 | { 278 | int (^primes)(void) = Primes(); 279 | for(int i = 0; i < 10; i++) 280 | NSLog(@"%d", primes()); 281 | 282 | NSArray *(^builder)(id) = ArrayBuilder(); 283 | NSLog(@"%@", builder(@"hello")); 284 | NSLog(@"%@", builder(@"world")); 285 | NSLog(@"%@", builder(@"how")); 286 | NSLog(@"%@", builder(@"are")); 287 | NSLog(@"%@", builder(@"you?")); 288 | 289 | NSString *(^wordParser)(unichar ch) = WordParser(); 290 | NSLog(@"%@", wordParser('h')); 291 | NSLog(@"%@", wordParser('e')); 292 | NSLog(@"%@", wordParser('l')); 293 | NSLog(@"%@", wordParser('l')); 294 | NSLog(@"%@", wordParser('o')); 295 | NSLog(@"%@", wordParser(' ')); 296 | NSLog(@"%@", wordParser('w')); 297 | NSLog(@"%@", wordParser('o')); 298 | NSLog(@"%@", wordParser('r')); 299 | NSLog(@"%@", wordParser('l')); 300 | NSLog(@"%@", wordParser('d')); 301 | NSLog(@"%@", wordParser('!')); 302 | NSLog(@"%@", wordParser(0)); 303 | 304 | int (^counter)(void) = Counter(5, 10); 305 | for(int i = 0; i < 10; i++) 306 | NSLog(@"%d", counter()); 307 | 308 | int i = 0; 309 | for(NSString *path in MAGeneratorEnumerator(FileFinder(@"/Applications", @"app"))) 310 | { 311 | NSLog(@"%@", path); 312 | if(++i >= 10) 313 | break; 314 | } 315 | 316 | NSMutableData *data = [NSMutableData data]; 317 | int (^rleDecoder)(unsigned char) = RLEDecoder(^(char byte) { [data appendBytes: &byte length: 1]; }); 318 | rleDecoder(3); 319 | rleDecoder('a'); 320 | rleDecoder(1); 321 | rleDecoder('X'); 322 | rleDecoder(5); 323 | rleDecoder('0'); 324 | NSLog(@"%@", data); 325 | 326 | TestHTTP(); 327 | } 328 | return 0; 329 | } 330 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MAGenerator is provided under the MIT license. 2 | 3 | 4 | Copyright (c) 2009 Michael Ash 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /MAGenerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // MAGenerator.h 3 | // MAGenerator 4 | // 5 | // Created by Michael Ash on 10/21/09. 6 | // 7 | 8 | #import 9 | 10 | 11 | #define GENERATOR_DECL(returnType, nameAndCreationParams, perCallParams) \ 12 | returnType (^nameAndCreationParams) perCallParams 13 | 14 | #define GENERATOR(returnType, nameAndCreationParams, perCallParams) \ 15 | returnType (^nameAndCreationParams) perCallParams \ 16 | { \ 17 | returnType GENERATOR_zeroReturnValue; \ 18 | bzero(&GENERATOR_zeroReturnValue, sizeof(GENERATOR_zeroReturnValue)); \ 19 | returnType (^GENERATOR_cleanupBlock)(void) = nil; 20 | 21 | #define GENERATOR_BEGIN(...) \ 22 | __block int GENERATOR_where = -1; \ 23 | NSMutableArray *GENERATOR_cleanupArray = MAGeneratorMakeCleanupArray(); \ 24 | id GENERATOR_mainBlock = ^ (__VA_ARGS__) { \ 25 | [GENERATOR_cleanupArray self]; \ 26 | switch(GENERATOR_where) \ 27 | { \ 28 | case -1: 29 | 30 | #define GENERATOR_YIELD(...) \ 31 | do { \ 32 | GENERATOR_where = __LINE__; \ 33 | return __VA_ARGS__; \ 34 | case __LINE__: ; \ 35 | } while(0) 36 | 37 | #define GENERATOR_CLEANUP \ 38 | } \ 39 | GENERATOR_where = -1; \ 40 | return GENERATOR_zeroReturnValue; \ 41 | }; \ 42 | GENERATOR_cleanupBlock = ^{{ 43 | 44 | #define GENERATOR_END \ 45 | } \ 46 | GENERATOR_where = -1; \ 47 | return GENERATOR_zeroReturnValue; \ 48 | }; \ 49 | if(GENERATOR_cleanupBlock) \ 50 | [GENERATOR_cleanupArray addObject: ^{ GENERATOR_cleanupBlock(); }]; \ 51 | return [GENERATOR_mainBlock copy]; \ 52 | } 53 | 54 | 55 | 56 | NSMutableArray *MAGeneratorMakeCleanupArray(void); 57 | id MAGeneratorEnumerator(id (^generator)(void)); 58 | -------------------------------------------------------------------------------- /MAGenerator.m: -------------------------------------------------------------------------------- 1 | // 2 | // MAGenerator.m 3 | // MAGenerator 4 | // 5 | // Created by Michael Ash on 10/21/09. 6 | // 7 | 8 | #import "MAGenerator.h" 9 | 10 | 11 | static const void *CopyCleanupBlock(CFAllocatorRef allocator, const void *value) 12 | { 13 | return Block_copy(value); 14 | } 15 | 16 | static void CallCleanupBlockAndRelease(CFAllocatorRef allocator, const void *value) 17 | { 18 | if(value) 19 | { 20 | ((__bridge void (^)(void))value)(); 21 | Block_release(value); 22 | } 23 | } 24 | 25 | static CFArrayCallBacks gCleanupCallbacks = { 26 | 0, // version 27 | CopyCleanupBlock, // retain 28 | CallCleanupBlockAndRelease, // release 29 | NULL, // description 30 | NULL // equal 31 | }; 32 | 33 | NSMutableArray *MAGeneratorMakeCleanupArray(void) 34 | { 35 | CFMutableArrayRef array = CFArrayCreateMutable(NULL, 1, &gCleanupCallbacks); 36 | return CFBridgingRelease(array); 37 | } 38 | 39 | 40 | @interface _MAGeneratorEnumerator : NSEnumerator 41 | { 42 | id (^_generator)(void); 43 | } 44 | - (id)initWithGenerator: (id (^)(void))generator; 45 | @end 46 | 47 | @implementation _MAGeneratorEnumerator 48 | 49 | - (id)initWithGenerator: (id (^)(void))generator 50 | { 51 | if((self = [self init])) 52 | _generator = [generator copy]; 53 | return self; 54 | } 55 | 56 | - (id)nextObject 57 | { 58 | return _generator(); 59 | } 60 | 61 | @end 62 | 63 | 64 | id MAGeneratorEnumerator(id (^generator)(void)) 65 | { 66 | return [[_MAGeneratorEnumerator alloc] initWithGenerator: generator]; 67 | } 68 | -------------------------------------------------------------------------------- /MAGenerator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; 11 | C2A3649B1090095D00A1872C /* GeneratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C2A3649A1090095D00A1872C /* GeneratorTests.m */; }; 12 | FA6045BA151906A30026EB93 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA6045B9151906A30026EB93 /* Cocoa.framework */; }; 13 | FA6045C8151906AD0026EB93 /* MAGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = C2A364741090011B00A1872C /* MAGenerator.h */; }; 14 | FA6045C9151906AD0026EB93 /* MAGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = C2A364751090011B00A1872C /* MAGenerator.m */; }; 15 | FA6045DF151906EC0026EB93 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA6045DE151906EC0026EB93 /* Foundation.framework */; }; 16 | FA6045E9151907250026EB93 /* MAGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = C2A364741090011B00A1872C /* MAGenerator.h */; }; 17 | FA6045EA151907250026EB93 /* MAGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = C2A364751090011B00A1872C /* MAGenerator.m */; }; 18 | FA6045ED1519099B0026EB93 /* libMAGenerator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA6045B7151906A30026EB93 /* libMAGenerator.a */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | FA6045EB151909950026EB93 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = FA6045B6151906A30026EB93; 27 | remoteInfo = "MAGenerator-MacOSX"; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXCopyFilesBuildPhase section */ 32 | 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { 33 | isa = PBXCopyFilesBuildPhase; 34 | buildActionMask = 8; 35 | dstPath = /usr/share/man/man1/; 36 | dstSubfolderSpec = 0; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 1; 40 | }; 41 | /* End PBXCopyFilesBuildPhase section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 45 | 8DD76FA10486AA7600D96B5E /* MAGeneratorTests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MAGeneratorTests; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | C2A364741090011B00A1872C /* MAGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MAGenerator.h; sourceTree = ""; }; 47 | C2A364751090011B00A1872C /* MAGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MAGenerator.m; sourceTree = ""; }; 48 | C2A3649A1090095D00A1872C /* GeneratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratorTests.m; sourceTree = ""; }; 49 | C2A368261093AA8800A1872C /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 50 | C2A368271093BFF000A1872C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; 51 | FA6045B7151906A30026EB93 /* libMAGenerator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMAGenerator.a; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | FA6045B9151906A30026EB93 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 53 | FA6045BC151906A30026EB93 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 54 | FA6045BD151906A30026EB93 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 55 | FA6045BE151906A30026EB93 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 56 | FA6045DD151906EC0026EB93 /* libMAGenerator-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libMAGenerator-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | FA6045DE151906EC0026EB93 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | FA6045ED1519099B0026EB93 /* libMAGenerator.a in Frameworks */, 66 | 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | FA6045B4151906A30026EB93 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | FA6045BA151906A30026EB93 /* Cocoa.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | FA6045DA151906EC0026EB93 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | FA6045DF151906EC0026EB93 /* Foundation.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 08FB7794FE84155DC02AAC07 /* MAGenerator */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 08FB7795FE84155DC02AAC07 /* Source */, 93 | C6859EA2029092E104C91782 /* Documentation */, 94 | 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, 95 | FA6045B8151906A30026EB93 /* Frameworks */, 96 | 1AB674ADFE9D54B511CA2CBB /* Products */, 97 | ); 98 | name = MAGenerator; 99 | sourceTree = ""; 100 | }; 101 | 08FB7795FE84155DC02AAC07 /* Source */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | C2A364741090011B00A1872C /* MAGenerator.h */, 105 | C2A364751090011B00A1872C /* MAGenerator.m */, 106 | C2A3649A1090095D00A1872C /* GeneratorTests.m */, 107 | C2A368271093BFF000A1872C /* README.md */, 108 | C2A368261093AA8800A1872C /* LICENSE */, 109 | ); 110 | name = Source; 111 | sourceTree = ""; 112 | }; 113 | 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 08FB779EFE84155DC02AAC07 /* Foundation.framework */, 117 | ); 118 | name = "External Frameworks and Libraries"; 119 | sourceTree = ""; 120 | }; 121 | 1AB674ADFE9D54B511CA2CBB /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 8DD76FA10486AA7600D96B5E /* MAGeneratorTests */, 125 | FA6045B7151906A30026EB93 /* libMAGenerator.a */, 126 | FA6045DD151906EC0026EB93 /* libMAGenerator-iOS.a */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | C6859EA2029092E104C91782 /* Documentation */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | ); 135 | name = Documentation; 136 | sourceTree = ""; 137 | }; 138 | FA6045B8151906A30026EB93 /* Frameworks */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | FA6045B9151906A30026EB93 /* Cocoa.framework */, 142 | FA6045DE151906EC0026EB93 /* Foundation.framework */, 143 | FA6045BB151906A30026EB93 /* Other Frameworks */, 144 | ); 145 | name = Frameworks; 146 | sourceTree = ""; 147 | }; 148 | FA6045BB151906A30026EB93 /* Other Frameworks */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | FA6045BC151906A30026EB93 /* AppKit.framework */, 152 | FA6045BD151906A30026EB93 /* CoreData.framework */, 153 | FA6045BE151906A30026EB93 /* Foundation.framework */, 154 | ); 155 | name = "Other Frameworks"; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXHeadersBuildPhase section */ 161 | FA6045B5151906A30026EB93 /* Headers */ = { 162 | isa = PBXHeadersBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | FA6045C8151906AD0026EB93 /* MAGenerator.h in Headers */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | FA6045DB151906EC0026EB93 /* Headers */ = { 170 | isa = PBXHeadersBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | FA6045E9151907250026EB93 /* MAGenerator.h in Headers */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXHeadersBuildPhase section */ 178 | 179 | /* Begin PBXNativeTarget section */ 180 | 8DD76F960486AA7600D96B5E /* MAGeneratorTests */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "MAGeneratorTests" */; 183 | buildPhases = ( 184 | 8DD76F990486AA7600D96B5E /* Sources */, 185 | 8DD76F9B0486AA7600D96B5E /* Frameworks */, 186 | 8DD76F9E0486AA7600D96B5E /* CopyFiles */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | FA6045EC151909950026EB93 /* PBXTargetDependency */, 192 | ); 193 | name = MAGeneratorTests; 194 | productInstallPath = "$(HOME)/bin"; 195 | productName = MAGenerator; 196 | productReference = 8DD76FA10486AA7600D96B5E /* MAGeneratorTests */; 197 | productType = "com.apple.product-type.tool"; 198 | }; 199 | FA6045B6151906A30026EB93 /* MAGenerator-MacOSX */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = FA6045C5151906A30026EB93 /* Build configuration list for PBXNativeTarget "MAGenerator-MacOSX" */; 202 | buildPhases = ( 203 | FA6045B3151906A30026EB93 /* Sources */, 204 | FA6045B4151906A30026EB93 /* Frameworks */, 205 | FA6045B5151906A30026EB93 /* Headers */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | ); 211 | name = "MAGenerator-MacOSX"; 212 | productName = "MAGenerator-MacOSX"; 213 | productReference = FA6045B7151906A30026EB93 /* libMAGenerator.a */; 214 | productType = "com.apple.product-type.library.static"; 215 | }; 216 | FA6045DC151906EC0026EB93 /* MAGenerator-iOS */ = { 217 | isa = PBXNativeTarget; 218 | buildConfigurationList = FA6045E6151906EC0026EB93 /* Build configuration list for PBXNativeTarget "MAGenerator-iOS" */; 219 | buildPhases = ( 220 | FA6045D9151906EC0026EB93 /* Sources */, 221 | FA6045DA151906EC0026EB93 /* Frameworks */, 222 | FA6045DB151906EC0026EB93 /* Headers */, 223 | ); 224 | buildRules = ( 225 | ); 226 | dependencies = ( 227 | ); 228 | name = "MAGenerator-iOS"; 229 | productName = "MAGenerator-iOS"; 230 | productReference = FA6045DD151906EC0026EB93 /* libMAGenerator-iOS.a */; 231 | productType = "com.apple.product-type.library.static"; 232 | }; 233 | /* End PBXNativeTarget section */ 234 | 235 | /* Begin PBXProject section */ 236 | 08FB7793FE84155DC02AAC07 /* Project object */ = { 237 | isa = PBXProject; 238 | buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "MAGenerator" */; 239 | compatibilityVersion = "Xcode 3.1"; 240 | developmentRegion = English; 241 | hasScannedForEncodings = 1; 242 | knownRegions = ( 243 | en, 244 | ); 245 | mainGroup = 08FB7794FE84155DC02AAC07 /* MAGenerator */; 246 | projectDirPath = ""; 247 | projectRoot = ""; 248 | targets = ( 249 | FA6045B6151906A30026EB93 /* MAGenerator-MacOSX */, 250 | FA6045DC151906EC0026EB93 /* MAGenerator-iOS */, 251 | 8DD76F960486AA7600D96B5E /* MAGeneratorTests */, 252 | ); 253 | }; 254 | /* End PBXProject section */ 255 | 256 | /* Begin PBXSourcesBuildPhase section */ 257 | 8DD76F990486AA7600D96B5E /* Sources */ = { 258 | isa = PBXSourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | C2A3649B1090095D00A1872C /* GeneratorTests.m in Sources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | FA6045B3151906A30026EB93 /* Sources */ = { 266 | isa = PBXSourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | FA6045C9151906AD0026EB93 /* MAGenerator.m in Sources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | FA6045D9151906EC0026EB93 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | FA6045EA151907250026EB93 /* MAGenerator.m in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin PBXTargetDependency section */ 284 | FA6045EC151909950026EB93 /* PBXTargetDependency */ = { 285 | isa = PBXTargetDependency; 286 | target = FA6045B6151906A30026EB93 /* MAGenerator-MacOSX */; 287 | targetProxy = FA6045EB151909950026EB93 /* PBXContainerItemProxy */; 288 | }; 289 | /* End PBXTargetDependency section */ 290 | 291 | /* Begin XCBuildConfiguration section */ 292 | 1DEB927508733DD40010E9CD /* Debug */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | COPY_PHASE_STRIP = NO; 298 | GCC_DYNAMIC_NO_PIC = NO; 299 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 300 | GCC_OPTIMIZATION_LEVEL = 0; 301 | INSTALL_PATH = /usr/local/bin; 302 | PRODUCT_NAME = MAGeneratorTests; 303 | }; 304 | name = Debug; 305 | }; 306 | 1DEB927608733DD40010E9CD /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 312 | INSTALL_PATH = /usr/local/bin; 313 | PRODUCT_NAME = MAGeneratorTests; 314 | }; 315 | name = Release; 316 | }; 317 | 1DEB927908733DD40010E9CD /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 321 | GCC_CW_ASM_SYNTAX = NO; 322 | GCC_C_LANGUAGE_STANDARD = gnu99; 323 | GCC_ENABLE_PASCAL_STRINGS = NO; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; 326 | GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = ( 327 | "NS_BLOCK_ASSERTIONS=1", 328 | "NDEBUG=1", 329 | ); 330 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 331 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 332 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 333 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 334 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 336 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 337 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 338 | GCC_WARN_SHADOW = YES; 339 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 340 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_LABEL = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | ONLY_ACTIVE_ARCH = YES; 345 | SDKROOT = macosx; 346 | WARNING_CFLAGS = ( 347 | "-W", 348 | "-Wall", 349 | "-Wextra", 350 | "-Wno-unused-parameter", 351 | ); 352 | }; 353 | name = Debug; 354 | }; 355 | 1DEB927A08733DD40010E9CD /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 359 | GCC_CW_ASM_SYNTAX = NO; 360 | GCC_C_LANGUAGE_STANDARD = gnu99; 361 | GCC_ENABLE_PASCAL_STRINGS = NO; 362 | GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; 363 | GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = ( 364 | "NS_BLOCK_ASSERTIONS=1", 365 | "NDEBUG=1", 366 | ); 367 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 368 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 369 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 370 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 371 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 373 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 374 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 375 | GCC_WARN_SHADOW = YES; 376 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 377 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 378 | GCC_WARN_UNUSED_FUNCTION = YES; 379 | GCC_WARN_UNUSED_LABEL = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | SDKROOT = macosx; 382 | WARNING_CFLAGS = ( 383 | "-W", 384 | "-Wall", 385 | "-Wextra", 386 | "-Wno-unused-parameter", 387 | ); 388 | }; 389 | name = Release; 390 | }; 391 | FA6045C6151906A30026EB93 /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | COPY_PHASE_STRIP = NO; 397 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | PRODUCT_NAME = MAGenerator; 400 | }; 401 | name = Debug; 402 | }; 403 | FA6045C7151906A30026EB93 /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ALWAYS_SEARCH_USER_PATHS = NO; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 409 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | PRODUCT_NAME = MAGenerator; 412 | }; 413 | name = Release; 414 | }; 415 | FA6045E7151906EC0026EB93 /* Debug */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 420 | CLANG_ENABLE_OBJC_ARC = YES; 421 | COPY_PHASE_STRIP = NO; 422 | DSTROOT = /tmp/MAGenerator_iOS.dst; 423 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 424 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 425 | OTHER_LDFLAGS = "-ObjC"; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | SDKROOT = iphoneos; 428 | SKIP_INSTALL = YES; 429 | }; 430 | name = Debug; 431 | }; 432 | FA6045E8151906EC0026EB93 /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ALWAYS_SEARCH_USER_PATHS = NO; 436 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | COPY_PHASE_STRIP = YES; 439 | DSTROOT = /tmp/MAGenerator_iOS.dst; 440 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 441 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 442 | OTHER_LDFLAGS = "-ObjC"; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | SDKROOT = iphoneos; 445 | SKIP_INSTALL = YES; 446 | VALIDATE_PRODUCT = YES; 447 | }; 448 | name = Release; 449 | }; 450 | /* End XCBuildConfiguration section */ 451 | 452 | /* Begin XCConfigurationList section */ 453 | 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "MAGeneratorTests" */ = { 454 | isa = XCConfigurationList; 455 | buildConfigurations = ( 456 | 1DEB927508733DD40010E9CD /* Debug */, 457 | 1DEB927608733DD40010E9CD /* Release */, 458 | ); 459 | defaultConfigurationIsVisible = 0; 460 | defaultConfigurationName = Release; 461 | }; 462 | 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "MAGenerator" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 1DEB927908733DD40010E9CD /* Debug */, 466 | 1DEB927A08733DD40010E9CD /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | FA6045C5151906A30026EB93 /* Build configuration list for PBXNativeTarget "MAGenerator-MacOSX" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | FA6045C6151906A30026EB93 /* Debug */, 475 | FA6045C7151906A30026EB93 /* Release */, 476 | ); 477 | defaultConfigurationIsVisible = 0; 478 | }; 479 | FA6045E6151906EC0026EB93 /* Build configuration list for PBXNativeTarget "MAGenerator-iOS" */ = { 480 | isa = XCConfigurationList; 481 | buildConfigurations = ( 482 | FA6045E7151906EC0026EB93 /* Debug */, 483 | FA6045E8151906EC0026EB93 /* Release */, 484 | ); 485 | defaultConfigurationIsVisible = 0; 486 | }; 487 | /* End XCConfigurationList section */ 488 | }; 489 | rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; 490 | } 491 | -------------------------------------------------------------------------------- /MAGenerator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MAGenerator: experimental generator support in Objective-C using blocks 2 | ======================================================================= 3 | 4 | Introduction 5 | ------------ 6 | MAGenerator allows easy construction of generators in Objective-C. A generator is essentially a function which remembers its state between calls. A generator uses a 'yield' statement rather than a 'return' statement. When control reaches a 'yield' statement, the generator returns the indicated value, and saves its state. On the next call, execution resumes after the 'yield' statement, rather than at the top. 7 | 8 | Generators can allow for "inside out" programming, lazy evaluation, and asynchronous state machines, while preserving a natural top-to-bottom flow of code. 9 | 10 | 11 | How to Make Generators 12 | ---------------------- 13 | 14 | To create a generator using MAGenerator, start with the GENERATOR macro. The first parameter to this macro is the return type of the generator, which is what it returns on each call. The second parameter is the name of the generator creation function as well as the parameters that it takes, combined using function-declaration syntax. The third parameter is the parameters that the generator itself takes on each call. This is a bit confusing, so here are some examples. 15 | 16 | This is an example of the start of a generator which takes a single parameter for the generator creation function, and no parameters for the generator itself: 17 | 18 | GENERATOR(int, Counter(int start), (void)) 19 | 20 | Here's one which takes two parameters for each: 21 | 22 | GENERATOR(int, Counter(int start, int end), (NSString *label, NSString *name)) 23 | 24 | Note that the generator must return some sort of value. Void is not allowed. If you don't want to return anything, then pick a reasonable type, like int, and then yield a reasonable value, like 0 when you wish to yield. 25 | 26 | Follow the GENERATOR line with local variable declarations. Because of how generators are implemented, local variables MUST be declared here, and not inline with the code. (Doing so will cause their values to be forgotten, with potentially disastrous and hard-to-debug results.) Because generators are implemented using blocks, any local variable which you wish to mutate must be declared with the __block qualifier. Locals may have initializers; they will execute when the generator is created, and can use the generator creator's parameters if needed. 27 | 28 | After declaring your local variables, if any, use the GENERATOR_BEGIN macro to start writing the generator code. The parameters to this are the parameters to the generator itself, and must match the third parameter passed to the GENERATOR macro. 29 | 30 | Within the body of the generator, you can mostly write code like usual. Use the GENERATOR_YIELD macro to yield values. When yielding, control returns to the caller, and the caller receives the value passed to it. If and when the caller calls your generator again, control resumes after that GENERATOR_YIELD. When control resumes, local variables have the same value that they did previously, and generator parameters contain whatever the caller passed to the generator on the latest call. 31 | 32 | If you allow execution to "fall off the bottom" of a generator, the generator will yield 0 (or its equivalent for non-integer types; the generator macros simply zero-fill the return value) and execution on the next invocation, if any, resumes at the top of the generator. 33 | 34 | You must avoid switch statements and for/in loops (regular for loops and other loops are fine) which contain GENERATOR_YIELD invocations. Switch statements don't work because the generator mechanism is implemented using a switch statement, and it interferes. For/in loops involve implicit local variables (to hold the loop state) which will not be preserved across yields. The compiler will not warn about these in most cases, so beware. 35 | 36 | After the body of the generator, you can optionally use GENERATOR_CLEANUP to begin a cleanup block. A cleanup block is executed when the generator block is deallocated. The main purpose of the cleanup block is to manage the memory of __block-qualified object pointers. The compiler will not automatically retain and release __block-qualified object pointer variables, you must do this yourself. Furthermore, it's a bad idea to store autoreleased objects in such variables across yields, because the caller could surround each call to your generator with an autorelease pool. You need to retain these objects (and release them when you reassign them), but you also need to release them when the generator is destroyed, because you can't guarantee that the caller will invoke your generator enough to make it get to the end. (If it even has an end; many generators simply loop forever.) 37 | 38 | Here's how this sort of thing would look: 39 | 40 | __block NSString *str = nil; 41 | GENERATOR_BEGIN(void) 42 | { 43 | ... 44 | [str release]; 45 | str = [[NSString alloc] initWith...]; 46 | ... 47 | } 48 | GENERATOR_CLEANUP 49 | { 50 | [str release]; 51 | } 52 | 53 | Of course the cleanup block can do whatever else may be needed to clean things up as well, such as calling free() on other allocated memory, closing file descriptors, etc. In general, any code that must execute after the last call to the generator block goes here. 54 | 55 | Finally, terminate the generator with GENERATOR_END. You'll know you forgot this part when the compiler starts complaining to you about not supporting nested functions in the code that follows your generator. 56 | 57 | If you need a prototype for your generator, for use in a header file, use the GENERATOR_DECL macro. This macro takes the same parameters as the GENERATOR macro, but will produce output suitable for a function prototype declaration for a header file. 58 | 59 | 60 | How to Use Generators 61 | --------------------- 62 | 63 | The GENERATOR macro defines a function which creates a new instance of the generator. Generators are just blocks. As such, you declare a variable to hold them using standard block syntax, and manage their memory just like any other Objective-C object. Examples: 64 | 65 | int (^counter)(void) = Counter(42); 66 | [_counterIvar release]; 67 | _counterIvar = [counter copy]; 68 | 69 | To call a generator, just use the standard block call syntax, since they actually are blocks: 70 | 71 | int nextValue = counter(); 72 | 73 | How to actually use any particular generator will depend on what it does, of course. 74 | 75 | Generators which return objects and take no parameters can be thought of as enumerators. Calling the generator gets the next object. Returning nil will generally be a signal to stop. To make this usage more convenient, MAGenerator provides the MAGeneratorEnumerator function. This takes such a generator and returns an object conforming to NSFastEnumeration which wraps that generator. The result can then be used as the target of a for/in loop. 76 | 77 | For example, take this generator declaration: 78 | 79 | GENERATOR_DECL(id, EnumerateObjects(id inSomething), (void)); 80 | 81 | You can use this generator in a for/in loop like so: 82 | 83 | for(id obj in MAGeneratorEnumerator(EnumerateObjects(something))) 84 | ...do something with obj... 85 | 86 | 87 | Caveats 88 | ------- 89 | 90 | MAGenerator twists the Objective-C language in various ways, so there are certain things that are just fine in normal Objective-C that don't quite work right inside a generator. 91 | 92 | 1) I said this above, but it bears repeating. Local variables declared after GENERATOR_BEGIN will not remember their values across invocations of GENERATOR_YIELD. If you declare local variables within the generator body, ensure that the entire lifetime of their usage does not include any invocations of GENERATOR_YIELD. To be safer, only declare local variables above GENERATOR_BEGIN. 93 | 94 | 2) Because of (1), it's not safe to use a for/in loop inside the generator body unless it doesn't contain any GENERATOR_YIELD invocations. 95 | 96 | 3) Because the generator macros use a switch statement internally to control execution flow, any switch statements you write explicitly must not contain any GENERATOR_YIELD invocations. (GENERATOR_YIELD generates case: labels which will end up associated with the inner switch statement if used this way.) 97 | 98 | 4) GENERATOR_YIELD makes use of the __LINE__ macro to generate unique case: labels. Because of this, you can't have more than one invocation of GENERATOR_YIELD on the same line. 99 | 100 | 5) When assigning to a __block-qualified object pointer local variable from within the generator block, you can't assume anything about the surrounding autorelease environment or future calls to the generator once you yield. As such, the object must be retained when assigning, and you must write a cleanup block that releases it. 101 | 102 | 103 | Examples 104 | -------- 105 | 106 | This stuff is weird at first, especially if you've never seen generators or coroutines in other languages before. The GeneratorTests.m file contains many examples, from simple counters to complicated parsers. 107 | 108 | 109 | License 110 | ------- 111 | 112 | MAGenerator is released under an MIT license. For the full, legal license, see the LICENSE file. Use in any and every sort of project is encouraged, as long as the terms of the license are followed (and they're easy!). 113 | 114 | 115 | Further Reading 116 | --------------- 117 | 118 | For more information about coroutines and generators, Wikipedia is a decent resource: 119 | 120 | - http://en.wikipedia.org/wiki/Coroutine 121 | - http://en.wikipedia.org/wiki/Generator_(computer_science) 122 | 123 | MAGenerator was inspired by a similar system for making coroutines in C. Reading through the author's discussion may be help illustrate why MAGenerator is made the way it is: 124 | 125 | - http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 126 | 127 | Python is a well known language which makes much use of generators. Much of what can be found on the web about Python generators will apply, to greater or lesser degrees, to MAGenerator as well. 128 | 129 | Finally, my Friday Q&A article about MAGenerator discusses its construction and use in greater detail: 130 | 131 | - http://www.mikeash.com/?page=pyblog/friday-qa-2009-10-30-generators-in-objective-c.html 132 | 133 | 134 | Authorship 135 | ---------- 136 | 137 | MAGenerator is written by Mike Ash. For questions about the code or to contribute patches, contact mike@mikeash.com. 138 | --------------------------------------------------------------------------------