Giter VIP home page Giter VIP logo

gen's Introduction

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 <[email protected]>\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.

gen's People

Contributors

mmower avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

gen's Issues

Docs and/or tests

This is a really great start for a set of meta-programming tools! Thank you for sharing!
:-)

It would love to contribute, but the code base is a little daunting to dive into without much documentation, tests or comments. If you have some time to add even a few unit tests I think it could go a long way towards making the code base approachable if you are looking for outsiders to help you continue Gen's development.

Thanks again.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.