Skip to content

Instantly share code, notes, and snippets.

@gradha
Created December 10, 2013 00:18
Show Gist options
  • Save gradha/7883627 to your computer and use it in GitHub Desktop.
Save gradha/7883627 to your computer and use it in GitHub Desktop.
Better objc nimrod integration
# 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