Implementing Objective-C Message Calling in Nimrod With Macros

Okay, so our goal here is to add support for Objective-C from Nimrod.  We’d like to be able to allocate and use Objective-C objects.  So the way that Nimrod works, at least the parts we are interested in, is that first it does some lexer/parser magic then creates an Abstract Syntax Tree, then from that tree it generates C code, then it runs the C code through a C compiler to generate a binary.

There are a couple ways we could interface with Objective-C (objc).  We could just ignore objc syntax and interface directly with the C interface to the runtime.  We could be doing things like calling the C objc_msgSend function ourselves.  Or we could find a way to inject objc syntax into our generated C source files and instead compile with an objc compiler.  We’ll opt for the latter because in the future it would be neat to be able to define objc classes in Nimrod and it will be easier to debug if we are using the language directly instead of just tons of C function calls into the runtime.

The first thing we’ll want to do is change from compiling with a C compiler to an objc compiler.  Since objc is a super-set of C that should be easy to change and still be able to produce a valid binary.  Luckily the Nimrod compiler has a flag that allows us to do this easily, namely “nimrod objc <filename>” instead of “nimrod c <filename>”.  This makes the resulting code file be named with the “.m” suffix and be compiled with an objc compiler.

Okay, now we are exporting objc code, now lets try to inject some objc code into the resulting .m file.  Here again Nimrod provides us with a tool to do that with the emit pragma.  {.emit: “<code>”.} sends code verbatim to the resulting file.  The authors of Nimrod (Araq) put it in the langauge for exactly this purpose, interfacing with other C’ish languages.

One thing to note about Nimrod is that while it allows macros on the AST it doesn’t allow us to use reader macros like common-lisp does.  So we are stuck with the guidelines of the Nimrod language.  It will be impossible to make “[NSDictionary alloc]” make sense in Nimrod because it would think that is an array and would be looking for the comma between NSDictionary and alloc.  Also since the brackets already mean something we’ll need something special to distinguish objc message calls from arrays (eg [1,2,3,4,5]).  I decided to use the syntax “o[NSDictionary, alloc]” to mean “[NSDictionary alloc]”  Because of the prefix ‘o’ literal we can hunt out the difference between an array and an objc message send.

What I ended up doing was creating a Nimrod method that works on a statement, which is one or more expressions.  That way it can crawl its way down the AST and perform manipulations to convert arrays prefixed with ‘o’ into emit pragmas with the corresponding objc code.

Here is a link to the code as it stands now: callobjc

Notes on understanding the code:

  • Recursive expansion of objc message sends don’t exist yet.  Check out head it may be done by now.  That’s why you see messages alloc and init on separate lines instead of idiomatic objc on the same line.
  • {.compileTime.} pragma allows functions to be usable by macros
  • macros operate on PNimrodNode which can be interchangeable with stmt and expr types.  I don’t believe there are any static checks to verify that something is in fact a stmt vs expr.
  • the combo of getAST(<some template call>) is shortcut to avoid building an AST by hand
  • literals that prefix [] operators become children of the bracket operation.  I found this out by using the lispRepr proc which is indispensable when developing macros in Nimrod.

Intro to Nimrod

Nimrod is a not so popular programming language that I’ve been playing around with lately.  Instead of jumping into different things I’ve done with it I’ll start by giving a tiny introduction that explains why I care about Nimrod.  I’ve been using it for over a week now so I think I have enough of a grasp to have an opinion.

Nimrod first interested me because it compiles to C with little extra overhead, so it’s fast, but the killer feature you don’t see often is the ability to write hygienic macros.  You can manipulate the AST at compilation time.  Since one of my pet projects was writing C with s-exps to facilitate the usage of macros, this was of interest to me.

There are a few other languages that fill the same niche.  I’d say its competitors are C++, D, Go, and Rust.  I’ve played with them all except Rust, I don’t think any of them have the ability to write macros.

As I play with the language a bit more you can find some of my example projects on GitHub at nimrod-examples.  Also more can be found out about Nimrod at their website: Nimrod Lang.