43 lines
1.2 KiB
Nim
43 lines
1.2 KiB
Nim
|
# stew
|
||
|
# Copyright 2024 Status Research & Development GmbH
|
||
|
# Licensed under either of
|
||
|
#
|
||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||
|
#
|
||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||
|
|
||
|
import std/macros
|
||
|
|
||
|
macro evalOnceAs*(exp, alias: untyped): untyped =
|
||
|
## Ensure that `exp` is evaluated only once unless it is a symbol in which
|
||
|
## case it's used directly.
|
||
|
##
|
||
|
## A common case where this is useful is template parameters which, when
|
||
|
## an expression is passed in, get evaluated multiple times.
|
||
|
##
|
||
|
## Based on a similar macro in std/sequtils
|
||
|
expectKind(alias, nnkIdent)
|
||
|
|
||
|
let
|
||
|
body = nnkStmtList.newTree()
|
||
|
val =
|
||
|
if exp.kind == nnkSym:
|
||
|
# The symbol can be used directly
|
||
|
# TODO dot expressions? etc..
|
||
|
exp
|
||
|
else:
|
||
|
let val = genSym(ident = "evalOnce_" & $alias)
|
||
|
body.add newLetStmt(val, exp)
|
||
|
val
|
||
|
body.add(
|
||
|
newProc(
|
||
|
name = genSym(nskTemplate, $alias),
|
||
|
params = [getType(untyped)],
|
||
|
body = val,
|
||
|
procType = nnkTemplateDef,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
body
|