Last active
August 29, 2015 14:08
-
-
Save jovial/00fdcae18ac9bfa1a6af to your computer and use it in GitHub Desktop.
static call method hack
This file contains 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
import macros | |
macro genStaticGetter(a: expr): stmt = | |
## generates a getter which returns a pointer to a method with | |
## a particular type signature. | |
## | |
## The getter has the form methodname(typ: type(param1), typ2: type(param2), ...), | |
## where methodname is the name of the method passed to this macro, and param# | |
## corresponds to the the param in position # of the method signature. # is a integer | |
## representing the position of the parameter. | |
## | |
## WARNING: doesn't work with generics | |
expectKind(a,nnkDo) | |
result = newNimNode(nnkStmtList) | |
# this seg faults for some reason | |
#let methDef = a.findChild(it.kind == nnkMethodDef) | |
let methDef = a.findChild(it.kind == nnkProcDef) | |
if methDef == nil: | |
error("couldn't find method definition") | |
let methIdent = methDef.findChild(it.kind == nnkIdent) | |
if methIdent == nil: | |
error("couldn't find method definition") | |
# convert proc to method | |
var temp = newNimNode(nnkMethodDef) | |
for child in methDef.children: | |
temp.add(child) | |
result.add temp | |
# stores a reference to the method we are adding currently | |
let procTyp = newNimNode(nnkProcTy).add(methDef.params).add(newEmptyNode()) | |
let thisMethSym = genSym(nskLet, "thisMethod") | |
let identDefs = newIdentDefs(thisMethSym, procTyp, methIdent) | |
let letSection = newNimNode(nnkLetSection).add(identDefs) | |
result.add letSection | |
# this extracts the types for the method signature | |
var paramIdentDefs = newSeq[PNimrodNode](0) | |
let procTy = identDefs.findChild(it.kind == nnkProcTy) | |
let formalParams = procTy.findChild(it.kind == nnkFormalParams) | |
var count = 0 | |
for child in formalParams.children: | |
if child.kind == nnkIdentDefs: | |
# FIXME: assumes type was specified!!! | |
if child.len < 1: | |
error("method must specify type of param") | |
let paramSym = genSym(nskParam, "param" & $count) | |
let paramType = newNimNode(nnkBracketExpr).add(newIdentNode(!"typedesc"), child[1]) | |
let paramDefs = newIdentDefs(paramSym, paramType) | |
paramIdentDefs.add paramDefs | |
count.inc | |
#make getter | |
var params = newSeq[PNimrodNode](0) | |
params.add(newIdentNode(!"auto")) # return type | |
for node in paramIdentDefs: | |
params.add(node) | |
let body = newNimNode(nnkStmtList) | |
.add newNimNode(nnkReturnStmt).add(thisMethSym) | |
let getter = newProc(methIdent,params,body) | |
result.add getter | |
type A = ref object of RootObj | |
type B = ref object of A | |
type C = ref object of B | |
# There seems to be a compiler bug when passing a methods to a macro, | |
# so we declare these as procedures but they are converted to methods | |
# by the macro. | |
proc test(x: A, y: int) {.genStaticGetter.} = | |
echo "got A" | |
proc test(x: B, y: int) {.genStaticGetter.} = | |
test(A, type(y))(x.A, y) # calls test(A,int) | |
echo "got B" | |
proc test(x: C, y: int) {.genStaticGetter.} = | |
test(B, type(y))(x.B, y) # calls test(B,int) | |
echo "got C" | |
# one param version in case it's clearer | |
proc test2(x: A) {.genStaticGetter.} = | |
echo "received A" | |
proc test2(x: B) {.genStaticGetter.} = | |
test2(A)(x.A) | |
echo "received B" | |
proc test2(x: C) {.genStaticGetter.} = | |
# you can also split the calls | |
let super = test2(B) | |
# equivalent to: | |
#let super: proc(x:B) = test2 | |
super(x.B) | |
echo "received C" | |
let testee: A = C() | |
test(testee,123) | |
test2(testee) | |
test(A,int)(testee.A, 5) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment