An open API service indexing awesome lists of open source software.

https://github.com/mmower/gen

Objective-C Code Generator
https://github.com/mmower/gen

Last synced: 10 months ago
JSON representation

Objective-C Code Generator

Awesome Lists containing this project

README

          

# `Gen`
## An Objective-C Code Generator

The `Gen` framework implements an Objective-C code generator that I built it while I was working on Statec (a state machine DSL
and class generator for Objective-C) and have now extracted into a separate library.

`Gen` is not complete (although I think it models most of the useful parts of Objective-C) and certainly could do with some work
ironing out its kinks. But it works and I think it meets the criteria of being able to do useful work.

In practice `Gen` provides a set of classes to model Objective-C concepts such as classes (`GenClass`), protocols (`GenProtocol`),
properties (`GenProperty`), variables (`GenVariable`), methods (`GenMethod`), and so on. To create a class you create a `GenClass`
instance, add properties, methods, protocols, variables and so on. Then add it to a `GenCompilationUnit` that knows how to write
out the corresponding `.m`/`.h` files.

At the method level code is inserted into the method body using a template string (essentially `-stringWithFormat:`). In practice
attempts to model the structure of methods proved to be a somewhat tedious exercise for little reward and I found it easier to
work with strings. Though even here `Gen` provides some helper methods to make it easier to do things like invoking `GenMethod`'s.

## Example

Here is a real example of using `Gen`, taken from the source of Statec. This method creates the `.m`/`.h` files for the user-facing
state machine class. You can see the creation of a class with a private instance variable (i.e. the variable is defined in a class
extension) that implements a protocol and defines the methods of that protocol. In particular you can see how, when the method
tagged `start` is being defined it looks up a method in the implementation class and uses the `GenMethod` to create a call to it:

- (GenCompilationUnit *)generateUnit {
NSString *userClassName = [NSString stringWithFormat:@"%@%@Machine",
[[self machine] prefix],
[[self machine] name]];

GenCompilationUnit *unit = [[GenCompilationUnit alloc] initWithTag:@"user"
name:userClassName];

NSString *versionString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
NSString *revNumber = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];

NSMutableString *commentString = [NSMutableString string];
[commentString appendFormat:
@"// State machine %@ generated by Statec v%@(%@) on %@\n"
@"// Statec copyright (c) 2012 Matt Mower \n"
@"// \n",
[[self machine] name],
versionString,
revNumber,
[NSDate date]
];

[unit setComment:commentString];

// Import the generated machine into the user machine
[[unit declarationImports] addObject:[[self implUnit] headerFileName]];

GenClass *userClass = [[GenClass alloc] initWithTag:@"user"
name:userClassName
baseClass:nil];

NSString *implVariableName = [NSString stringWithFormat:@"_%@Machine", [[[self machine] name] statecStringByLoweringFirstLetter]];
GenVariable *implVariable = [[GenVariable alloc] initWithTag:@"impl"
scope:GenInstanceScope|GenPrivateScope
name:implVariableName
type:[[[self implUnit] classWithTag:@"impl"] pointerType]];
[userClass addVariable:implVariable];

// The delegate protocol is what this class conforms to so stub its methods for the user to implement
GenProtocol *delegateProtocol = [[self implUnit] protocolWithTag:@"delegate"];

[userClass addProtocol:delegateProtocol];

GenMethod *setupMethod = [[GenMethod alloc] initWithTag:@"setup"
scope:GenInstanceScope|GenPrivateScope
returnType:@"void"
selectorFormat:@"setup%@Machine", [[self machine] name]];
[[setupMethod body] append:@"\t%@ = [[%@ alloc] init];\n"
@"\t[%@ setDelegate:self];",
[implVariable name],
[[[self implUnit] classWithTag:@"impl"] name],
[implVariable name]
];
[userClass addMethod:setupMethod];

// Create the initializer that will setup the machine
GenMethod *initializer = [[GenMethod alloc] initWithTag:@"init"
scope:GenInstanceScope
returnType:StatecTypeId
selector:@selector(init)];
[[initializer body] append:@"\tself = [super init];\n"
@"\tif( self ) {\n"
@"\t\t%@;\n"
@"\t}\n"
@"\treturn self;\n",
[setupMethod invocationWithReceiver:@"self"]
];
[initializer setIsDeclaredHere:NO];
[userClass addInitializer:initializer];

/*
For the users convenience we will sort the methods into utility methods,
non-final state methods, and final state methods.
*/
NSArray *methods = [[delegateProtocol methods] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return method_weight(obj1) - method_weight(obj2);
}];

for( GenMethod *method in methods ) {
GenMethod *stubMethod = [method mutableCopy];

if( [[method tag] isEqualToString:@"start"] ) {
GenMethod *startMethod = [[[self implUnit] principalClass] instanceMethodWithTag:@"start"];
[stubMethod setBody:[[GenStatementGroup alloc] initWithFormat:@"\t%@;", [startMethod invocationWithReceiver:[implVariable name]]]];
[stubMethod setIsDeclaredHere:YES];
} else {
[stubMethod setBody:[[GenStatementGroup alloc] initWithFormat:@"\t// Your code here"]];
[stubMethod setIsDeclaredHere:NO];
}

[userClass addMethod:stubMethod];
}

[unit addClass:userClass];

return unit;
}

## Feedback
I'm not sure if anyone else will find `Gen` useful, it's not often that one needs to be able to dynamically generate Objective-C code. But,
if you do, and you use it, I'd be grateful to hear your feedback.