diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fa8a682ad50cf..1bd75833587b6 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1611,3 +1611,43 @@ when defined(nimMacrosSizealignof): ## from a field of a type. Therefore it only requires one argument ## instead of two. Returns a negative value if the Nim compiler ## does not know the offset. + +proc splitDefinition*(def: NimNode): tuple[lhs: NimNode, rhs: NimNode] = + ## allows library constructs such as: `byRef: a2 = expr` + doAssert def.kind == nnkStmtList and def.len == 1 + let def2 = def[0] + doAssert def2.kind == nnkAsgn + let lhs = def2[0] + let rhs = def2[1] + expectKind(lhs, nnkIdent) + return (lhs, rhs) + +macro byRef*(def: untyped): untyped = + ## Defines a ref alias for lvalue expressions. The expression is evaluated + ## only once, and any side effects will only be evaluated once, at declaration + ## time. + runnableExamples: + var count = 0 + proc identity(a: int): auto = + block: count.inc; a + var x = @[1,2,3] + byRef: x1=x[identity(1)] # the ref-alias is evaluated only here + doAssert count == 1 + x1 += 10 + doAssert type(x1) is int # use x1 just like a normal variable + doAssert x == @[1,12,3] + doAssert count == 1 # count has not changed + + let (name, exp) = splitDefinition(def) + result = quote do: + let myAddr = addr `exp` + template `name`: untyped = myAddr[] + +macro byPtr*(def: untyped): untyped = + ## Defines a ptr alias for expressions. Caution: this uses `unsafeAddr`, so + ## is unsafe to use in general, and `byRef` should be preferred when possible. + ## This can for example be used on `let` variables. + let (name, exp) = splitDefinition(def) + result = quote do: + let myAddr = unsafeAddr `exp` + template `name`: untyped = myAddr[]