Created
January 13, 2018 12:15
-
-
Save rokups/b40df23b36ea7a2849951627f8632e45 to your computer and use it in GitHub Desktop.
A macro which creates constructors for nim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{.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