Baking Nu

nubake

There’s a new tool in the Nu source distribution. It’s called nubake, and you can use it to “bake” Nu source files into compiled objects. I won’t call it an obfuscator because Nu has a way of making everything open, but nubake can be useful for simplifying Nu applications and giving them a bit of tamper-resistance.

nubake parses a Nu source file and writes an Objective-C function that, when called, returns the same parsed code structure that you would get by parsing the original source file. You can then compile and link this into your Nu application and call the new Objective-C function instead of loading the original Nu source file. I’ll add more bundling options in the future, but for now, you can either use nubake to generate a single function, or by adding the “-s” (standalone) option, you can generate a program that can be compiled into a standalone Nu-based binary.

Here’s an example; we’ll say it is in a file named ninetynine.nu:

(99 times:
    (do (i)
        (set count (- 99 i))
        (puts <<-END
#{count} bottles of Nu on the wall
#{count} bottles of Nu
Take one down, pass it around
#{count} bottles of Nu on the wall
END)))

To bake it, use the following command:

% nubake ninetynine.nu -s

Then compile it with gcc:

% gcc ninetynine.m -o ninetynine -framework Cocoa -framework Nu
Now run it.
% ninetynine 
99 bottles of Nu on the wall
99 bottles of Nu
Take one down, pass it around
99 bottles of Nu on the wall

98 bottles of Nu on the wall
98 bottles of Nu
Take one down, pass it around
98 bottles of Nu on the wall

97 bottles of Nu on the wall
97 bottles of Nu
Take one down, pass it around
97 bottles of Nu on the wall

...

With options and comments, nubake is a little over 100 lines of code. Its core is a small Nu method:

(+ (id) expandNuExpression:(id) node is
   (set result "")
   (cond ((eq node nil)
          (result appendString:"_nunull()"))
         ((node isKindOfClass:NuCell)
          (result appendString:
                  (+ "_nucell(" 
                     (self expandNuExpression:(node car))
                     ",\n" 
                     (self expandNuExpression:(node cdr)) 
                     ")")))
         ((node isKindOfClass:NuSymbol)
          (result appendString:
                  "_nusymbol(#{((node stringValue) characterArray)})"))
         ((node isKindOfClass:NSString)
          (result appendString:"_nustring(#{(node characterArray)})"))
         ((node isKindOfClass:NSNumber)
          (result appendString:"_nunumberd(#{node})"))
         ((node isKindOfClass:NuRegex)
          (result appendString:
                  "_nuregex(#{((node pattern) characterArray)}, #{(node options)})"))        
         (else (result appendString:"[ERROR]")))
   result)

That’s sufficient to turn any parsed Nu code into a nested set of C function calls that produces an equivalent structure. Then that structure can be evaluated with something as simple as (eval my-expression), where my-expression is set to the result of a call to your function. If you want to make that function call from Nu, just wrap it with an instance of NuBridgedFunction:

(set ninetynine 
     (NuBridgedFunction functionWithName:"ninetynine" signature:"@"))

Then call eval with its results:

(eval (ninetynine))

It’s enough to make you wonder, “why are other scripting languages so opaque?”

For now, nubake is only available in the Nu git repository at code.neontology.com.

2 comments ↓

#1peter on 2007-12-19 at 07:06:04 America/Los_Angeles

NSProcessInfo is available in 10.0 and up..

#2Tim on 2007-12-19 at 07:15:32 America/Los_Angeles

thanks! corrected.

Leave a Comment (sign in with Twitter)