nim-classes icon indicating copy to clipboard operation
nim-classes copied to clipboard

new operator

Open rokups opened this issue 4 years ago • 1 comments

Hey, Awesome you pursue this idea! I tried this a while in the past as well, although i tried to make automagic new operator for standard nim objects, allocating non-ref with init() and ref with new(). That was a limited success. I spent a while mucking in nim's ast trees. Since then i no longer use nim for anything. Since you are interested in this featured i figured i will share what i cobbled together. If you find any of this useful - treat this snippet as public domain code. And if it is useless - off to 🗑️ then 👍🏻

import macros
import strutils
# 12:43 <Araq> compiler/semstmts, edit semOverride
# 12:44 <Araq> add a new field finalizer* to the TType in ast.nim
# 12:44 <Araq> look at how deepcopy is implemented
# 12:44 <Araq> where it's used and instantiated
# 12:45 <Araq> cause `=finalize` needs to works with generics too
# 12:45 <Araq> so that's a slight complication
# 12:45 <Araq> but actually just grep for "deepcopy"
# 12:45 <Araq> it's all there for it
# 12:45 <Araq> generic instantation magic
# 12:46 <Araq> codegen type info slot

macro ctor*(none: untyped): auto =
    let args = callsite()
    var prc: NimNode
    var allocator: NimNode
    if args.len == 3:
        if args[1].kind != nnkBracket:
            error("Usage: {.ctor: [allocator: 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]
            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` =
            `allocator`(result)
            init(result[])
    allc = allc[0]
    allc[2] = prc[2]

    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][0][1] = new_ident_node("result")    # otherwise result is taken from macro context. weird!
    allc[6][1][1][0] = new_ident_node("result")
    for i in 2 ..< prc[3].len:
        allc[6][1].add(prc[3][i][0])

    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) =
        echo "allocating with custom allocator"
        var x: ref Foo
        new(x)
        res = x

    # var x: ref Foo
    # custom_new(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.} =
        self.a = n

    # proc init(self: var Bar, n: int) {.ctor: [allocator: custom_new].} =
    #     self.a = n

    var f: Foo
    f.init(1)
    echo f.repr
    #echo Foo.init(2).repr
    echo Foo.new(3).repr

rokups avatar Aug 16 '21 06:08 rokups

Hey, thanks for this 👍 I'll take a look when I can...

There's still so much about Nim I don't understand tbh, like destructors, pragmas, prioritizing overlapping references from multiple libraries (like the js issue on the bottom of the readme) etc

jjv360 avatar Aug 17 '21 08:08 jjv360