A simple example of Nu and Cocoa
Friday, June 3rd, 2011I recently had a request of showing how to execute arbitrary Nu code inside of an Objective-C context. I’ve created a simple project to demonstrate this. It is available on Github.
The first step when working with Nu is to add the framework to your project. The example project creates a small app that simply accepts Nu input in one text field and outputs the results in another. Any errors are eaten by the console, so you may want to run Console.app if you actually try to use the program for anything more than an example project. For the most part, though, you can ignore the example app. Everything is detailed below if you just want to dive in.
With Nu added, you’ll need to set up a Nu parser, pass in some Nu code, and execute it. Fortunately, Nu is really easy to use inside of Cocoa. Once you’ve imported the Nu framework into a file (import <nu /Nu.h>), you need to create the parser (id parser = [Nu parser]). Now, there are two steps to actually executing Nu code. The first step is to parse it into tokens (actually, NuCells internally). The second is to evaluate the tokens. This evaluation is the actual execution. Fortunately, this is simple. First, the parse, id parsedNu = [parser parse:nuString], then evaluate the parsed code, id result = [parser eval:parsedNu]. Of course, I prefer to use the convenience method that handles both at once:
id result = [parser parseEval:nuString];
So, how do we use this in a real application? From the example above, the only thing that is missing is the nuString, that is, an NSString containing Nu code. That string could come from anywhere. You could pull it from your application (NSString *nuString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nufile" ofType:@"nu"]];) or put it directly into the code (NSString *nuString = @"(+ 1 1)";).
Now, the question is “How do we set up the Nu environment?” Well, everything in the runtime is already included in our Nu environment, so we get access to all of the other frameworks that we’ve brought in:
; This just works if you've imported CalendarStore.framework
(set store (CalCalendarStore defaultCalendarStore))
(set calendar ((store calendars) lastObject))
(calendar title)
; Close the main window for the example project linked to above
appdelegate = ((NSApplication sharedApplication) delegate)
((appdelegate window) orderOut:nil)
This is great, but what about injecting our own objects into the Nu runtime? This is just as simple as everything else:
// Objective-C
NSString *example = @"This is an ";
NSString *nuCode = @"(+ exampleInjection \"example\")";
id parser = [Nu parser];
[parser setValue:example forKey:@"exampleInjection"];
NSLog( [parser parseEval:nuCode] ); // result: "this is an example"
Basically, inject whatever objects you want into the Nu code by calling setValue:forKey: on the parser. You can also get objects back out fairly easily by calling valueForKey: as in:
// Objective-C
NSString *nuSource = @"(set rejectionExample \"Nu->ObjC\")";
id parser = [Nu parser];
[parser parseEval:nuSource];
NSLog( [parser valueForKey:@"rejectionExample"] );
When you’re done, you can just clean up and close the parser ([parser close]). Working with the Nu parser is fairly easy once you know where to look. Nu.h has all of the information there if you want to read a bit more into it.
For further reading, check out my Nu PluginManager. Of special interest in the code is how I call functions by injecting objects and then calling the top-level functions using those objects as parameters by simply parsing and evaluating a bit more code. I could always get a reference to the actual function NuCell using valueForKey:, but this may be the most simple way without need to know anything about how Nu works internally.