Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 184 lines (166 sloc) 7.23 KB
#!/usr/bin/env nush
#
# @file nubake
# Bake Nu scripts into Objective-C.
# When run with the "-s" option, standalone programs are created. Compile them with:
# gcc myprogram.m -o myprogram -framework Foundation -framework Nu
# Compiled programs require Nu.framework in /Library/Frameworks or another standard path.
#
# @copyright Copyright (c) 2007 Tim Burks, Radtastical Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
(class NSData
(- byteArray is
(set result "(const unsigned char[]){")
((self length) times:
(do (i) (result appendString:(+ "" (self byteAtIndex:i) ","))))
(result appendString:"0}")
result))
;; @abstract A Nu code "baker".
;; @discussion This class is used by nubake, the standalone Nu code "baker", to automatically convert Nu code into equivalent Objective-C functions.
(class NuBake is NSObject
;; Convert a Nu expression into an equivalent C expression.
;; Used internally.
;; Upon reading it, you might ask, "is this really all it takes?" It is.
(+ (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)
(set data ((node stringValue) dataUsingEncoding:NSUTF8StringEncoding))
(result appendString:"_nusymbol_with_length(#{(data byteArray)}, #{(data length)})"))
((node isKindOfClass:NSString)
(set data (node dataUsingEncoding:NSUTF8StringEncoding))
(result appendString:"_nustring_with_length(#{(data byteArray)}, #{(data length)})"))
((node isKindOfClass:NSNumber)
(result appendString:"_nunumberd(#{node})"))
((node isKindOfClass:NuRegex)
(set data ((node pattern) dataUsingEncoding:NSUTF8StringEncoding))
(result appendString:"_nuregex_with_length(#{(data byteArray)}, #{(data length)}, #{(node options)})"))
(else (result appendString:"[ERROR]")))
result)
;; Generate an Objective-C source file from parsed Nu source code.
;; The file will a function named 'functionName' that when called,
;; will construct a code tree that is identical to the specified program.
;; If 'standalone' is true, a 'main()' function will be included to allow
;; the file to be compiled, linked, and run standalone.
(+ (id) bakeNuCode:(id)program intoFunction:(id)functionName standalone:(int)standalone is
(set result <<-END
#import <Foundation/Foundation.h>
#import <Nu/Nu.h>
id #{functionName}() {
return END)
(result appendString:(self expandNuExpression:program))
(result appendString:<<-END
;
}
END)
(if (standalone)
(result appendString:<<-END
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id nu = [Nu parser];
[nu eval:#{functionName}()];
[pool release];
return 0;
}
END))
result)
;; This bakes Nu code into a class method that expands and evaluates it in the main parser context.
;; The purpose is to have methods like (MyClass macros) that can be called from Nu to install
;; macros and other helpers that are written in Nu and baked into Objective-C.
(+ (id) bakeNuCode:(id)program intoMethod:(id) methodName ofCategory:(id) categoryName ofClass:(id) className is
(set result <<-END
//
// THIS FILE WAS AUTOMATICALLY GENERATED
//
// Regenerate it with nubake:
// nubake <sourcefile> --method #{methodName} --category #{categoryName} --class #{className}
//
#import <Foundation/Foundation.h>
#ifdef TARGET_OS_IPHONE
#import "Nu.h"
#else
#import <Nu/Nu.h>
#endif
#import "#{className}.h"
@implementation #{className} (#{categoryName})
+ (id) #{methodName} {
NuCell *code = END)
(result appendString:(self expandNuExpression:program))
(result appendString:<<-END
;
return [[Nu sharedParser] eval:code];
}
@end
END)
result))
;;;;;;;;;;;;;;;;;;;;;;;;;
;; main program
;;;;;;;;;;;;;;;;;;;;;;;;;
(set argv ((NSProcessInfo processInfo) arguments))
(set argi 0)
;; if we're running as a nush script, skip the nush path
(if (/(.*)nush$/ findInString:(argv 0))
(set argi (+ argi 1)))
;; skip the program name
(set argi (+ argi 1))
;; the options we need to set
(set sourceFileName nil)
(set functionName nil)
(set outputFileName nil)
(set methodName nil)
(set categoryName nil)
(set className nil)
(set standalone nil)
;; process the remaining arguments
(while (< argi (argv count))
(case (argv argi)
("-n" (set argi (+ argi 1)) (set functionName (argv argi)))
("-o" (set argi (+ argi 1)) (set outputFileName (argv argi)))
("-s" (set standalone t))
("--output" (set argi (+ argi 1)) (set outputFileName (argv argi)))
("--method" (set argi (+ argi 1)) (set methodName (argv argi)))
("--category" (set argi (+ argi 1)) (set categoryName (argv argi)))
("--class" (set argi (+ argi 1)) (set className (argv argi)))
(else (set sourceFileName (argv argi))))
(set argi (+ argi 1)))
(if sourceFileName
(then
(unless functionName
(set functionName ((sourceFileName stringByDeletingPathExtension) lastPathComponent)))
(unless outputFileName
(set outputFileName (+ (sourceFileName stringByDeletingPathExtension) ".m")))
(try
(set code (parse (NSString stringWithContentsOfFile:sourceFileName
encoding:NSUTF8StringEncoding error:nil)))
(if (and methodName categoryName className)
(then ((NuBake bakeNuCode:code
intoMethod:methodName
ofCategory:categoryName
ofClass:className)
writeToFile:outputFileName atomically:NO))
(else ((NuBake bakeNuCode:code
intoFunction:functionName
standalone:standalone)
writeToFile:outputFileName atomically:NO)))
(catch (exception)
(puts "error: #{(exception reason)}")
(set exit (NuBridgedFunction functionWithName:"exit" signature:"vi"))
(exit -1))))
(else
(puts "usage: nubake <sourcefile> [-n <functionname>] [-o <outputfilename>] [-s] or [--method <methodname> --class <classname> --category <categoryname>]")))