Created
October 31, 2014 13:01
-
-
Save jovial/9b8efa0fed114c7b5f9b to your computer and use it in GitHub Desktop.
staticCall with some issues
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 | |
macro staticCall(a:expr): expr = | |
## calls methodName(a.type,b.type ...)(a,b ...) | |
a.expectKind(nnkCall) | |
var params = newSeq[PNimrodNode](0) | |
var methodIdent: PNimrodNode | |
var index = 0 | |
for child in a.children: | |
if (index == 0): | |
index.inc | |
methodIdent = child | |
continue | |
params.add child | |
var typeParams = newSeq[PNimrodNode](0) | |
for param in params: | |
let p = newNimNode(nnkPar).add(param) | |
let typ = newNimNode(nnkTypeOfExpr).add(p) | |
typeParams.add typ | |
let getterCall = newCall(methodIdent,typeParams) | |
let wrapper = newCall(getterCall,params) | |
#result = newNimNode(nnkStmtList) | |
#result.add(wrapper) | |
result = wrapper | |
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" | |
# staticCall version: can't use staticCall inside a method - compiler bug? or | |
# are we abusing the proc type? :D | |
# Error: base method has lock level <unknown>, but dispatcher has 0 | |
proc test3(x: A, y:int): string {.genStaticGetter, locks: 0.} = | |
return "given A" | |
#proc test3(x: B, y:int):string {.genStaticGetter, locks: 0.} = | |
# let ret = staticCall test3(x.A, y) | |
# echo ret | |
# return "given B" | |
#proc test3(x: C, y:int):string {.genStaticGetter, locks: 0.} = | |
# let ret = staticCall test3(x.B, y) | |
# echo ret | |
# return "given C" | |
# same issue as test3 above, seems you can't return values with this method | |
# Error: base method has lock level <unknown>, but dispatcher has 0 | |
proc test4(x: A): string {.genStaticGetter.} = | |
return "test4: received A" | |
#proc test4(x: B): string {.genStaticGetter.} = | |
# echo test4(A)(x.A) | |
# return "test4: received B" | |
#proc test4(x: C): string {.genStaticGetter.} = | |
# echo test4(B)(x.B) | |
# return "test4: received C" | |
let testee: A = C() | |
test(testee,123) | |
test2(testee) | |
#echo test3(testee,456) | |
echo staticCall test3(testee.A,5) | |
#echo test4(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