mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-20 07:51:32 +00:00
* 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:
@@ -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)
|
||||
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
|
||||
|
||||
@@ -116,7 +116,8 @@ type
|
||||
callOperator,
|
||||
parallel,
|
||||
destructor,
|
||||
notnil
|
||||
notnil,
|
||||
dynamicBindSym
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
||||
|
||||
@@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
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 semOf(c: PContext, n: PNode): PNode =
|
||||
@@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mOf: result = semOf(c, n)
|
||||
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
|
||||
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:
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# included from sem.nim
|
||||
|
||||
const
|
||||
errStringOrIdentNodeExpected = "string or ident node expected"
|
||||
errStringLiteralExpected = "string literal expected"
|
||||
errIntLiteralExpected = "integer literal expected"
|
||||
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.sons[1].kind == nkNilLit))
|
||||
of opcNBindSym:
|
||||
# cannot use this simple check
|
||||
# if dynamicBindSym notin c.config.features:
|
||||
|
||||
# bindSym with static input
|
||||
decodeBx(rkNode)
|
||||
regs[ra].node = copyTree(c.constants.sons[rbx])
|
||||
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:
|
||||
decodeBC(rkNode)
|
||||
let idx = regs[rc].intVal.int
|
||||
@@ -1773,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
|
||||
|
||||
include vmops
|
||||
|
||||
proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
|
||||
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
|
||||
if graph.vm.isNil:
|
||||
graph.vm = newCtx(module, graph.cache, graph)
|
||||
registerAdditionalOps(PCtx graph.vm)
|
||||
|
||||
@@ -136,7 +136,7 @@ type
|
||||
opcLdGlobalAddr, # dest = addr(globals[Bx])
|
||||
|
||||
opcLdImmInt, # dest = immediate value
|
||||
opcNBindSym,
|
||||
opcNBindSym, opcNDynBindSym,
|
||||
opcSetType, # dest.typ = types[Bx]
|
||||
opcTypeTrait,
|
||||
opcMarshalLoad, opcMarshalStore,
|
||||
@@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
|
||||
c.prc = PProc(blocks: @[])
|
||||
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))
|
||||
|
||||
const
|
||||
|
||||
@@ -817,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
|
||||
c.freeTemp(tmp2)
|
||||
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) =
|
||||
case m
|
||||
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 mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
|
||||
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
|
||||
of mNBindSym:
|
||||
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 mNBindSym: genBindSym(c, n, dest)
|
||||
of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
|
||||
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
|
||||
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
|
||||
|
||||
@@ -382,16 +382,24 @@ type
|
||||
|
||||
{.deprecated: [TBindSymRule: BindSymRule].}
|
||||
|
||||
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
|
||||
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
|
||||
magic: "NBindSym", noSideEffect.}
|
||||
## creates a node that binds `ident` to a symbol node. The bound symbol
|
||||
## may be an overloaded symbol.
|
||||
## if `ident` is a NimNode, it must have nkIdent kind.
|
||||
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
|
||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
|
||||
## returned or ``nkSym`` if the symbol is not ambiguous.
|
||||
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
|
||||
## 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 {.
|
||||
magic: "NGenSym", noSideEffect.}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
discard """
|
||||
msg: '''initApple
|
||||
deinitApple
|
||||
Coral
|
||||
enum
|
||||
redCoral, blackCoral'''
|
||||
output: '''TFoo
|
||||
TBar'''
|
||||
"""
|
||||
@@ -23,3 +28,40 @@ macro test: untyped =
|
||||
bindSym("TBar"))
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user