closure iterators work

This commit is contained in:
Araq
2014-01-23 08:47:22 +01:00
parent 3f87326247
commit d01ff8994b
4 changed files with 76 additions and 33 deletions

View File

@@ -116,7 +116,7 @@ type
TDep = tuple[e: PEnv, field: PSym]
TEnv {.final.} = object of TObject
attachedNode: PNode
closure: PSym # if != nil it is a used environment
createdVar: PSym # if != nil it is a used environment
capturedVars: seq[PSym] # captured variables in this environment
deps: seq[TDep] # dependencies
up: PEnv
@@ -544,19 +544,20 @@ proc addVar*(father, v: PNode) =
addSon(vpart, ast.emptyNode)
addSon(father, vpart)
proc getClosureVar(o: POuterContext, e: PEnv): PSym =
if e.closure == nil:
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
incl(result.flags, sfShadowed)
result.typ = newType(tyRef, o.fn)
result.typ.rawAddSon(e.tup)
e.closure = result
proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym =
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
incl(result.flags, sfShadowed)
result.typ = newType(tyRef, o.fn)
result.typ.rawAddSon(e.tup)
proc getClosureVar(o: POuterContext; e: PEnv): PSym =
if e.createdVar == nil:
result = newClosureCreationVar(o, e)
e.createdVar = result
else:
result = e.closure
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
var env = getClosureVar(o, scope)
result = e.createdVar
proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
result = newNodeI(nkStmtList, env.info)
var v = newNodeI(nkVarSection, env.info)
addVar(v, newSymNode(env))
@@ -577,6 +578,21 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
# add ``env.up = env2``
result.add(newAsgnStmt(indirectAccess(env, field, env.info),
newSymNode(getClosureVar(o, e)), env.info))
proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
var env = getClosureVar(o, scope)
result = rawClosureCreation(o, scope, env)
proc generateIterClosureCreation(o: POuterContext; env: PEnv;
scope: PNode): PSym =
result = newClosureCreationVar(o, env)
let cc = rawClosureCreation(o, env, result)
var insertPoint = scope.sons[0]
if insertPoint.kind == nkEmpty: scope.sons[0] = cc
else:
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList
for x in cc: insertPoint.add(x)
if env.createdVar == nil: env.createdVar = result
proc interestingIterVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
@@ -635,8 +651,16 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
let a = closure.closure
# we need to replace the lambda with '(lambda, env)':
if local.kind == skIterator and local.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again:
let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode)
return makeClosure(local, createdVar, n.info)
let a = closure.createdVar
if a != nil:
return makeClosure(local, a, n.info)
else:
@@ -646,7 +670,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
if scope.sons[0].kind == nkEmpty:
# change the empty node to contain the closure construction:
scope.sons[0] = generateClosureCreation(o, closure)
let x = closure.closure
let x = closure.createdVar
assert x != nil
return makeClosure(local, x, n.info)

View File

@@ -2837,8 +2837,22 @@ The builtin ``system.finished`` can be used to determine if an iterator has
finished its operation; no exception is raised on an attempt to invoke an
iterator that has already finished its work.
One always has to
Closure iterators are *resumable functions* and so one has to provide the
arguments to every call. To get around this limitation one can capture
parameters of an outer factory proc:
.. code-block:: nimrod
proc mycount(a, b: int): iterator (): int =
return iterator (): int =
var x = a
while x <= b:
yield x
inc x
let foo = mycount 1, 4
for f in foo():
echo f
Type sections

View File

@@ -4,8 +4,8 @@ type
TButtonClicked = proc(button: PButton) {.nimcall.}
proc newButton*(onClick: TButtonClicked) =
nil
discard
proc main() =
newButton(onClick = proc(b: PButton) =
var requestomat = 12

View File

@@ -1,20 +1,8 @@
version 0.9.4
=============
- test&finish first class iterators:
* nested iterators
- implement strongSpaces:on
- ensure (ref T)(a, b) works as a type conversion and type constructor
- better debugging support for writes to locations
- document new templating symbol binding rules
- make '--implicitStatic:on' the default
- change comment handling in the AST
- special rule for ``[]=``
- ``=`` should be overloadable; requires specialization for ``=``; general
lift mechanism in the compiler is already implemented for 'fields'
- built-in 'getImpl'
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC
- fix eval in macros.nim
@@ -37,12 +25,29 @@ Bugs
version 0.9.x
=============
- macros as type pragmas
- ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC
- implement strongSpaces:on
- make '--implicitStatic:on' the default
- implicit deref for parameter matching
- special rule for ``[]=``
- ``=`` should be overloadable; requires specialization for ``=``; general
lift mechanism in the compiler is already implemented for 'fields'
- built-in 'getImpl'
- change comment handling in the AST; that's lots of work as c2nim and pas2nim
make use of the fast every node can have a comment!
version 0.9.X
=============
- macros as type pragmas
- lazy overloading resolution:
* special case ``tyStmt``
- FFI:
* test libffi on windows
* test: times.format with the FFI
- document NimMain and check whether it works for threading
- 'quote' without 'do' doesn't work: parser/grammar issue; could be supported