summary refs log tree commit diff stats
path: root/compiler/transf.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/transf.nim')
-rw-r--r--compiler/transf.nim108
1 files changed, 76 insertions, 32 deletions
diff --git a/compiler/transf.nim b/compiler/transf.nim
index dece1ac18..b9b06675e 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -13,7 +13,7 @@
 # * inlines iterators
 # * inlines constants
 # * performes constant folding
-# * converts "continue" to "break"
+# * converts "continue" to "break"; disambiguates "break"
 # * introduces method dispatchers
 # * performs lambda lifting for closure support
 
@@ -44,7 +44,6 @@ type
     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.} = 
@@ -201,6 +200,18 @@ proc newLabel(c: PTransf, n: PNode): PSym =
   result = newSym(skLabel, nil, getCurrOwner(c), n.info)
   result.name = getIdent(genPrefix & $result.id)
 
+proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) =
+  if n.kind in {nkBlockStmt, nkBlockExpr}:
+    if n.sons[0].kind == nkSym:
+      let x = newLabel(c, n[0])
+      idTablePut(symMap, n[0].sym, x)
+      n.sons[0].sym = x
+  if n.kind == nkSym and n.sym.kind == skLabel:
+    let x = PSym(idTableGet(symMap, n.sym))
+    if x != nil: n.sym = x
+  else:
+    for i in 0 .. <safeLen(n): freshLabels(c, n.sons[i], symMap)
+
 proc transformBlock(c: PTransf, n: PNode): PTransNode =
   var labl: PSym
   if n.sons[0].kind != nkEmpty:
@@ -213,14 +224,6 @@ proc transformBlock(c: PTransf, n: PNode): PTransNode =
   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!
@@ -239,6 +242,37 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
   else: 
     result = transform(c, n)
 
+proc transformWhile(c: PTransf; n: PNode): PTransNode =
+  if c.inlining > 0:
+    result = transformSons(c, n)
+  else:
+    let labl = newLabel(c, n)
+    c.breakSyms.add(labl)
+    result = newTransNode(nkBlockStmt, n.info, 2)
+    result[0] = newSymNode(labl).PTransNode
+
+    var body = newTransNode(n)
+    for i in 0..n.len-2:
+      body[i] = transform(c, n.sons[i])
+    body[<n.len] = transformLoopBody(c, n.sons[<n.len])
+    result[1] = body
+    discard c.breakSyms.pop
+
+proc transformBreak(c: PTransf, n: PNode): PTransNode =
+  if n.sons[0].kind != nkEmpty or c.inlining > 0:
+    result = n.PTransNode
+    when false:
+      let lablCopy = idNodeTableGet(c.transCon.mapping, n.sons[0].sym)
+      if lablCopy.isNil:
+        result = n.PTransNode
+      else:
+        result = newTransNode(n.kind, n.info, 1)
+        result[0] = lablCopy.PTransNode
+  else:
+    let labl = c.breakSyms[c.breakSyms.high]
+    result = transformSons(c, n)
+    result[0] = newSymNode(labl).PTransNode
+
 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): 
@@ -424,20 +458,32 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
 
   var length = sonsLen(n)
   var call = n.sons[length - 2]
+
+  let labl = newLabel(c, n)
+  c.breakSyms.add(labl)
+  result = newTransNode(nkBlockStmt, n.info, 2)
+  result[0] = newSymNode(labl).PTransNode
+
   if call.typ.kind != tyIter and
     (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or 
       call.sons[0].sym.kind != skIterator):
     n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
-    return lambdalifting.liftForLoop(n).PTransNode
-    #InternalError(call.info, "transformFor")
+    result[1] = lambdalifting.liftForLoop(n).PTransNode
+    discard c.breakSyms.pop
+    return result
   
   #echo "transforming: ", renderTree(n)
-  result = newTransNode(nkStmtList, n.info, 0)
+  var stmtList = newTransNode(nkStmtList, n.info, 0)
+  
   var loopBody = transformLoopBody(c, n.sons[length-1])
+
+  result[1] = stmtList
+  discard c.breakSyms.pop
+
   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)
+  add(stmtList, v.PTransNode)
   
   # Bugfix: inlined locals belong to the invoking routine, not to the invoked
   # iterator!
@@ -453,27 +499,32 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
     var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym 
     if arg.typ.kind == tyIter: continue
     case putArgInto(arg, formal.typ)
-    of paDirectMapping: 
+    of paDirectMapping:
       idNodeTablePut(newC.mapping, formal, arg)
-    of paFastAsgn: 
+    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))
+      add(stmtList, 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
+  var body = iter.getBody.copyTree
   pushInfoContext(n.info)
+  # XXX optimize this somehow. But the check "c.inlining" is not correct:
+  var symMap: TIdTable
+  initIdTable symMap
+  freshLabels(c, body, symMap)
+
   inc(c.inlining)
-  add(result, transform(c, body))
-  #findWrongOwners(c, result.pnode)
+  add(stmtList, transform(c, body))
+  #findWrongOwners(c, stmtList.pnode)
   dec(c.inlining)
   popInfoContext()
   popTransCon(c)
-  # echo "transformed: ", result.PNode.renderTree
+  # echo "transformed: ", stmtList.PNode.renderTree
   
 proc getMagicOp(call: PNode): TMagic = 
   if call.sons[0].kind == nkSym and
@@ -643,25 +694,16 @@ proc transform(c: PTransf, n: PNode): PTransNode =
         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(newNodeI(nkBreakStmt, n.info))
     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 nkWhileStmt: result = transformWhile(c, n)
   of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, 
      nkCallStrLit: 
     result = transformCall(c, n)
@@ -740,6 +782,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
     #  result = lambdalifting.liftIterator(prc, result)
     incl(result.flags, nfTransf)
     when useEffectSystem: trackProc(prc, result)
+    if prc.name.s == "testbody":
+      echo renderTree(result)
 
 proc transformStmt*(module: PSym, n: PNode): PNode =
   if nfTransf in n.flags: