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
parent fe9bb1869d
commit a0a8934a4f
6 changed files with 68 additions and 25 deletions

View File

@@ -32,6 +32,7 @@ type
tos*: PStackFrame # top of stack
lastException*: PNode
optEval*: bool # evaluation done for optimization purposes
globals*: TIdNodeTable # state of global vars
PEvalContext* = ref TEvalContext
@@ -56,13 +57,14 @@ proc newEvalContext*(module: PSym, filename: string,
new(result)
result.module = module
result.optEval = optEval
initIdNodeTable(result.globals)
proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} =
t.next = c.tos
c.tos = t
proc popStackFrame*(c: PEvalContext) {.inline.} =
if (c.tos == nil): InternalError("popStackFrame")
if c.tos == nil: InternalError("popStackFrame")
c.tos = c.tos.next
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
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 =
result = evalAux(c, n.sons[0], flags)
if isSpecial(result): return
@@ -430,19 +446,23 @@ proc evalSwap(c: PEvalContext, n: PNode): PNode =
result = emptyNode
proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
case n.sym.kind
of skProc, skConverter, skMacro: result = n.sym.ast.sons[codePos]
of skVar, skForVar, skTemp, skResult:
result = evalVariable(c.tos, n.sym, flags)
var s = n.sym
case s.kind
of skProc, skConverter, skMacro: result = s.ast.sons[codePos]
of skVar, skForVar, skTemp, skResult:
if sfGlobal notin s.flags:
result = evalVariable(c.tos, s, flags)
else:
result = evalGlobalVar(c, s, flags)
of skParam:
# XXX what about LValue?
result = c.tos.params[n.sym.position + 1]
of skConst: result = n.sym.ast
of skEnumField: result = newIntNodeT(n.sym.position, n)
result = c.tos.params[s.position + 1]
of skConst: result = s.ast
of skEnumField: result = newIntNodeT(s.position, n)
else:
stackTrace(c, n, errCannotInterpretNodeX, $n.sym.kind)
stackTrace(c, n, errCannotInterpretNodeX, $s.kind)
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 =
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 =
result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return
var a = result
result = foldConv(n, a)
if result == nil:
# foldConv() cannot deal with everything that we want to do here:
result = a
if result.typ != nil:
var a = result
result = foldConv(n, a)
if result == nil:
# foldConv() cannot deal with everything that we want to do here:
result = a
proc evalCheckedFieldAccess(c: PEvalContext, n: 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,
semCheck: bool = true): PNode =
markUsed(n, sym)
var p = newEvalContext(c.module, "", false)
result = evalMacroCall(p, n, sym)
if c.evalContext == nil:
c.evalContext = newEvalContext(c.module, "", false)
result = evalMacroCall(c.evalContext, n, sym)
if semCheck: result = semAfterMacroCall(c, result, sym)
proc forceBool(c: PContext, n: PNode): PNode =

View File

@@ -13,7 +13,7 @@ import
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
wordrecg,
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
TOptionEntry* = object of lists.TListEntry # entries to put on a
@@ -69,6 +69,7 @@ type
includedFiles*: TIntSet # used to detect recursive include files
filename*: string # the module's filename
userPragmas*: TStrTable
evalContext*: PEvalContext
var
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):
var v = semIdentDef(c, a.sons[j], skVar)
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:
v.typ = typ
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
- make threadvar efficient again on linux after testing
- document & test splicing; don't forget to test negative indexes
- implement lib/pure/memfiles properly
- 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)
- dead code elim for JS backend
incremental compilation
-----------------------
- the loading has to be MUCH more lazy! --> next version: We should re-load
symbol.ast lazily
- the loading has to be MUCH lazier! --> next version: We should re-load
symbol.ast.sons[codePos] lazily
- implement lib/pure/memfiles properly
version 0.9.0