* bindSym power up, working prototype * update bindSym doc * add bindSym test * fix some typo * fix bindSym doc * get rid of specialops field from vm * add experimental: dynamicBindSym
This commit is contained in:
parent
674bd7bfad
commit
78c0ac5407
9 changed files with 183 additions and 13 deletions
|
|
@ -196,4 +196,9 @@
|
||||||
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
|
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
|
- macros.bindSym now capable to accepts not only literal string or string constant expression.
|
||||||
|
bindSym enhancement make it also can accepts computed string or ident node inside macros /
|
||||||
|
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
|
||||||
|
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,8 @@ type
|
||||||
callOperator,
|
callOperator,
|
||||||
parallel,
|
parallel,
|
||||||
destructor,
|
destructor,
|
||||||
notnil
|
notnil,
|
||||||
|
dynamicBindSym
|
||||||
|
|
||||||
SymbolFilesOption* = enum
|
SymbolFilesOption* = enum
|
||||||
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
|
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
|
||||||
|
|
||||||
|
proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode =
|
||||||
|
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}:
|
||||||
|
localError(c.config, info.info, errStringOrIdentNodeExpected)
|
||||||
|
return errorNode(c, n)
|
||||||
|
|
||||||
|
if isMixin < 0 or isMixin > high(TSymChoiceRule).int:
|
||||||
|
localError(c.config, info.info, errConstExprExpected)
|
||||||
|
return errorNode(c, n)
|
||||||
|
|
||||||
|
let id = if n.kind == nkIdent: n
|
||||||
|
else: newIdentNode(getIdent(c.cache, n.strVal), info.info)
|
||||||
|
|
||||||
|
let tmpScope = c.currentScope
|
||||||
|
c.currentScope = scope
|
||||||
|
let s = qualifiedLookUp(c, id, {checkUndeclared})
|
||||||
|
if s != nil:
|
||||||
|
# we need to mark all symbols:
|
||||||
|
result = symChoice(c, id, s, TSymChoiceRule(isMixin))
|
||||||
|
else:
|
||||||
|
errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
|
||||||
|
else: n.strVal)
|
||||||
|
c.currentScope = tmpScope
|
||||||
|
|
||||||
|
proc semDynamicBindSym(c: PContext, n: PNode): PNode =
|
||||||
|
# inside regular code, bindSym resolves to the sym-choice
|
||||||
|
# nodes (see tinspectsymbol)
|
||||||
|
if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
|
||||||
|
return semBindSym(c, n)
|
||||||
|
|
||||||
|
if c.graph.vm.isNil:
|
||||||
|
setupGlobalCtx(c.module, c.graph)
|
||||||
|
|
||||||
|
let
|
||||||
|
vm = PCtx c.graph.vm
|
||||||
|
# cache the current scope to
|
||||||
|
# prevent it lost into oblivion
|
||||||
|
scope = c.currentScope
|
||||||
|
|
||||||
|
# cannot use this
|
||||||
|
# vm.config.features.incl dynamicBindSym
|
||||||
|
|
||||||
|
proc bindSymWrapper(a: VmArgs) =
|
||||||
|
# capture PContext and currentScope
|
||||||
|
# param description:
|
||||||
|
# 0. ident, a string literal / computed string / or ident node
|
||||||
|
# 1. bindSym rule
|
||||||
|
# 2. info node
|
||||||
|
a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2))
|
||||||
|
|
||||||
|
let
|
||||||
|
# altough we use VM callback here, it is not
|
||||||
|
# executed like 'normal' VM callback
|
||||||
|
idx = vm.registerCallback("bindSymImpl", bindSymWrapper)
|
||||||
|
# dummy node to carry idx information to VM
|
||||||
|
idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt))
|
||||||
|
|
||||||
|
result = copyNode(n)
|
||||||
|
for x in n: result.add x
|
||||||
|
result.add n # info node
|
||||||
|
result.add idxNode
|
||||||
|
|
||||||
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
|
|
||||||
proc semOf(c: PContext, n: PNode): PNode =
|
proc semOf(c: PContext, n: PNode): PNode =
|
||||||
|
|
@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
of mOf: result = semOf(c, n)
|
of mOf: result = semOf(c, n)
|
||||||
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
||||||
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
||||||
of mNBindSym: result = semBindSym(c, n)
|
of mNBindSym:
|
||||||
|
if dynamicBindSym notin c.features:
|
||||||
|
result = semBindSym(c, n)
|
||||||
|
else:
|
||||||
|
result = semDynamicBindSym(c, n)
|
||||||
of mProcCall:
|
of mProcCall:
|
||||||
result = n
|
result = n
|
||||||
result.typ = n[1].typ
|
result.typ = n[1].typ
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
# included from sem.nim
|
# included from sem.nim
|
||||||
|
|
||||||
const
|
const
|
||||||
|
errStringOrIdentNodeExpected = "string or ident node expected"
|
||||||
errStringLiteralExpected = "string literal expected"
|
errStringLiteralExpected = "string literal expected"
|
||||||
errIntLiteralExpected = "integer literal expected"
|
errIntLiteralExpected = "integer literal expected"
|
||||||
errWrongNumberOfVariables = "wrong number of variables"
|
errWrongNumberOfVariables = "wrong number of variables"
|
||||||
|
|
|
||||||
|
|
@ -1222,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
|
node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
|
||||||
node.sons[1].kind == nkNilLit))
|
node.sons[1].kind == nkNilLit))
|
||||||
of opcNBindSym:
|
of opcNBindSym:
|
||||||
|
# cannot use this simple check
|
||||||
|
# if dynamicBindSym notin c.config.features:
|
||||||
|
|
||||||
|
# bindSym with static input
|
||||||
decodeBx(rkNode)
|
decodeBx(rkNode)
|
||||||
regs[ra].node = copyTree(c.constants.sons[rbx])
|
regs[ra].node = copyTree(c.constants.sons[rbx])
|
||||||
regs[ra].node.flags.incl nfIsRef
|
regs[ra].node.flags.incl nfIsRef
|
||||||
|
of opcNDynBindSym:
|
||||||
|
# experimental bindSym
|
||||||
|
let
|
||||||
|
rb = instr.regB
|
||||||
|
rc = instr.regC
|
||||||
|
idx = int(regs[rb+rc-1].intVal)
|
||||||
|
callback = c.callbacks[idx].value
|
||||||
|
args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs),
|
||||||
|
currentException: c.currentExceptionB,
|
||||||
|
currentLineInfo: c.debug[pc])
|
||||||
|
callback(args)
|
||||||
|
regs[ra].node.flags.incl nfIsRef
|
||||||
of opcNChild:
|
of opcNChild:
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
let idx = regs[rc].intVal.int
|
let idx = regs[rc].intVal.int
|
||||||
|
|
@ -1773,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
|
||||||
|
|
||||||
include vmops
|
include vmops
|
||||||
|
|
||||||
proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
|
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
|
||||||
if graph.vm.isNil:
|
if graph.vm.isNil:
|
||||||
graph.vm = newCtx(module, graph.cache, graph)
|
graph.vm = newCtx(module, graph.cache, graph)
|
||||||
registerAdditionalOps(PCtx graph.vm)
|
registerAdditionalOps(PCtx graph.vm)
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ type
|
||||||
opcLdGlobalAddr, # dest = addr(globals[Bx])
|
opcLdGlobalAddr, # dest = addr(globals[Bx])
|
||||||
|
|
||||||
opcLdImmInt, # dest = immediate value
|
opcLdImmInt, # dest = immediate value
|
||||||
opcNBindSym,
|
opcNBindSym, opcNDynBindSym,
|
||||||
opcSetType, # dest.typ = types[Bx]
|
opcSetType, # dest.typ = types[Bx]
|
||||||
opcTypeTrait,
|
opcTypeTrait,
|
||||||
opcMarshalLoad, opcMarshalStore,
|
opcMarshalLoad, opcMarshalStore,
|
||||||
|
|
@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
|
||||||
c.prc = PProc(blocks: @[])
|
c.prc = PProc(blocks: @[])
|
||||||
c.loopIterations = MaxLoopIterations
|
c.loopIterations = MaxLoopIterations
|
||||||
|
|
||||||
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
|
proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
|
||||||
|
result = c.callbacks.len
|
||||||
c.callbacks.add((name, callback))
|
c.callbacks.add((name, callback))
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -817,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
|
||||||
c.freeTemp(tmp2)
|
c.freeTemp(tmp2)
|
||||||
c.freeTemp(tmp3)
|
c.freeTemp(tmp3)
|
||||||
|
|
||||||
|
proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
|
# nah, cannot use c.config.features because sempass context
|
||||||
|
# can have local experimental switch
|
||||||
|
# if dynamicBindSym notin c.config.features:
|
||||||
|
if n.len == 2: # hmm, reliable?
|
||||||
|
# bindSym with static input
|
||||||
|
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
|
||||||
|
let idx = c.genLiteral(n[1])
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
c.gABx(n, opcNBindSym, dest, idx)
|
||||||
|
else:
|
||||||
|
localError(c.config, n.info, "invalid bindSym usage")
|
||||||
|
else:
|
||||||
|
# experimental bindSym
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
let x = c.getTempRange(n.len, slotTempUnknown)
|
||||||
|
|
||||||
|
# callee symbol
|
||||||
|
var tmp0 = TDest(x)
|
||||||
|
c.genLit(n.sons[0], tmp0)
|
||||||
|
|
||||||
|
# original parameters
|
||||||
|
for i in 1..<n.len-2:
|
||||||
|
var r = TRegister(x+i)
|
||||||
|
c.gen(n.sons[i], r)
|
||||||
|
|
||||||
|
# info node
|
||||||
|
var tmp1 = TDest(x+n.len-2)
|
||||||
|
c.genLit(n.sons[^2], tmp1)
|
||||||
|
|
||||||
|
# payload idx
|
||||||
|
var tmp2 = TDest(x+n.len-1)
|
||||||
|
c.genLit(n.sons[^1], tmp2)
|
||||||
|
|
||||||
|
c.gABC(n, opcNDynBindSym, dest, x, n.len)
|
||||||
|
c.freeTempRange(x, n.len)
|
||||||
|
|
||||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
case m
|
case m
|
||||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||||
|
|
@ -1137,13 +1174,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
|
of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
|
||||||
of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
|
of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
|
||||||
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
|
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
|
||||||
of mNBindSym:
|
of mNBindSym: genBindSym(c, n, dest)
|
||||||
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
|
|
||||||
let idx = c.genLiteral(n[1])
|
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
|
||||||
c.gABx(n, opcNBindSym, dest, idx)
|
|
||||||
else:
|
|
||||||
localError(c.config, n.info, "invalid bindSym usage")
|
|
||||||
of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
|
of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
|
||||||
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
|
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
|
||||||
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
|
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
|
||||||
|
|
|
||||||
|
|
@ -382,16 +382,24 @@ type
|
||||||
|
|
||||||
{.deprecated: [TBindSymRule: BindSymRule].}
|
{.deprecated: [TBindSymRule: BindSymRule].}
|
||||||
|
|
||||||
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
|
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
|
||||||
magic: "NBindSym", noSideEffect.}
|
magic: "NBindSym", noSideEffect.}
|
||||||
## creates a node that binds `ident` to a symbol node. The bound symbol
|
## creates a node that binds `ident` to a symbol node. The bound symbol
|
||||||
## may be an overloaded symbol.
|
## may be an overloaded symbol.
|
||||||
|
## if `ident` is a NimNode, it must have nkIdent kind.
|
||||||
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
|
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
|
||||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||||
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
|
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
|
||||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||||
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
||||||
## returned even if the symbol is not ambiguous.
|
## returned even if the symbol is not ambiguous.
|
||||||
|
##
|
||||||
|
## experimental feature:
|
||||||
|
## use {.experimental: "dynamicBindSym".} to activate it
|
||||||
|
## if called from template / regular code, `ident` and `rule` must be
|
||||||
|
## constant expression / literal value.
|
||||||
|
## if called from macros / compile time procs / static blocks,
|
||||||
|
## `ident` and `rule` can be VM computed value.
|
||||||
|
|
||||||
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
||||||
magic: "NGenSym", noSideEffect.}
|
magic: "NGenSym", noSideEffect.}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
discard """
|
discard """
|
||||||
|
msg: '''initApple
|
||||||
|
deinitApple
|
||||||
|
Coral
|
||||||
|
enum
|
||||||
|
redCoral, blackCoral'''
|
||||||
output: '''TFoo
|
output: '''TFoo
|
||||||
TBar'''
|
TBar'''
|
||||||
"""
|
"""
|
||||||
|
|
@ -23,3 +28,40 @@ macro test: untyped =
|
||||||
bindSym("TBar"))
|
bindSym("TBar"))
|
||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
||||||
|
# issue 7827, bindSym power up
|
||||||
|
{.experimental: "dynamicBindSym".}
|
||||||
|
type
|
||||||
|
Apple = ref object
|
||||||
|
name: string
|
||||||
|
color: int
|
||||||
|
weight: int
|
||||||
|
|
||||||
|
proc initApple(name: string): Apple =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc deinitApple(x: Apple) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
macro wrapObject(obj: typed, n: varargs[untyped]): untyped =
|
||||||
|
let m = n[0]
|
||||||
|
for x in m:
|
||||||
|
var z = bindSym x
|
||||||
|
echo z.repr
|
||||||
|
|
||||||
|
wrapObject(Apple):
|
||||||
|
initApple
|
||||||
|
deinitApple
|
||||||
|
|
||||||
|
type
|
||||||
|
Coral = enum
|
||||||
|
redCoral
|
||||||
|
blackCoral
|
||||||
|
|
||||||
|
macro mixer(): untyped =
|
||||||
|
let m = "Co" & "ral"
|
||||||
|
let x = bindSym(m)
|
||||||
|
echo x.repr
|
||||||
|
echo getType(x).repr
|
||||||
|
|
||||||
|
mixer()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue