Quick actions

cmd+k|ctrl+k

Navigation

Languages

Untitled

Snippet info

Language

Nim

Visibility

unlisted

Author

legacy-anonymous

Created

2015-07-08T09:23:15Z

Updated

2015-07-08T09:23:15Z

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
INFO