Faux closures: a facility for creating closure-like non-closure procs

This code is extracted from the error-handling proposal where it's
used to implement `Try` blocks (Faux closures are created there in
order to assign raises lists to them). The desktop team faced a
similar problem where the `spawn` API doesn't support closures,
but the restriction can be easily worked-around with faux closures.
This commit is contained in:
Zahary Karadjov 2020-07-07 20:29:04 +03:00
parent 61d5cfc376
commit 32b86bfd1f
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
2 changed files with 92 additions and 0 deletions

70
stew/faux_closures.nim Normal file
View File

@ -0,0 +1,70 @@
import
tables, macros
macro fauxClosureAux(procName: untyped, procDef: typed): untyped =
type ParamRegistryEntry = object
origSymbol: NimNode
newParam: NimNode
var params = initTable[string, ParamRegistryEntry]()
proc makeParam(replacedSym: NimNode,
registryEntry: var ParamRegistryEntry): NimNode =
if registryEntry.newParam == nil:
registryEntry.origSymbol = replacedSym
registryEntry.newParam = genSym(nskParam, $replacedSym)
return registryEntry.newParam
let realClosureSym = procDef.name
proc registerParamsInBody(n: NimNode) =
for i in 0 ..< n.len:
let son = n[i]
case son.kind
of nnkIdent, nnkCharLit..nnkTripleStrLit:
discard
of nnkSym:
if son.symKind in {nskLet, nskVar, nskForVar,
nskParam, nskTemp, nskResult}:
if owner(son) != realClosureSym:
n[i] = makeParam(son, params.mgetOrPut(son.signatureHash,
default(ParamRegistryEntry)))
of nnkHiddenDeref:
registerParamsInBody son
n[i] = son[0]
else:
registerParamsInBody son
let
fauxClosureName = genSym(nskProc, $procName)
fauxClosureBody = copy procDef.body
registerParamsInBody fauxClosureBody
var
procCall = newCall(fauxClosureName)
fauxClosureParams = @[newEmptyNode()]
for hash, param in params:
var paramType = getType(param.origSymbol)
if param.origSymbol.symKind in {nskVar, nskResult}:
if paramType.kind != nnkBracketExpr or not eqIdent(paramType[0], "var"):
paramType = newTree(nnkVarTy, paramType)
fauxClosureParams.add newIdentDefs(param.newParam, paramType)
procCall.add param.origSymbol
result = newStmtList(
newProc(name = fauxClosureName,
params = fauxClosureParams,
body = fauxClosureBody),
newProc(name = procName,
procType = nnkTemplateDef,
body = procCall))
macro fauxClosure*(procDef: untyped): untyped =
let name = procDef.name
procDef.name = genSym(nskProc, $name)
result = newCall(bindSym"fauxClosureAux", name, procDef)

View File

@ -0,0 +1,22 @@
import
os, threadpool,
../stew/faux_closures
template sendSignal(data: string) =
echo data
template spawnAndSend(exprToSend: untyped) =
proc payload {.fauxClosure.} =
let data = exprToSend
sendSignal data
spawn payload()
proc main(x: string) =
spawnAndSend:
var i = 10
x & $(23 + i)
main("tests")
sleep(2)