Created
December 10, 2013 00:18
-
-
Save gradha/7883627 to your computer and use it in GitHub Desktop.
Better objc nimrod integration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # horrible example of how to interface with GNUStep ... | |
| import macros, strutils | |
| {.passL: "-lobjc".} | |
| {.passL: "-framework AppKit".} | |
| {.emit: """ | |
| #include <Foundation/NSObject.h> | |
| @interface Greeter: NSObject | |
| { | |
| NSString *name_; | |
| } | |
| - (void)greet:(long)x y:(long)dummy; | |
| - (NSString*)name; | |
| - (void)setName:(char*)name; | |
| + (void)genericGreeter:(char*)hello; | |
| @end | |
| #include <stdio.h> | |
| @implementation Greeter | |
| - (void)dealloc | |
| { | |
| [name_ release]; | |
| [super dealloc]; | |
| } | |
| + (void)genericGreeter:(char*)text | |
| { | |
| printf("Generic hello %s!\n", text); | |
| } | |
| - (void)greet:(long)x y:(long)y | |
| { | |
| printf("Hello, World!\nx:%ld y:%ld\n", x, y); | |
| printf("I'm %s\n", [name_ UTF8String]); | |
| } | |
| - (NSString*)name | |
| { | |
| return name_; | |
| } | |
| - (void)setName:(char*)name | |
| { | |
| [name_ release]; | |
| name_ = [[NSString alloc] initWithUTF8String:name]; | |
| } | |
| @end | |
| #include <stdlib.h> | |
| """.} | |
| type | |
| Greeter {.importc: "Greeter*", header: "<objc/Object.h>", final.} = object | |
| NSDate* {.importc: "NSDate*", header: "<Foundation/Foundation.h>", | |
| final.} = object | |
| iterator unroll_parameters(node: PNimrodNode, startPos = 0): | |
| tuple[name, ptype, default: PNimrodNode] = | |
| node.expect_kind(nnkFormalParams) | |
| var pos = 0 | |
| for ident_node_index in 1..len(node)-1: | |
| let ident_node = node[ident_node_index] | |
| ident_node.expect_kind(nnkIdentDefs) | |
| let | |
| defaultValue = ident_node.last | |
| paramType = ident_node[len(ident_node) - 2] | |
| for param_index in 0..len(ident_node) - 3: | |
| let param_node = ident_node[param_index] | |
| if pos >= startPos: | |
| yield (param_node, paramType, defaultValue) | |
| pos += 1 | |
| macro import_objc_class(class_name: string, body: stmt): stmt {.immediate.} = | |
| ## Modifies procdefs to use emit. | |
| result = newNimNode(nnkStmtList) | |
| for inode in body.children: | |
| case inode.kind: | |
| of nnkProcDef: | |
| # A proc definition should have 7 nodes, the last being empty of body | |
| inode.expect_min_len(7) | |
| inode[6].expect_kind(nnkEmpty) | |
| var c = inode.copyNimTree | |
| c[0].expect_kind(nnkIdent) | |
| let | |
| proc_name = $c[0] | |
| params = c[3] | |
| pragmas = c[4] | |
| params.expect_kind(nnkFormalParams) | |
| pragmas.expect_kind(nnkEmpty) | |
| # Convenience mangling for unique methods like alloc or new. | |
| case toLower(proc_name) | |
| of "new": c[0] = newIdentNode("new" & $class_name) | |
| else: nil | |
| params.expect_min_len(1) | |
| let ret_type = params[0] | |
| # Detect based on convention of first parameter name if method is static. | |
| var static_method = true | |
| if params.len > 1: | |
| let first_param_name = $params[1][0] | |
| if cmpIgnoreStyle(first_param_name, "self") == 0: | |
| static_method = false | |
| # Prepare to start emitting raw objc code replacing params. | |
| var emit_body = newNimNode(nnkStmtList) | |
| emit_body.add(newNimNode(nnkPragma).add( | |
| newNimNode(nnkExprColonExpr))) | |
| let e = emit_body[0][0] | |
| e.add(newIdentNode("emit")) | |
| var | |
| start_param = 0 | |
| code: string | |
| # Procs with a return value should use the objc return keyword. | |
| if ret_type.kind == nnkEmpty: | |
| code = "[" | |
| else: | |
| code = "result = [" | |
| # Define how do we start to emit the method. | |
| if static_method: | |
| code.add($class_name & " " & proc_name) | |
| else: | |
| code.add("`self` " & proc_name) | |
| start_param = 1 | |
| # Loop over the parameters, carefully adding the first without name. | |
| var did_add_first = false | |
| for pname, ptype, pdefault in params.unroll_parameters(start_param): | |
| if did_add_first: | |
| code.add($pname & ":`" & $pname & "` ") | |
| else: | |
| did_add_first = true | |
| code.add(":`" & $pname & "` ") | |
| code.add("];") | |
| # Ok, replace the last node with the body we just built. | |
| let lit = newNimNode(nnkTripleStrLit) | |
| lit.strVal = code | |
| e.add(lit) | |
| c[6] = emit_body | |
| # Add some pragmas for values with a return type. | |
| #if ret_type.kind == nnkEmpty: | |
| # let p = newNimNode(nnkPragma).add( | |
| # newIdentNode("NoStackFrame")).add( | |
| # newIdentNode("inline")) | |
| # #echo treeRepr(p) | |
| # c[4] = p | |
| result.add(c) | |
| echo treeRepr(c) | |
| else: | |
| echo "Ignoring unexpected node ", repr(inode.kind) | |
| #dumpTree: | |
| # proc newModel(): Greeter {.NoStackFrame, inline.} = | |
| # {.emit: """return [Greeter new];""".} | |
| # proc greet(self: Greeter, x, y: int) {.importobjc: "greet", nodecl.} = nil | |
| # proc temp_param(hello: cstring = "papa") = nil | |
| # #proc genericGreeter(hello: cstring) | |
| # ##"a" | |
| # #proc genericGreeter(hello: cstring) = | |
| # # {.emit: """[Greeter genericGreeter:`hello`];""".} | |
| import_objc_class(Greeter): | |
| proc genericGreeter(hello: cstring = "pepe") | |
| proc greet(self: Greeter, x, y: int) | |
| proc new(): Greeter | |
| proc release(self: Greeter) | |
| proc setName(self: Greeter, name: cstring) | |
| genericGreeter("blah" & " and " & "foo") | |
| genericGreeter() | |
| let g = newGreeter() | |
| #var g = newModel() | |
| g.setName(cstring("foo")) | |
| g.greet(1, 3) | |
| g.release() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment