This commit is contained in:
Araq 2012-05-20 10:56:17 +02:00
commit 15dd3a2251
9 changed files with 95 additions and 16 deletions

View file

@ -235,6 +235,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
preserveBreakIdx: preserveBreakIdx:
p.breakIdx = startBlock(p, "while (1) {$n") p.breakIdx = startBlock(p, "while (1) {$n")
p.blocks[p.breakIdx].isLoop = true
initLocExpr(p, t.sons[0], a) initLocExpr(p, t.sons[0], a)
if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
let label = assignLabel(p.blocks[p.breakIdx]) let label = assignLabel(p.blocks[p.breakIdx])
@ -265,6 +266,11 @@ proc genBreakStmt(p: BProc, t: PNode) =
var sym = t.sons[0].sym var sym = t.sons[0].sym
assert(sym.loc.k == locOther) assert(sym.loc.k == locOther)
idx = sym.loc.a idx = sym.loc.a
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
InternalError(t.info, "no loop to break")
let label = assignLabel(p.blocks[idx]) let label = assignLabel(p.blocks[idx])
blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts)
genLineDir(p, t) genLineDir(p, t)

View file

@ -54,6 +54,7 @@ type
# nil if label is not used # nil if label is not used
nestedTryStmts*: int # how many try statements is it nested into nestedTryStmts*: int # how many try statements is it nested into
sections*: TCProcSections # the code beloging sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop
TCProc{.final.} = object # represents C proc that is currently generated TCProc{.final.} = object # represents C proc that is currently generated
prc*: PSym # the Nimrod proc that this C proc belongs to prc*: PSym # the Nimrod proc that this C proc belongs to

View file

@ -47,6 +47,7 @@ type
id*: int # the ID of the label; positive means that it id*: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted) # has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into nestedTryStmts*: int # how many try statements is it nested into
isLoop: bool # whether it's a 'block' or 'while'
TGlobals{.final.} = object TGlobals{.final.} = object
typeInfo*, code*: PRope typeInfo*, code*: PRope
@ -467,6 +468,7 @@ proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) =
setlen(p.blocks, length + 1) setlen(p.blocks, length + 1)
p.blocks[length].id = - p.unique p.blocks[length].id = - p.unique
p.blocks[length].nestedTryStmts = p.nestedTryStmts p.blocks[length].nestedTryStmts = p.nestedTryStmts
p.blocks[length].isLoop = true
labl = p.unique labl = p.unique
gen(p, n.sons[0], cond) gen(p, n.sons[0], cond)
genStmt(p, n.sons[1], stmt) genStmt(p, n.sons[1], stmt)
@ -635,13 +637,18 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) =
idx: int idx: int
sym: PSym sym: PSym
genLineDir(p, n, r) genLineDir(p, n, r)
idx = len(p.blocks) - 1
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
# named break? # named break?
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
sym = n.sons[0].sym sym = n.sons[0].sym
assert(sym.loc.k == locOther) assert(sym.loc.k == locOther)
idx = sym.loc.a idx = sym.loc.a
else:
# an unnamed 'break' can only break a loop after 'transf' pass:
idx = len(p.blocks) - 1
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
InternalError(n.info, "no loop to break")
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts) finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts)
appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)]) appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)])

View file

@ -162,16 +162,17 @@ proc evalWhile(c: PEvalContext, n: PNode): PNode =
stackTrace(c, n, errTooManyIterations) stackTrace(c, n, errTooManyIterations)
break break
proc evalBlock(c: PEvalContext, n: PNode): PNode = proc evalBlock(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n.sons[1], {}) result = evalAux(c, n.sons[1], {})
if result.kind == nkBreakStmt: if result.kind == nkBreakStmt:
if result.sons[0] != nil: if result.sons[0] != nil:
assert(result.sons[0].kind == nkSym) assert(result.sons[0].kind == nkSym)
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode
else: # blocks can only be left with an explicit label now!
result = emptyNode # consume ``break`` token #else:
# result = emptyNode # consume ``break`` token
proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = proc evalFinally(c: PEvalContext, n, exc: PNode): PNode =
var finallyNode = lastSon(n) var finallyNode = lastSon(n)

View file

