Skip to content

Instantly share code, notes, and snippets.

@rokups
Created January 13, 2018 12:15
Show Gist options
  • Save rokups/b40df23b36ea7a2849951627f8632e45 to your computer and use it in GitHub Desktop.
Save rokups/b40df23b36ea7a2849951627f8632e45 to your computer and use it in GitHub Desktop.
A macro which creates constructors for nim
{.experimental.}
import macros
import tables
macro ctor*(none: untyped): auto =
let args = callsite()
if args[1].kind != nnkProcDef:
error("`ctor` pragma is used only with procedures.")
var procTemplate = args[1]
var paramsTemplate = procTemplate[3]
if paramsTemplate.len < 2:
error("Constructor must have at least one parameter.")
if paramsTemplate[1][1].kind != nnkVarTy:
error("First parameter of constructor must be a `var Type`.")
if paramsTemplate[0].kind != nnkEmpty:
error("Constructor must not return a value.")
# Original proc is preserved.
var prc = procTemplate.copy()
result = newStmtList(prc)
# Construct parameter `_: typedesc[T]`
var resultType = paramsTemplate[1][1][0] # Type identifier
var typeParam = newNimNode(nnkIdentDefs).add(
newIdentNode("_"),
newNimNode(nnkBracketExpr).add(
newIdentNode("typedesc"),
resultType.copy()
),
newNimNode(nnkEmpty)
)
# Generate value constructor
# proc init*[T](_: typedesc[MyObj], n: T): MyObj =
# init(result, n)
prc = procTemplate.copy()
var params = prc[3]
# Insert return type
params[0] = resultType.copy()
# Replace `self` parameter with `_: typedesc[T]`
params[1] = typeParam.copy()
# Replace body of generated constructor with call to original constructor init(result, ...)
prc[6] = newStmtList(
newNimNode(nnkCall).add(
newIdentNode("init"),
newIdentNode("result")
)
)
for i in 2..<params.len:
prc[6][0].add(params[i][0].copy())
result.add(prc)
# Generate reference constructor
# proc new*[T](_: typedesc[MyObj], n: T): ref MyObj =
# result.new()
# init(result, n)
prc = procTemplate.copy()
if prc[0].kind == nnkPostfix:
prc[0][1] = newIdentNode("new")
else:
prc[0] = newIdentNode("new")
params = prc[3]
# Insert ref return type
params[0] = newNimNode(nnkRefTy).add(resultType.copy())
# Replace `self` parameter with `_: typedesc[T]`
params[1] = typeParam.copy()
# Replace body of generated constructor with call to original constructor new(result); init(result, ...)
prc[6] = newStmtList(
newNimNode(nnkCall).add(
newIdentNode("new"),
newIdentNode("result")
),
newNimNode(nnkCall).add(
newIdentNode("init"),
newIdentNode("result")
)
)
for i in 2..<params.len:
prc[6][1].add(params[i][0].copy())
result.add(prc)
if isMainModule:
type
MyObj[T] = object
n: T
proc init[T](self: var MyObj[T]; n: T) {.ctor.} =
self.n = n
proc init[A, B](self: var Table[A, B], initial_size=64) {.ctor.} =
self = init_table[A, B](initial_size)
self[123] = 321
proc main =
var a = MyObj.init(1) # Generated value type constructor
doAssert(a.n == 1)
var b = MyObj.new(2) # Generated ref type constructor
doAssert(b.n == 2)
var c = MyObj[byte]() # Initialize object without constructing it
doAssert(c.n == 0)
c.init(3) # Call original constructor which had `ctor` pragma applied to it
doAssert(c.n == 3)
var d = MyObj[byte].new() # Initialize ref object without constructing it
doAssert(d.n == 0)
d.init(4) # Call original constructor which had `ctor` pragma applied to it
doAssert(d.n == 4)
var e = Table[int, int].new()
doAssert(e[123] == 321)
var f = Table[int, int].init()
doAssert(f[123] == 321)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment