eval context for macros lives as long as the current module is compiled

This commit is contained in:
Araq 2011-10-28 01:38:41 +02:00
commit a0a8934a4f
6 changed files with 68 additions and 25 deletions

View file

@ -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 =

View file

@ -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 =

View file

@ -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

View file

@ -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)

View 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()

View file

@ -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