diff options
author | Araq <rumpf_a@web.de> | 2012-11-15 01:27:25 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-11-15 01:27:25 +0100 |
commit | 814fcb263939e8127eb89c379c448f481df14dbd (patch) | |
tree | ffbe247e03da9373caad8d1d079e21b314bf4cb1 | |
parent | c439010d5275e4f6c41573f2679a298e73d128cc (diff) | |
download | Nim-814fcb263939e8127eb89c379c448f481df14dbd.tar.gz |
bugfix: stack traces; first class iterators almost working
-rwxr-xr-x | compiler/ast.nim | 3 | ||||
-rwxr-xr-x | compiler/ccgexprs.nim | 2 | ||||
-rwxr-xr-x | compiler/ccgstmts.nim | 10 | ||||
-rwxr-xr-x | compiler/ccgtypes.nim | 2 | ||||
-rwxr-xr-x | compiler/cgen.nim | 2 | ||||
-rw-r--r-- | compiler/lambdalifting.nim | 110 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 13 | ||||
-rwxr-xr-x | compiler/transf.nim | 16 | ||||
-rwxr-xr-x | lib/system/debugger.nim | 18 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 18 | ||||
-rw-r--r-- | tests/run/titer8.nim | 33 | ||||
-rwxr-xr-x | todo.txt | 8 | ||||
-rwxr-xr-x | web/index.txt | 2 |
13 files changed, 186 insertions, 51 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 46be0379f..46cb792b5 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -205,7 +205,8 @@ type nkReturnToken, # token used for interpretation nkClosure, # (prc, env)-pair (internally used for code gen) nkGotoState, # used for the state machine (for iterators) - nkState # give a label to a code section (for iterators) + nkState, # give a label to a code section (for iterators) + nkBreakState # special break statement for easier code generation TNodeKinds* = set[TNodeKind] type diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5989d94b3..f031e72b0 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1678,7 +1678,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) - of skProc, skConverter: + of skProc, skConverter, skIterator: genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: InternalError(e.info, "expr: proc not init " & sym.name.s) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 4ff309424..9c9b61088 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -106,10 +106,17 @@ proc genGotoState(p: BProc, n: PNode) = var a: TLoc initLocExpr(p, n.sons[0], a) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) + p.BeforeRetNeeded = true + lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", []) for i in 0 .. lastOrd(n.sons[0].typ): lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [toRope(i)]) lineF(p, cpsStmts, "}$n", []) +proc genBreakState(p: BProc, n: PNode) = + var a: TLoc + initLocExpr(p, n.sons[0], a) + lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)]) + proc genSingleVar(p: BProc, a: PNode) = var v = a.sons[0].sym if sfCompileTime in v.flags: return @@ -713,7 +720,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode): PRope = app(result, t.sons[i].strVal) of nkSym: var sym = t.sons[i].sym - if sym.kind in {skProc, skMethod}: + if sym.kind in {skProc, skIterator, skMethod}: var a: TLoc initLocExpr(p, t.sons[i], a) app(result, rdLoc(a)) @@ -884,5 +891,6 @@ proc genStmts(p: BProc, t: PNode) = of nkParForStmt: genParForStmt(p, t) of nkState: genState(p, t) of nkGotoState: genGotoState(p, t) + of nkBreakState: genBreakState(p, t) else: internalError(t.info, "genStmts(" & $t.kind & ')') diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 0495c33fb..62426a435 100755 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -69,7 +69,7 @@ proc mangleName(s: PSym): PRope = if result == nil: if gCmd == cmdCompileToLLVM: case s.kind - of skProc, skMethod, skConverter, skConst: + of skProc, skMethod, skConverter, skConst, skIterator: result = toRope("@") of skVar, skForVar, skResult, skLet: if sfGlobal in s.flags: result = toRope("@") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index bcb9f8ffc..5bdbefde1 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -589,7 +589,7 @@ proc cgsym(m: BModule, name: string): PRope = var sym = magicsys.getCompilerProc(name) if sym != nil: case sym.kind - of skProc, skMethod, skConverter: genProc(m, sym) + of skProc, skMethod, skConverter, skIterator: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) else: InternalError("cgsym: " & name) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 925cccb72..ada14edf2 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -214,8 +214,14 @@ proc addHiddenParam(routine: PSym, param: PSym) = incl(routine.typ.flags, tfCapturesEnv) #echo "produced environment: ", param.id, " for ", routine.name.s +proc getHiddenParam(routine: PSym): PSym = + let params = routine.ast.sons[paramsPos] + let hidden = lastSon(params) + assert hidden.kind == nkSym + result = hidden.sym + proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = s.kind in {skProc, skIterator, skMethod, skConverter} and + result = s.kind in {skProc, skMethod, skConverter} and s.owner == outerProc and not isGenericRoutine(s) #s.typ.callConv == ccClosure @@ -481,7 +487,6 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = newSymNode(getClosureVar(o, e)))) proc transformOuterProc(o: POuterContext, n: PNode): PNode = - # XXX I wish I knew where these 'nil' nodes come from: 'array[.. |X]' if n == nil: return nil case n.kind of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil @@ -593,13 +598,16 @@ proc newIterResult(iter: PSym): PSym = result.typ = iter.typ.sons[0] incl(result.flags, sfUsed) +proc interestingIterVar(s: PSym): bool {.inline.} = + result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags + proc transfIterBody(c: var TIterContext, n: PNode): PNode = # gather used vars for closure generation if n == nil: return nil case n.kind of nkSym: var s = n.sym - if interestingVar(s) and c.iter.id == s.owner.id: + if interestingIterVar(s) and c.iter.id == s.owner.id: if not containsOrIncl(c.capturedVars, s.id): addField(c.tup, s) result = indirectAccess(newSymNode(c.closureParam), s, n.info) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil @@ -609,19 +617,20 @@ proc transfIterBody(c: var TIterContext, n: PNode): PNode = var stateAsgnStmt = newNodeI(nkAsgn, n.info) stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) - stateAsgnStmt.add(newIntNode(nkIntLit, stateNo)) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) var retStmt = newNodeI(nkReturnStmt, n.info) if n.sons[0].kind != nkEmpty: var a = newNodeI(nkAsgn, n.sons[0].info) + var retVal = transfIterBody(c, n.sons[0]) addSon(a, newSymNode(c.resultSym)) - addSon(a, n.sons[0]) + addSon(a, if retVal.isNil: n.sons[0] else: retVal) retStmt.add(a) else: retStmt.add(emptyNode) var stateLabelStmt = newNodeI(nkState, n.info) - stateLabelStmt.add(newIntNode(nkIntLit, stateNo-1)) + stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) result = newNodeI(nkStmtList, n.info) result.add(stateAsgnStmt) @@ -676,23 +685,82 @@ proc liftIterator*(iter: PSym, body: PNode): PNode = result.add(newBody) else: result.add(body) - - var state1 = newNodeI(nkState, iter.info) - state1.add(newIntNode(nkIntLit, -1)) - result.add(state1) -proc transformForLoop*(iter: PSym, body: PNode): PNode = + var stateAsgnStmt = newNodeI(nkAsgn, iter.info) + stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam), + c.state,iter.info)) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) + result.add(stateAsgnStmt) + +proc liftForLoop*(body: PNode): PNode = + # BIG problem ahead: the iterator could be invoked indirectly, but then + # we don't know what environment to create here: + # + # iterator count(): int = + # yield 0 + # + # iterator count2(): int = + # var x = 3 + # yield x + # inc x + # yield x + # + # proc invoke(iter: iterator(): int) = + # for x in iter(): echo x + # + # --> When to create the closure? --> for the (count) occurence! discard """ - for i in foo(): nil + for i in foo(): ... -Is transformed to: - - cl = createClosure() - while true: - let i = foo(cl) - if cl.state == -1: break -""" - InternalAssert body.kind == nkForStmt - # gather vars in a tuple: + Is transformed to: + + cl = createClosure() + while true: + let i = foo(cl) + nkBreakState(cl.state) + ... + """ var L = body.len + InternalAssert body.kind == nkForStmt and body[L-2].kind in nkCallKinds + var call = body[L-2] + + result = newNodeI(nkStmtList, body.info) + + # static binding? + var env: PSym + if call[0].kind == nkSym and call[0].sym.kind == skIterator: + # createClose() + let iter = call[0].sym + assert iter.kind == skIterator + env = copySym(getHiddenParam(iter)) + + var v = newNodeI(nkVarSection, body.info) + addVar(v, newSymNode(env)) + result.add(v) + # add 'new' statement: + result.add(newCall(getSysSym"internalNew", env)) + var loopBody = newNodeI(nkStmtList, body.info, 3) + var whileLoop = newNodeI(nkWhileStmt, body.info, 2) + whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool)) + whileLoop.sons[1] = loopBody + result.add whileLoop + + # setup loopBody: + # gather vars in a tuple: + var v2 = newNodeI(nkLetSection, body.info) + var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info) + for i in 0 .. L-3: addSon(vpart, body[i]) + + addSon(vpart, ast.emptyNode) # no explicit type + if not env.isnil: + call.sons[0] = makeClosure(call.sons[0].sym, env, body.info) + addSon(vpart, call) + addSon(v2, vpart) + + loopBody.sons[0] = v2 + var bs = newNodeI(nkBreakState, body.info) + bs.addSon(indirectAccess(env, + newSym(skField, getIdent(":state"), env, env.info), body.info)) + loopBody.sons[1] = bs + loopBody.sons[2] = body[L-1] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 586270277..40f081b59 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -817,11 +817,16 @@ proc semIterator(c: PContext, n: PNode): PNode = var t = s.typ if t.sons[0] == nil: LocalError(n.info, errXNeedsReturnType, "iterator") - # iterators are either 'inline' or 'closure': - if s.typ.callConv != ccInline: - s.typ.callConv = ccClosure - # and they always at least use the 'env' for the state field: + # iterators are either 'inline' or 'closure'; for backwards compatibility, + # we require first class iterators to be marked with 'closure' explicitly + # -- at least for 0.9.2. + if s.typ.callConv == ccClosure: incl(s.typ.flags, tfCapturesEnv) + when false: + if s.typ.callConv != ccInline: + s.typ.callConv = ccClosure + # and they always at least use the 'env' for the state field: + incl(s.typ.flags, tfCapturesEnv) if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone: LocalError(n.info, errImplOfXexpected, s.name.s) diff --git a/compiler/transf.nim b/compiler/transf.nim index 767a23111..d93cef203 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -415,17 +415,23 @@ 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") + + var length = sonsLen(n) + var call = n.sons[length - 2] + if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or + call.sons[0].typ.callConv == ccClosure 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") + #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! @@ -697,6 +703,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if prc.kind != skMacro: # XXX no closures yet for macros: result = liftLambdas(prc, result) + if prc.kind == skIterator and prc.typ.callConv == ccClosure: + result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) proc transformStmt*(module: PSym, n: PNode): PNode = diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim index 2add37740..68fbccef6 100755 --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -675,21 +675,27 @@ proc dbgWriteStackTrace(f: PFrame) = i = 0 total = 0 tempFrames: array [0..127, PFrame] - while it != nil and i <= high(tempFrames)-(firstCalls-1): - # the (-1) is for a nil entry that marks where the '...' should occur + # setup long head: + while it != nil and i <= high(tempFrames)-firstCalls: tempFrames[i] = it inc(i) inc(total) it = it.prev + # go up the stack to count 'total': var b = it while it != nil: inc(total) it = it.prev - for j in 1..total-i-(firstCalls-1): - if b != nil: b = b.prev - if total != i: + var skipped = 0 + if total > len(tempFrames): + # skip N + skipped = total-i-firstCalls+1 + for j in 1..skipped: + if b != nil: b = b.prev + # create '...' entry: tempFrames[i] = nil inc(i) + # setup short tail: while b != nil and i <= high(tempFrames): tempFrames[i] = b inc(i) @@ -697,7 +703,7 @@ proc dbgWriteStackTrace(f: PFrame) = for j in countdown(i-1, 0): if tempFrames[j] == nil: write(stdout, "(") - write(stdout, (total-i-1)) + write(stdout, skipped) write(stdout, " calls omitted) ...") else: write(stdout, tempFrames[j].filename) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index fa2622435..9fbdecbb2 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -135,21 +135,27 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = it = f i = 0 total = 0 - while it != nil and i <= high(tempFrames)-(firstCalls-1): - # the (-1) is for a nil entry that marks where the '...' should occur + # setup long head: + while it != nil and i <= high(tempFrames)-firstCalls: tempFrames[i] = it inc(i) inc(total) it = it.prev + # go up the stack to count 'total': var b = it while it != nil: inc(total) it = it.prev - for j in 1..total-i-(firstCalls-1): - if b != nil: b = b.prev - if total != i: + var skipped = 0 + if total > len(tempFrames): + # skip N + skipped = total-i-firstCalls+1 + for j in 1..skipped: + if b != nil: b = b.prev + # create '...' entry: tempFrames[i] = nil inc(i) + # setup short tail: while b != nil and i <= high(tempFrames): tempFrames[i] = b inc(i) @@ -157,7 +163,7 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = for j in countdown(i-1, 0): if tempFrames[j] == nil: add(s, "(") - add(s, $(total-i-1)) + add(s, $skipped) add(s, " calls omitted) ...") else: var oldLen = s.len diff --git a/tests/run/titer8.nim b/tests/run/titer8.nim new file mode 100644 index 000000000..ae82c7d3c --- /dev/null +++ b/tests/run/titer8.nim @@ -0,0 +1,33 @@ +discard """ + output: '''tada +ta da''' +""" +# Test first class iterator: + +import strutils + +iterator tokenize2(s: string, seps: set[char] = Whitespace): tuple[ + token: string, isSep: bool] {.closure.} = + var i = 0 + while i < s.len: + var j = i + if s[j] in seps: + while j < s.len and s[j] in seps: inc(j) + if j > i: + yield (substr(s, i, j-1), true) + else: + while j < s.len and s[j] notin seps: inc(j) + if j > i: + yield (substr(s, i, j-1), false) + i = j + +for word, isSep in tokenize2("ta da", whiteSpace): + if not isSep: + stdout.write(word) +echo "" + +proc inProc() = + for word, isSep in tokenize2("ta da", whiteSpace): + stdout.write(word) + +inProc() diff --git a/todo.txt b/todo.txt index e6a73a913..fdaa317ee 100755 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,8 @@ version 0.9.2 ============= -- implement the effect system -- implement for loop transformation for first class iterators +- test&finish first class iterators +- fix closure bug finally - overloading based on ASTs: 'constraint' should not be in PType but for the parameter *symbol* @@ -11,8 +11,9 @@ version 0.9.2 - ``hoist`` pragma for loop hoisting: can be easily done with AST overloading + global -- implement the compiler as a service +- improve the compiler as a service - ``=`` should be overloadable; requires specialization for ``=`` +- implement constructors and non-nil types - make 'bind' default for templates and introduce 'mixin'; special rule for ``[]=`` - implicit deref for parameter matching; overloading based on 'var T' @@ -45,6 +46,7 @@ version 0.9.XX echo a echo b) +- implement read/write tracking in the effect system - implement the "snoopResult" pragma; no, make a strutils with string append semantics instead ... - implement "closure tuple consists of a single 'ref'" optimization diff --git a/web/index.txt b/web/index.txt index 25853867a..b81ecae0d 100755 --- a/web/index.txt +++ b/web/index.txt @@ -103,11 +103,9 @@ Roadmap to 1.0 Version 0.9.2 * overloading based on ASTs (like already possible for term rewriting macros) * better interaction between macros, templates and overloading - * the effect system will be extended * the symbol binding rules for generics and templates may change again Version 0.9.x - * first class iterators * message passing performance will be greatly improved * the syntactic distinction between statements and expressions will be removed |