import macros
import strutils
macro ctor(none: untyped): auto {.immediate.} =
let args = callsite()
var prc: NimNode
var allocator: NimNode
var destructor: NimNode
if args.len == 3:
if args[1].kind != nnkBracket:
error("Usage: {.ctor: [allocator: prc, destructor: dtor_prc]}")
if args[2].kind != nnkProcDef:
error("ctor pragma is used only with procedures")
prc = args[2]
var margs = args[1]
for i in 0 ..< margs.len:
if $margs[i][0] == "allocator":
allocator = margs[i][1]
elif $margs[i][0] == "destructor":
destructor = margs[i][1]
else:
error("Unknown {.ctor.} paramater " & $margs[i][0])
else:
if args[1].kind != nnkProcDef:
error("ctor pragma is used only with procedures")
prc = args[1]
allocator = new_ident_node("new")
if prc[3][1][1].kind != nnkVarTy:
error("Constructor must have var non-ref type as first parameter")
if prc[3][0].kind != nnkEmpty:
error("Constructor must not have return type")
var proc_name = $prc[0]
if proc_name notin ["init", "init*"]:
error("Constructor name must be `init` but is `" & proc_name & "`")
var export_proc = proc_name.ends_with("*")
var type_identifier = prc[3][1][1][0]
var ctor = quote do:
proc init(_: typedesc[`type_identifier`]): `type_identifier` =
init(result)
ctor = ctor[0]
ctor[2] = prc[2]
if export_proc:
ctor[0] = new_nim_node(nnkPostfix).add(
new_ident_node("*"),
ctor[0]
)
# Extend ctor with parameters of constructor
for i in 2 ..< prc[3].len:
ctor[3].add(prc[3][i])
# Passes ctor params to main init proc
ctor[6][0][1] = new_ident_node("result") # otherwise result is taken from macro context. weird!
for i in 2 ..< prc[3].len:
ctor[6][0].add(prc[3][i][0])
var allc = quote do:
proc new(_: typedesc[`type_identifier`]): ref `type_identifier` =
var res: ref `type_identifier`
`allocator`(res)
init(res[])
return res
allc = allc[0]
allc[2] = prc[2]
if destructor != nil:
allc[6][1].add(destructor)
if export_proc:
allc[0] = new_nim_node(nnkPostfix).add(
new_ident_node("*"),
allc[0]
)
# Extend allc with parameters of constructor
for i in 2 ..< prc[3].len:
allc[3].add(prc[3][i])
# Passes allc params to main init proc
#allc[6][1][1] = new_ident_node("result") # otherwise result is taken from macro context. weird!
#allc[6][2][1] = new_ident_node("result")
for i in 2 ..< prc[3].len:
allc[6][2].add(prc[3][i][0])
echo tree_repr(allc)
result = new_stmt_list(prc, ctor, allc)
when is_main_module:
type
Foo = object
a: int
Bar = object
a: int
proc destroy(self: ref Foo) =
echo "done"
proc custom_new(res: var ref Foo, dtor: proc (x: ref Foo) {.nimcall.}) =
echo "allocating with custom allocator"
var x: ref Foo
new(x, dtor)
res = x
# proc init(self: var Foo, n: int) =
# self.a = n
# proc init(_: typedesc[Foo], n: int): Foo =
# init(result, n)
# proc new(_: typedesc[Foo], n: int): ref Foo =
# custom_new(result, destroy)
# init(result[], n)
proc init(self: var Foo, n: int) {.ctor: [allocator: custom_new, destructor: destroy].} =
self.a = n
proc init(self: var Bar, n: int) {.ctor.} =
self.a = n
var f: Foo
f.init(1)
echo f.repr
#echo Foo.init(2).repr
echo Foo.new(3).repr