eval context for macros lives as long as the current module is compiled
This commit is contained in:
parent
fe9bb1869d
commit
a0a8934a4f
6 changed files with 68 additions and 25 deletions
|
|
@ -32,6 +32,7 @@ type
|
||||||
tos*: PStackFrame # top of stack
|
tos*: PStackFrame # top of stack
|
||||||
lastException*: PNode
|
lastException*: PNode
|
||||||
optEval*: bool # evaluation done for optimization purposes
|
optEval*: bool # evaluation done for optimization purposes
|
||||||
|
globals*: TIdNodeTable # state of global vars
|
||||||
|
|
||||||
PEvalContext* = ref TEvalContext
|
PEvalContext* = ref TEvalContext
|
||||||
|
|
||||||
|
|
@ -56,13 +57,14 @@ proc newEvalContext*(module: PSym, filename: string,
|
||||||
new(result)
|
new(result)
|
||||||
result.module = module
|
result.module = module
|
||||||
result.optEval = optEval
|
result.optEval = optEval
|
||||||
|
initIdNodeTable(result.globals)
|
||||||
|
|
||||||
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
|
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
|
||||||
t.next = c.tos
|
t.next = c.tos
|
||||||
c.tos = t
|
c.tos = t
|
||||||
|
|
||||||
proc popStackFrame*(c: PEvalContext) {.inline.} =
|
proc popStackFrame*(c: PEvalContext) {.inline.} =
|
||||||
if (c.tos == nil): InternalError("popStackFrame")
|
if c.tos == nil: InternalError("popStackFrame")
|
||||||
c.tos = c.tos.next
|
c.tos = c.tos.next
|
||||||
|
|
||||||
proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode
|
proc evalMacroCall*(c: PEvalContext, n: PNode, sym: PSym): PNode
|
||||||
|
|
@ -306,6 +308,20 @@ proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
|
||||||
x = x.next
|
x = x.next
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
|
|
||||||
|
proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
|
||||||
|
result = IdNodeTableGet(c.globals, s)
|
||||||
|
if result != nil:
|
||||||
|
if not aliasNeeded(result, flags):
|
||||||
|
result = copyTree(result)
|
||||||
|
else:
|
||||||
|
result = s.ast
|
||||||
|
if result == nil or result.kind == nkEmpty:
|
||||||
|
result = getNullValue(s.typ, s.info)
|
||||||
|
else:
|
||||||
|
result = evalAux(c, result, {})
|
||||||
|
if isSpecial(result): return
|
||||||
|
IdNodeTablePut(c.globals, s, result)
|
||||||
|
|
||||||
proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
result = evalAux(c, n.sons[0], flags)
|
result = evalAux(c, n.sons[0], flags)
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
|
|
@ -430,19 +446,23 @@ proc evalSwap(c: PEvalContext, n: PNode): PNode =
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
|
|
||||||
proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
case n.sym.kind
|
var s = n.sym
|
||||||
of skProc, skConverter, skMacro: result = n.sym.ast.sons[codePos]
|
case s.kind
|
||||||
of skVar, skForVar, skTemp, skResult:
|
of skProc, skConverter, skMacro: result = s.ast.sons[codePos]
|
||||||
result = evalVariable(c.tos, n.sym, flags)
|
of skVar, skForVar, skTemp, skResult:
|
||||||
|
if sfGlobal notin s.flags:
|
||||||
|
result = evalVariable(c.tos, s, flags)
|
||||||
|
else:
|
||||||
|
result = evalGlobalVar(c, s, flags)
|
||||||
of skParam:
|
of skParam:
|
||||||
# XXX what about LValue?
|
# XXX what about LValue?
|
||||||
result = c.tos.params[n.sym.position + 1]
|
result = c.tos.params[s.position + 1]
|
||||||
of skConst: result = n.sym.ast
|
of skConst: result = s.ast
|
||||||
of skEnumField: result = newIntNodeT(n.sym.position, n)
|
of skEnumField: result = newIntNodeT(s.position, n)
|
||||||
else:
|
else:
|
||||||
stackTrace(c, n, errCannotInterpretNodeX, $n.sym.kind)
|
stackTrace(c, n, errCannotInterpretNodeX, $s.kind)
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
if result == nil: stackTrace(c, n, errCannotInterpretNodeX, n.sym.name.s)
|
if result == nil: stackTrace(c, n, errCannotInterpretNodeX, s.name.s)
|
||||||
|
|
||||||
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
|
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
|
||||||
result = evalAux(c, n.sons[1], {efLValue})
|
result = evalAux(c, n.sons[1], {efLValue})
|
||||||
|
|
@ -532,11 +552,12 @@ proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
|
||||||
proc evalConv(c: PEvalContext, n: PNode): PNode =
|
proc evalConv(c: PEvalContext, n: PNode): PNode =
|
||||||
result = evalAux(c, n.sons[1], {efLValue})
|
result = evalAux(c, n.sons[1], {efLValue})
|
||||||
if isSpecial(result): return
|
if isSpecial(result): return
|
||||||
var a = result
|
if result.typ != nil:
|
||||||
result = foldConv(n, a)
|
var a = result
|
||||||
if result == nil:
|
result = foldConv(n, a)
|
||||||
# foldConv() cannot deal with everything that we want to do here:
|
if result == nil:
|
||||||
result = a
|
# foldConv() cannot deal with everything that we want to do here:
|
||||||
|
result = a
|
||||||
|
|
||||||
proc evalCheckedFieldAccess(c: PEvalContext, n: PNode,
|
proc evalCheckedFieldAccess(c: PEvalContext, n: PNode,
|
||||||
flags: TEvalFlags): PNode =
|
flags: TEvalFlags): PNode =
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,9 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
|
proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
|
||||||
semCheck: bool = true): PNode =
|
semCheck: bool = true): PNode =
|
||||||
markUsed(n, sym)
|
markUsed(n, sym)
|
||||||
var p = newEvalContext(c.module, "", false)
|
if c.evalContext == nil:
|
||||||
result = evalMacroCall(p, n, sym)
|
c.evalContext = newEvalContext(c.module, "", false)
|
||||||
|
result = evalMacroCall(c.evalContext, n, sym)
|
||||||
if semCheck: result = semAfterMacroCall(c, result, sym)
|
if semCheck: result = semAfterMacroCall(c, result, sym)
|
||||||
|
|
||||||
proc forceBool(c: PContext, n: PNode): PNode =
|
proc forceBool(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import
|
||||||
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
|
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
|
||||||
wordrecg,
|
wordrecg,
|
||||||
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
|
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
|
||||||
magicsys, nversion, nimsets, parser, times, passes, rodread
|
magicsys, nversion, nimsets, parser, times, passes, rodread, evals
|
||||||
|
|
||||||
type
|
type
|
||||||
TOptionEntry* = object of lists.TListEntry # entries to put on a
|
TOptionEntry* = object of lists.TListEntry # entries to put on a
|
||||||
|
|
@ -69,6 +69,7 @@ type
|
||||||
includedFiles*: TIntSet # used to detect recursive include files
|
includedFiles*: TIntSet # used to detect recursive include files
|
||||||
filename*: string # the module's filename
|
filename*: string # the module's filename
|
||||||
userPragmas*: TStrTable
|
userPragmas*: TStrTable
|
||||||
|
evalContext*: PEvalContext
|
||||||
|
|
||||||
var
|
var
|
||||||
gGenericsCache: PGenericsCache # save for modularity
|
gGenericsCache: PGenericsCache # save for modularity
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,9 @@ proc semVar(c: PContext, n: PNode): PNode =
|
||||||
for j in countup(0, length-3):
|
for j in countup(0, length-3):
|
||||||
var v = semIdentDef(c, a.sons[j], skVar)
|
var v = semIdentDef(c, a.sons[j], skVar)
|
||||||
addInterfaceDecl(c, v)
|
addInterfaceDecl(c, v)
|
||||||
|
if def != nil and def.kind != nkEmpty:
|
||||||
|
# this is only needed for the evaluation pass:
|
||||||
|
v.ast = def
|
||||||
if a.kind != nkVarTuple:
|
if a.kind != nkVarTuple:
|
||||||
v.typ = typ
|
v.typ = typ
|
||||||
b = newNodeI(nkIdentDefs, a.info)
|
b = newNodeI(nkIdentDefs, a.info)
|
||||||
|
|
|
||||||
19
tests/accept/run/tidgen.nim
Normal file
19
tests/accept/run/tidgen.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
discard """
|
||||||
|
output: "3 4"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import macros
|
||||||
|
|
||||||
|
# Test compile-time state in same module
|
||||||
|
|
||||||
|
var gid = 3
|
||||||
|
|
||||||
|
macro genId(invokation: expr): expr =
|
||||||
|
result = newIntLitNode(gid)
|
||||||
|
inc gid
|
||||||
|
|
||||||
|
proc Id1(): int = return genId()
|
||||||
|
proc Id2(): int = return genId()
|
||||||
|
|
||||||
|
echo Id1(), " ", Id2()
|
||||||
|
|
||||||
12
todo.txt
12
todo.txt
|
|
@ -6,17 +6,15 @@ Version 0.8.14
|
||||||
- fix actors.nim
|
- fix actors.nim
|
||||||
- make threadvar efficient again on linux after testing
|
- make threadvar efficient again on linux after testing
|
||||||
- document & test splicing; don't forget to test negative indexes
|
- document & test splicing; don't forget to test negative indexes
|
||||||
- implement lib/pure/memfiles properly
|
- dead code elim for JS backend
|
||||||
- eval context is per module; this way modularity is kept; global id generation
|
|
||||||
macro can still be done once macros support basic IO (store current id in
|
|
||||||
some file)
|
|
||||||
|
|
||||||
|
|
||||||
incremental compilation
|
incremental compilation
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
- the loading has to be MUCH more lazy! --> next version: We should re-load
|
- the loading has to be MUCH lazier! --> next version: We should re-load
|
||||||
symbol.ast lazily
|
symbol.ast.sons[codePos] lazily
|
||||||
|
- implement lib/pure/memfiles properly
|
||||||
|
|
||||||
|
|
||||||
version 0.9.0
|
version 0.9.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue