#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module implements the transformator. It transforms the syntax tree
# to ease the work of the code generators. Does some transformations:
#
# * inlines iterators
# * inlines constants
# * performes constant folding
# * converts "continue" to "break"
# * introduces method dispatchers
# * performs lambda lifting for closure support
import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting
const
genPrefix* = ":tmp" # prefix for generated names
# implementation
type
PTransNode* = distinct PNode
PTransCon = ref TTransCon
TTransCon{.final.} = object # part of TContext; stackable
mapping: TIdNodeTable # mapping from symbols to nodes
owner: PSym # current owner
forStmt: PNode # current for stmt
forLoopBody: PTransNode # transformed for loop body
yieldStmts: int # we count the number of yield statements,
# because we need to introduce new variables
# if we encounter the 2nd yield statement
next: PTransCon # for stacking
TTransfContext = object of passes.TPassContext
module: PSym
transCon: PTransCon # top of a TransCon stack
inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
inLoop: int # > 0 if we are in a loop
PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} =
result = PTransNode(shallowCopy(a))
proc newTransNode(kind: TNodeKind, info: TLineInfo,
sons: int): PTransNode {.inline.} =
var x = newNodeI(kind, info)
newSeq(x.sons, sons)
result = x.PTransNode
proc newTransNode(kind: TNodeKind, n: PNode,
sons: int): PTransNode {.inline.} =
var x = newNodeIT(kind, n.info, n.typ)
newSeq(x.sons, sons)
x.typ = n.typ
result = x.PTransNode
proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
var n = PNode(a)
n.sons[i] = PNode(x)
proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
var n = PNode(a)
result = n.sons[i].PTransNode
proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
proc newTransCon(owner: PSym): PTransCon =
assert owner != nil
new(result)
initIdNodeTable(result.mapping)
result.owner = owner
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon
c.transCon = t
proc popTransCon(c: PTransf) =
if (c.transCon == nil): InternalError("popTransCon")
c.transCon = c.transCon.next
proc getCurrOwner(c: PTransf): PSym =
if c.transCon != nil: result = c.transCon.owner
else: result = c.module
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PSym =
result = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
result.typ = skipTypes(typ, {tyGenericInst})
incl(result.flags, sfFromGeneric)
proc transform(c: PTransf, n: PNode): PTransNode
proc transformSons(c: PTransf, n: PNode): PTransNode =
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
result[i] = transform(c, n.sons[i])
proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
result[0] = PTransNode(le)
result[1] = ri
proc transformSymAux(c: PTransf, n: PNode): PNode =
var b: PNode
var tc = c.transCon
if sfBorrow in n.sym.flags:
# simply exchange the symbol:
b = n.sym.getBody
if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym)
b.info = n.info
else:
b = n
while tc != nil:
result = IdNodeTableGet(tc.mapping, b.sym)
if result != nil: return
tc = tc.next
result = b
proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n))
proc transformVarSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v)
for i in countup(0, sonsLen(v)-1):
var it = v.sons[i]
if it.kind == nkCommentStmt:
result[i] = PTransNode(it)
elif it.kind == nkIdentDefs:
if it.sons[0].kind != nkSym: InternalError(it.info, "transformVarSection")
InternalAssert(it.len == 3)
var newVar = copySym(it.sons[0].sym)
incl(newVar.flags, sfFromGeneric)
# fixes a strange bug for rodgen:
#include(it.sons[0].sym.flags, sfFromGeneric);
newVar.owner = getCurrOwner(c)
IdNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
var defs = newTransNode(nkIdentDefs, it.info, 3)
defs[0] = newSymNode(newVar).PTransNode
defs[1] = it.sons[1].PTransNode
defs[2] = transform(c, it.sons[2])
result[i] = defs
else:
if it.kind != nkVarTuple:
InternalError(it.info, "transformVarSection: not nkVarTuple")
var L = sonsLen(it)
var defs = newTransNode(it.kind, it.info, L)
for j in countup(0, L-3):
var newVar = copySym(it.sons[j].sym)
incl(newVar.flags, sfFromGeneric)
newVar.owner = getCurrOwner(c)
IdNodeTablePut(c.transCon.mapping, it.sons[j].sym, newSymNode(newVar))
defs[j] = newSymNode(newVar).PTransNode
assert(it.sons[L-2].kind == nkEmpty)
defs[L-1] = transform(c, it.sons[L-1])
result[i] = defs
proc transformConstSection(c: PTransf, v: PNode): PTransNode =
result = newTransNode(v)
for i in countup(0, sonsLen(v)-1):
var it = v.sons[i]
if it.kind == nkCommentStmt:
result[i] = PTransNode(it)
else:
if it.kind != nkConstDef: InternalError(it.info, "transformConstSection")
if it.sons[0].kind != nkSym:
InternalError(it.info, "transformConstSection")
if sfFakeConst in it[0].sym.flags:
var b = newNodeI(nkConstDef, it.info)
addSon(b, it[0])
addSon(b, ast.emptyNode) # no type description
addSon(b, transform(c, it[2]).pnode)
result[i] = PTransNode(b)
else:
result[i] = PTransNode(it)
proc hasContinue(n: PNode): bool =
case n.kind
of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: nil
of nkContinueStmt: result = true
else:
for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym =
result = newSym(skLabel, nil, getCurrOwner(c), n.info)
result.name = getIdent(genPrefix & $result.id)
proc transformBlock(c: PTransf, n: PNode): PTransNode =
var labl: PSym
if n.sons[0].kind != nkEmpty:
# already named block? -> Push symbol on the stack:
labl = n.sons[0].sym
else:
labl = newLabel(c, n)
c.breakSyms.add(labl)
result = transformSons(c, n)
discard c.breakSyms.pop
result[0] = newSymNode(labl).PTransNode
proc transformBreak(c: PTransf, n: PNode): PTransNode =
if c.inLoop > 0 or n.sons[0].kind != nkEmpty:
result = n.ptransNode
else:
let labl = c.breakSyms[c.breakSyms.high]
result = transformSons(c, n)
result[0] = newSymNode(labl).PTransNode
proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same!
# We fix this here by making every 'break' belong to its enclosing loop
# and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already).
if hasContinue(n):
let labl = newLabel(c, n)
c.contSyms.add(labl)
result = newTransNode(nkBlockStmt, n.info, 2)
result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n)
discard c.contSyms.pop()
else:
result = transform(c, n)
proc newTupleAccess(tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i])
addSon(result, copyTree(tup))
var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
lit.intVal = i
addSon(result, lit)
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
# XXX: BUG: what if `n` is an expression with side-effects?
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))))
proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkVarSection, nkLetSection:
result = transformVarSection(c, n)
else:
result = newTransNode(n)
for i in countup(0, sonsLen(n)-1):
result[i] = introduceNewLocalVars(c, n.sons[i])
proc transformYield(c: PTransf, n: PNode): PTransNode =
result = newTransNode(nkStmtList, n.info, 0)
var e = n.sons[0]
# c.transCon.forStmt.len == 3 means that there is one for loop variable
# and thus no tuple unpacking:
if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
c.transCon.forStmt.len != 3:
e = skipConv(e)
if e.kind == nkPar:
for i in countup(0, sonsLen(e) - 1):
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, e.sons[i])))
else:
unpackTuple(c, e, result)
else:
var x = transform(c, e)
add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
inc(c.transCon.yieldStmts)
if c.transCon.yieldStmts <= 1:
# common case
add(result, c.transCon.forLoopBody)
else:
# we need to introduce new local variables:
add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.pnode))
proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
result = transformSons(c, n)
var n = result.pnode
case n.sons[0].kind
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
var m = n.sons[0].sons[0]
if m.kind == a or m.kind == b:
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[0] = m.sons[0]
result = PTransNode(n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n.sons[0].sons[1]
if m.kind == a or m.kind == b:
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[1] = m.sons[0]
result = PTransNode(n.sons[0])
else:
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0])
proc transformConv(c: PTransf, n: PNode): PTransNode =
# numeric types need range checks:
var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
# we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source):
# XXX int64 -> float conversion?
result = transformSons(c, n)
elif firstOrd(dest) <= firstOrd(source) and
lastOrd(source) <= lastOrd(dest):
# BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check:
result = transformSons(c, n)
else:
# generate a range check:
if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3)
else:
result = newTransNode(nkChckRange, n, 3)
dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1])
result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128:
if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1])
result[1] = copyTree(dest.n.sons[0]).PTransNode
result[2] = copyTree(dest.n.sons[1]).PTransNode
else:
result = transformSons(c, n)
of tyOpenArray, tyVarargs:
result = transform(c, n.sons[1])
of tyCString:
if source.kind == tyString:
result = newTransNode(nkStringToCString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyString:
if source.kind == tyCString:
result = newTransNode(nkCStringToString, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyRef, tyPtr:
dest = skipTypes(dest, abstractPtrs)
source = skipTypes(source, abstractPtrs)
if source.kind == tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transform(c, n.sons[1])
else:
result = transformSons(c, n)
of tyObject:
var diff = inheritanceDiff(dest, source)
if diff < 0:
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n.sons[1])
elif diff > 0:
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n.sons[1])
else:
result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal, tyTypeClass:
result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc.
else:
result = transformSons(c, n)
type
TPutArgInto = enum
paDirectMapping, paFastAsgn, paVarAsgn
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
# This analyses how to treat the mapping "formal <-> arg" in an
# inline context.
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
return paDirectMapping # XXX really correct?
# what if ``arg`` has side-effects?
case arg.kind
of nkEmpty..nkNilLit:
result = paDirectMapping
of nkPar, nkCurly, nkBracket:
result = paFastAsgn
for i in countup(0, sonsLen(arg) - 1):
if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping
else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn
proc transformFor(c: PTransf, n: PNode): PTransNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters
if n.kind != nkForStmt: InternalError(n.info, "transformFor")
#echo "transforming: ", renderTree(n)
result = newTransNode(nkStmtList, n.info, 0)
var length = sonsLen(n)
var loopBody = transformLoopBody(c, n.sons[length-1])
var v = newNodeI(nkVarSection, n.info)
for i in countup(0, length - 3):
addVar(v, copyTree(n.sons[i])) # declare new vars
add(result, v.ptransNode)
var call = n.sons[length - 2]
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym:
InternalError(call.info, "transformFor")
# Bugfix: inlined locals belong to the invoking routine, not to the invoked
# iterator!
let iter = call.sons[0].sym
var newC = newTransCon(getCurrOwner(c))
newC.forStmt = n
newC.forLoopBody = loopBody
if iter.kind != skIterator: InternalError(call.info, "transformFor")
# generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1):
var arg = transform(c, call.sons[i]).pnode
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
case putArgInto(arg, formal.typ)
of paDirectMapping:
IdNodeTablePut(newC.mapping, formal, arg)
of paFastAsgn:
# generate a temporary and produce an assignment statement:
var temp = newTemp(c, formal.typ, formal.info)
addVar(v, newSymNode(temp))
add(result, newAsgnStmt(c, newSymNode(temp), arg.ptransNode))
IdNodeTablePut(newC.mapping, formal, newSymNode(temp))
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
IdNodeTablePut(newC.mapping, formal, arg)
# XXX BUG still not correct if the arg has a side effect!
var body = iter.getBody
pushInfoContext(n.info)
inc(c.inlining)
add(result, transform(c, body))
dec(c.inlining)
popInfoContext()
popTransCon(c)
#echo "transformed: ", renderTree(n)
proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
result = call.sons[0].sym.magic
else:
result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator
result = newTransNode(nkCaseStmt, n, 0)
var ifs = PTransNode(nil)
for i in 0 .. sonsLen(n)-1:
var it = n.sons[i]
var e = transform(c, it)
case it.kind
of nkElifBranch:
if ifs.pnode == nil:
ifs = newTransNode(nkIfStmt, it.info, 0)
ifs.add(e)
of nkElse:
if ifs.pnode == nil: result.add(e)
else: ifs.add(e)
else:
result.add(e)
if ifs.pnode != nil:
var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = ifs
result.add(elseBranch)
elif result.Pnode.lastSon.kind != nkElse and not (
skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
{tyInt..tyInt64, tyChar, tyEnum}):
# fix a stupid code gen bug by normalizing:
var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
add(result, elseBranch)
proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
# XXX this is really bad; transf should use a proper AST visitor
if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
result = n.ptransnode
else:
result = newTransNode(n)
for i in 0 .. < n.len:
result[i] = transform(c, skipConv(n.sons[i]))
proc getMergeOp(n: PNode): PSym =
case n.kind
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
if (n.sons[0].Kind == nkSym) and (n.sons[0].sym.kind == skProc) and
(sfMerge in n.sons[0].sym.flags):
result = n.sons[0].sym
else: nil
proc flattenTreeAux(d, a: PNode, op: PSym) =
var op2 = getMergeOp(a)
if op2 != nil and
(op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
else:
addSon(d, copyTree(a))
proc flattenTree(root: PNode): PNode =
var op = getMergeOp(root)
if op != nil:
result = copyNode(root)
addSon(result, copyTree(root.sons[0]))
flattenTreeAux(result, root, op)
else:
result = root
proc transformCall(c: PTransf, n: PNode): PTransNode =
var n = flattenTree(n)
var op = getMergeOp(n)
if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3):
result = newTransNode(nkCall, n, 0)
add(result, transform(c, n.sons[0]))
var j = 1
while j < sonsLen(n):
var a = transform(c, n.sons[j]).pnode
inc(j)
if isConstExpr(a):
while (j < sonsLen(n)):
let b = transform(c, n.sons[j]).pnode
if not isConstExpr(b): break
a = evalOp(op.magic, n, a, b, nil)
inc(j)
add(result, a.ptransnode)
if len(result) == 2: result = result[1]
else:
let s = transformSons(c, n).pnode
# bugfix: check after 'transformSons' if it's still a method call:
# use the dispatcher for the call:
if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
result = methodCall(s).ptransNode
else:
result = s.ptransNode
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array:
result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
cnst.len != 0
proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind
of nkSym:
result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves:
result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n)
of procDefs:
when false:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if s.ast.sons[bodyPos] != n.sons[bodyPos]:
# somehow this can happen ... :-/
s.ast.sons[bodyPos] = n.sons[bodyPos]
#n.sons[bodyPos] = liftLambdas(s, n)
#if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkMacroDef:
# XXX no proper closure support yet:
when false:
if n.sons[genericParamsPos].kind == nkEmpty:
var s = n.sons[namePos].sym
n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n)
of nkForStmt:
inc c.inLoop
result = transformFor(c, n)
dec c.inLoop
of nkParForStmt:
inc c.inLoop
result = transformSons(c, n)
dec c.inLoop
of nkCaseStmt: result = transformCase(c, n)
of nkContinueStmt:
result = PTransNode(newNode(nkBreakStmt))
var labl = c.contSyms[c.contSyms.high]
add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt:
inc c.inLoop
result = newTransNode(n)
result[0] = transform(c, n.sons[0])
result[1] = transformLoopBody(c, n.sons[1])
dec c.inLoop
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit:
result = transformCall(c, n)
of nkAddr, nkHiddenAddr:
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
of nkDerefExpr, nkHiddenDeref:
result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = transformConv(c, n)
of nkDiscardStmt:
result = PTransNode(n)
if n.sons[0].kind != nkEmpty:
result = transformSons(c, n)
if isConstExpr(PNode(result).sons[0]):
# ensure that e.g. discard "some comment" gets optimized away
# completely:
result = PTransNode(newNode(nkCommentStmt))
of nkCommentStmt, nkTemplateDef:
return n.ptransNode
of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3``
return transformConstSection(c, n)
of nkTypeSection:
# no need to transform type sections:
return PTransNode(n)
of nkVarSection, nkLetSection:
if c.inlining > 0:
# we need to copy the variables for multiple yield statements:
result = transformVarSection(c, n)
else:
result = transformSons(c, n)
of nkYieldStmt:
if c.inlining > 0:
result = transformYield(c, n)
else:
result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n)
else:
result = transformSons(c, n)
var cnst = getConstExpr(c.module, PNode(result))
# we inline constants if they are not complex constants:
if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(c: PTransf, n: PNode): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node.
if passes.skipCodegen(n) or c.fromCache or nfTransf in n.flags: return n
pushTransCon(c, newTransCon(getCurrOwner(c)))
result = PNode(transform(c, n))
popTransCon(c)
incl(result.flags, nfTransf)
proc openTransf(module: PSym, filename: string): PTransf =
new(result)
result.contSyms = @[]
result.breakSyms = @[]
result.module = module
when false:
proc openTransfCached(module: PSym, filename: string,
rd: PRodReader): PPassContext =
result = openTransf(module, filename)
for m in items(rd.methods): methodDef(m, true)
proc transfPass(): TPass =
initPass(result)
result.open = openTransf
result.openCached = openTransfCached
result.process = processTransf
result.close = processTransf # we need to process generics too!
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
if prc.kind != skMacro:
# XXX no closures yet for macros:
result = liftLambdas(prc, result)
incl(result.flags, nfTransf)
proc transformStmt*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf)
proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
incl(result.flags, nfTransf)