@ -46,7 +46,8 @@ type
transCon: PTransCon # top of a TransCon stack transCon: PTransCon # top of a TransCon stack
inlining: int # > 0 if we are in inlining context (copy vars) inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc nestedProcs: int # > 0 if we are in a nested proc
blocksyms: seq[PSym] contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
inLoop: int # > 0 if we are in a loop
transformedInnerProcs: TIntSet transformedInnerProcs: TIntSet
PTransf = ref TTransfContext PTransf = ref TTransfContext
@ -252,6 +253,31 @@ proc hasContinue(n: PNode): bool =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if hasContinue(n.sons[i]): return true if hasContinue(n.sons[i]): return true
proc newLabel(c: PTransf, n: PNode): PSym =
result = newSym(skLabel, nil, getCurrOwner(c))
result.name = getIdent(genPrefix & $result.id)
result.info = n.info
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 = proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# XXX BUG: What if it contains "continue" and "break"? "break" needs # XXX BUG: What if it contains "continue" and "break"? "break" needs
# an explicit label too, but not the same! # an explicit label too, but not the same!
@ -260,15 +286,13 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
# and changing all breaks that belong to a 'block' by annotating it with # and changing all breaks that belong to a 'block' by annotating it with
# a label (if it hasn't one already). # a label (if it hasn't one already).
if hasContinue(n): if hasContinue(n):
var labl = newSym(skLabel, nil, getCurrOwner(c)) let labl = newLabel(c, n)
labl.name = getIdent(genPrefix & $labl.id) c.contSyms.add(labl)
labl.info = n.info
c.blockSyms.add(labl)
result = newTransNode(nkBlockStmt, n.info, 2) result = newTransNode(nkBlockStmt, n.info, 2)
result[0] = newSymNode(labl).PTransNode result[0] = newSymNode(labl).PTransNode
result[1] = transform(c, n) result[1] = transform(c, n)
discard c.blockSyms.pop() discard c.contSyms.pop()
else: else:
result = transform(c, n) result = transform(c, n)
@ -631,16 +655,22 @@ proc transform(c: PTransf, n: PNode): PTransNode =
n.sons[bodyPos] = PNode(transform(c, s.getBody)) n.sons[bodyPos] = PNode(transform(c, s.getBody))
if n.kind == nkMethodDef: methodDef(s, false) if n.kind == nkMethodDef: methodDef(s, false)
result = PTransNode(n) result = PTransNode(n)
of nkForStmt: result = transformFor(c, n) of nkForStmt:
inc c.inLoop
result = transformFor(c, n)
dec c.inLoop
of nkCaseStmt: result = transformCase(c, n) of nkCaseStmt: result = transformCase(c, n)
of nkContinueStmt: of nkContinueStmt:
result = PTransNode(newNode(nkBreakStmt)) result = PTransNode(newNode(nkBreakStmt))
var labl = c.blockSyms[c.blockSyms.high] var labl = c.contSyms[c.contSyms.high]
add(result, PTransNode(newSymNode(labl))) add(result, PTransNode(newSymNode(labl)))
of nkBreakStmt: result = transformBreak(c, n)
of nkWhileStmt: of nkWhileStmt:
inc c.inLoop
result = newTransNode(n) result = newTransNode(n)
result[0] = transform(c, n.sons[0]) result[0] = transform(c, n.sons[0])
result[1] = transformLoopBody(c, n.sons[1]) result[1] = transformLoopBody(c, n.sons[1])
dec c.inLoop
of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
nkCallStrLit: nkCallStrLit:
result = transformCall(c, n) result = transformCall(c, n)
@ -671,6 +701,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
result = transformYield(c, n) result = transformYield(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
of nkBlockStmt, nkBlockExpr:
result = transformBlock(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
var cnst = getConstExpr(c.module, PNode(result)) var cnst = getConstExpr(c.module, PNode(result))
@ -692,7 +724,8 @@ proc processTransf(context: PPassContext, n: PNode): PNode =
proc openTransf(module: PSym, filename: string): PPassContext = proc openTransf(module: PSym, filename: string): PPassContext =
var n: PTransf var n: PTransf
new(n) new(n)
n.blocksyms = @[] n.contSyms = @[]
n.breakSyms = @[]
n.module = module n.module = module
n.transformedInnerProcs = initIntSet() n.transformedInnerProcs = initIntSet()
result = n result = n

View file

@ -5,6 +5,7 @@ Nimrod's Garbage Collector
:Author: Andreas Rumpf :Author: Andreas Rumpf
:Version: |nimrodversion| :Version: |nimrodversion|
"The road to hell is paved with good intentions."
Introduction Introduction
============ ============

View file

@ -947,7 +947,9 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
## If ``format == ffScientific`` then precision is the maximum number ## If ``format == ffScientific`` then precision is the maximum number
## of significant digits to be printed. ## of significant digits to be printed.
## `precision`'s default value is the maximum number of meaningful digits ## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nimrod's ``biggestFloat`` type. ## after the decimal point for Nimrod's ``biggestFloat`` type.
##
## If ``precision == 0``, it uses the
const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e'] const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e']
var var
frmtstr {.noinit.}: array[0..5, char] frmtstr {.noinit.}: array[0..5, char]

28
tests/run/tcontinue.nim Normal file
View file

@ -0,0 +1,28 @@
discard """
output: "came here"
"""
var i = 0
while i < 400:
if i == 10: break
elif i == 3:
inc i
continue
inc i
var f = "failure"
var j = 0
while j < 300:
for x in 0..34:
if j < 300: continue
if x == 10:
echo "failure: should never happen"
break
f = "came here"
break
if i == 10:
echo f
else:
echo "failure"

View file

@ -1,7 +1,7 @@
version 0.9.0 version 0.9.0
============= =============
- fix transformLoopBody - make stack traces part of the exception in debug builds
- make templates hygienic by default - make templates hygienic by default
- ``bind`` for overloaded symbols does not work apparently - ``bind`` for overloaded symbols does not work apparently
- ``=`` should be overloadable; requires specialization for ``=`` - ``=`` should be overloadable; requires specialization for ``=``