Skip to content

Instantly share code, notes, and snippets.

@jovial
Created October 31, 2014 13:01
Show Gist options
  • Save jovial/9b8efa0fed114c7b5f9b to your computer and use it in GitHub Desktop.
Save jovial/9b8efa0fed114c7b5f9b to your computer and use it in GitHub Desktop.
staticCall with some issues
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