From e0fc07df89a2bc9a9472b08c4003be7b2ff6498b Mon Sep 17 00:00:00 2001 From: Erik O'Leary Date: Tue, 31 Dec 2013 19:37:20 -0600 Subject: [PATCH 01/19] Added docgen documentation --- doc/docgen.txt | 182 +++++++++++++++++++++ doc/docgen_samples/sample.html | 282 ++++++++++++++++++++++++++++++++ doc/docgen_samples/sample2.html | 282 ++++++++++++++++++++++++++++++++ 3 files changed, 746 insertions(+) create mode 100644 doc/docgen.txt create mode 100644 doc/docgen_samples/sample.html create mode 100644 doc/docgen_samples/sample2.html diff --git a/doc/docgen.txt b/doc/docgen.txt new file mode 100644 index 0000000000..998cde8391 --- /dev/null +++ b/doc/docgen.txt @@ -0,0 +1,182 @@ +=================================== + Nimrod DocGen Tools Guide +=================================== + +:Author: Erik O'Leary +:Version: |nimrodversion| + +.. contents:: + + +Introduction +============ + +This document describes the documentation generation tools built into the *Nimrod compiler*, +which can generate HTML and JSON output from input .nim files and projects, as well as HTML +and LaTex from input RST (reStructuredText) files. The output documentation will include +module dependencies (``import``), any top-level documentation comments (##), and exported +symbols (*), including procedures, types, and variables. + + +Documentation Comments +---------------------- +Any comments which are preceeded by a double-hash (##), are interpreted as documentation. +Comments are parsed as RST +(see `reference `_), providing +Nimrod module authors the ability to easily generate richly formatted documentation with only +their well-documented code. + +Example: + +.. code-block:: nimrod + type TPerson* = object + ## This type contains a description of a person + name: string + age: int + +Outputs:: + TPerson* = object + name: string + age: int + +This type contains a description of a person + +Field documentation comments can be added to fields like so: + +.. code-block:: nimrod + var numValues: int ## \ + ## `numValues` stores the number of values + +Note that without the `*` following the name of the type, the documentation for this type +would not be generated. Documentation will only be generated for *exported* types/procedures/etc. + + +Nimrod file input +----------------- + +The following examples will generate documentation for the below contrived +*Nimrod* module, aptly named 'sample.nim' + +sample.nim: + +.. code-block:: nimrod + ## This module is a sample. + + import strutils + + proc helloWorld*(times: int) = + ## Takes an integer and outputs + ## as many "hello world!"s + + for i in 0 .. times-1: + echo "hello world!" + + helloWorld(5) + + +Document Types +============== + + +HTML +---- +Generation of HTML documents is done via both the ``doc`` and ``doc2`` commands. These +command take either a single .nim file, outputting a single .html file with the same base filename, +or multiple .nim files, outputting multiple .html files and, optionally, +an index file. + +The ``doc`` command:: + nimrod doc sample + +Partial Output:: + ... + proc helloWorld*(times: int) + ... + +Output can be viewed in full here `sample.html `_. The next command, +called ``doc2``, is very similar to the ``doc`` command, but will be run after the +compiler performs semantic checking on the input nimrod module(s), which allows it to process macros. + +The ``doc2`` command:: + nimrod doc2 sample + +Partial Output:: + ... + proc helloWorld(times: int) {.raises: [], tags: [].} + ... + +The full output can be seen here `sample2.html `_. As you can see, the tool has +extracted additional information provided to it by the compiler beyond what the ``doc`` +command provides, such as pragmas attached implicitly by the compiler. This type of information +is not available from looking at the AST (Abstract Syntax Tree) prior to semantic checking, +as the ``doc`` command does. + + +JSON +---- +Generation of JSON documents is done via the ``jsondoc`` command. This command takes +in a .nim file, and outputs a .json file with the same base filename. Note that this +tool is built off of the ``doc`` command, and therefore is performed before semantic +checking. + +The ``jsondoc`` command:: + nimrod jsondoc sample + +Output:: + [ + { + "comment": "This module is a sample." + }, + { + "name": "helloWorld", + "type": "skProc", + "description": "Takes an integer and outputs as many "hello world!"s", + "code": "proc helloWorld*(times: int)" + } + ] + + +Related Options +=============== + +``--project`` switch +:: + nimrod doc2 --project sample + +This will recursively generate documentation of all nimrod modules imported into the input module, +including system modules. Be careful with this command, as it may end up sprinkling html files all +over your filesystem! + + +``--index`` switch +:: + nimrod doc2 --index:on sample + +This will generate an index of all the exported symbols in the input Nimrod module, and put it into +a neighboring file with the extension of `.idx`. + + +Other Input Formats +=================== + +The *Nimrod compiler* also has support for RST (reStructuredText) files with the ``rst2html`` and ``rst2tex`` +commands. Documents like this one are initially written in a dialect of RST which adds support for nimrod +sourcecode highlighting with the ``.. code-block:: nimrod`` prefix. ``code-block`` also supports highlighting +of C++ and some other c-like languages. + +Usage:: + nimrod rst2html docgen.txt + +Output:: + You're reading it! + +The input can be viewed here `docgen.txt `_. The ``rst2tex`` command is invoked identically to +``rst2html``, but outputs a .tex file instead of .html. + + +Additional Resources +========= + +`Nimrod Compiler User Guide `_ + +`RST Quick Reference `_ diff --git a/doc/docgen_samples/sample.html b/doc/docgen_samples/sample.html new file mode 100644 index 0000000000..4efa40905e --- /dev/null +++ b/doc/docgen_samples/sample.html @@ -0,0 +1,282 @@ + + + + + + +Module sample + + + + +
+

Module sample

+ +
+This module is a sample. + +
+

Procs

+
+
proc helloWorld*(times: int)
+
+Takes an integer and outputs as many "hello world!"s +
+ +
+ +
+ +Generated: 2013-12-21 11:45:42 UTC +
+ + diff --git a/doc/docgen_samples/sample2.html b/doc/docgen_samples/sample2.html new file mode 100644 index 0000000000..3ff5e27e54 --- /dev/null +++ b/doc/docgen_samples/sample2.html @@ -0,0 +1,282 @@ + + + + + + +Module sample + + + + +
+

Module sample

+ +
+This module is a sample. + +
+

Procs

+
+
proc helloWorld(times: int) {.raises: [], tags: [].}
+
+Takes an integer and outputs as many "hello world!"s +
+ +
+ +
+ +Generated: 2013-12-21 11:45:52 UTC +
+ + From 0b6c9d7d75db7b3a9b93c465ba8a99c559fc305c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 13:18:27 +0100 Subject: [PATCH 02/19] Fixes two minor typos. --- doc/docgen.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/docgen.txt b/doc/docgen.txt index 998cde8391..e287e48251 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -13,14 +13,14 @@ Introduction This document describes the documentation generation tools built into the *Nimrod compiler*, which can generate HTML and JSON output from input .nim files and projects, as well as HTML -and LaTex from input RST (reStructuredText) files. The output documentation will include +and LaTeX from input RST (reStructuredText) files. The output documentation will include module dependencies (``import``), any top-level documentation comments (##), and exported symbols (*), including procedures, types, and variables. Documentation Comments ---------------------- -Any comments which are preceeded by a double-hash (##), are interpreted as documentation. +Any comments which are preceded by a double-hash (##), are interpreted as documentation. Comments are parsed as RST (see `reference `_), providing Nimrod module authors the ability to easily generate richly formatted documentation with only From fd6bb131b87c740e843f1615af85d217018fe9c7 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 13:22:44 +0100 Subject: [PATCH 03/19] Reformats text to fit width of 80 columns. --- doc/docgen.txt | 90 ++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/doc/docgen.txt b/doc/docgen.txt index e287e48251..0e897dcd59 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -11,20 +11,22 @@ Introduction ============ -This document describes the documentation generation tools built into the *Nimrod compiler*, -which can generate HTML and JSON output from input .nim files and projects, as well as HTML -and LaTeX from input RST (reStructuredText) files. The output documentation will include -module dependencies (``import``), any top-level documentation comments (##), and exported -symbols (*), including procedures, types, and variables. +This document describes the documentation generation tools built into the +*Nimrod compiler*, which can generate HTML and JSON output from input .nim +files and projects, as well as HTML and LaTeX from input RST (reStructuredText) +files. The output documentation will include module dependencies (``import``), +any top-level documentation comments (##), and exported symbols (*), including +procedures, types, and variables. Documentation Comments ---------------------- -Any comments which are preceded by a double-hash (##), are interpreted as documentation. -Comments are parsed as RST -(see `reference `_), providing -Nimrod module authors the ability to easily generate richly formatted documentation with only -their well-documented code. + +Any comments which are preceded by a double-hash (##), are interpreted as +documentation. Comments are parsed as RST (see `reference +`_), providing +Nimrod module authors the ability to easily generate richly formatted +documentation with only their well-documented code. Example: @@ -47,8 +49,9 @@ Field documentation comments can be added to fields like so: var numValues: int ## \ ## `numValues` stores the number of values -Note that without the `*` following the name of the type, the documentation for this type -would not be generated. Documentation will only be generated for *exported* types/procedures/etc. +Note that without the `*` following the name of the type, the documentation for +this type would not be generated. Documentation will only be generated for +*exported* types/procedures/etc. Nimrod file input @@ -80,10 +83,11 @@ Document Types HTML ---- -Generation of HTML documents is done via both the ``doc`` and ``doc2`` commands. These -command take either a single .nim file, outputting a single .html file with the same base filename, -or multiple .nim files, outputting multiple .html files and, optionally, -an index file. + +Generation of HTML documents is done via both the ``doc`` and ``doc2`` +commands. These command take either a single .nim file, outputting a single +.html file with the same base filename, or multiple .nim files, outputting +multiple .html files and, optionally, an index file. The ``doc`` command:: nimrod doc sample @@ -93,9 +97,10 @@ Partial Output:: proc helloWorld*(times: int) ... -Output can be viewed in full here `sample.html `_. The next command, -called ``doc2``, is very similar to the ``doc`` command, but will be run after the -compiler performs semantic checking on the input nimrod module(s), which allows it to process macros. +Output can be viewed in full here `sample.html `_. +The next command, called ``doc2``, is very similar to the ``doc`` command, but +will be run after the compiler performs semantic checking on the input nimrod +module(s), which allows it to process macros. The ``doc2`` command:: nimrod doc2 sample @@ -105,19 +110,21 @@ Partial Output:: proc helloWorld(times: int) {.raises: [], tags: [].} ... -The full output can be seen here `sample2.html `_. As you can see, the tool has -extracted additional information provided to it by the compiler beyond what the ``doc`` -command provides, such as pragmas attached implicitly by the compiler. This type of information -is not available from looking at the AST (Abstract Syntax Tree) prior to semantic checking, -as the ``doc`` command does. +The full output can be seen here `sample2.html `_. +As you can see, the tool has extracted additional information provided to it by +the compiler beyond what the ``doc`` command provides, such as pragmas attached +implicitly by the compiler. This type of information is not available from +looking at the AST (Abstract Syntax Tree) prior to semantic checking, as the +``doc`` command does. JSON ---- -Generation of JSON documents is done via the ``jsondoc`` command. This command takes -in a .nim file, and outputs a .json file with the same base filename. Note that this -tool is built off of the ``doc`` command, and therefore is performed before semantic -checking. + +Generation of JSON documents is done via the ``jsondoc`` command. This command +takes in a .nim file, and outputs a .json file with the same base filename. +Note that this tool is built off of the ``doc`` command, and therefore is +performed before semantic checking. The ``jsondoc`` command:: nimrod jsondoc sample @@ -143,26 +150,27 @@ Related Options :: nimrod doc2 --project sample -This will recursively generate documentation of all nimrod modules imported into the input module, -including system modules. Be careful with this command, as it may end up sprinkling html files all -over your filesystem! +This will recursively generate documentation of all nimrod modules imported +into the input module, including system modules. Be careful with this command, +as it may end up sprinkling html files all over your filesystem! ``--index`` switch :: nimrod doc2 --index:on sample -This will generate an index of all the exported symbols in the input Nimrod module, and put it into -a neighboring file with the extension of `.idx`. +This will generate an index of all the exported symbols in the input Nimrod +module, and put it into a neighboring file with the extension of `.idx`. Other Input Formats =================== -The *Nimrod compiler* also has support for RST (reStructuredText) files with the ``rst2html`` and ``rst2tex`` -commands. Documents like this one are initially written in a dialect of RST which adds support for nimrod -sourcecode highlighting with the ``.. code-block:: nimrod`` prefix. ``code-block`` also supports highlighting -of C++ and some other c-like languages. +The *Nimrod compiler* also has support for RST (reStructuredText) files with +the ``rst2html`` and ``rst2tex`` commands. Documents like this one are +initially written in a dialect of RST which adds support for nimrod sourcecode +highlighting with the ``.. code-block:: nimrod`` prefix. ``code-block`` also +supports highlighting of C++ and some other c-like languages. Usage:: nimrod rst2html docgen.txt @@ -170,8 +178,9 @@ Usage:: Output:: You're reading it! -The input can be viewed here `docgen.txt `_. The ``rst2tex`` command is invoked identically to -``rst2html``, but outputs a .tex file instead of .html. +The input can be viewed here `docgen.txt `_. The ``rst2tex`` +command is invoked identically to ``rst2html``, but outputs a .tex file instead +of .html. Additional Resources @@ -179,4 +188,5 @@ Additional Resources `Nimrod Compiler User Guide `_ -`RST Quick Reference `_ +`RST Quick Reference +`_ From 5844697aa6875b03fb4b7df4ccbcb824a3c1e1c1 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 13:31:21 +0100 Subject: [PATCH 04/19] Adds docgen to list of documentation to build. --- web/nimrod.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/nimrod.ini b/web/nimrod.ini index f10a4b2f21..b3e8594459 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson.""" [Documentation] doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters;trmacros" -doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools" +doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen" pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" From 91ae5a3585d93c94d1dba45c9c607496945bce0e Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 13:40:11 +0100 Subject: [PATCH 05/19] Adds some cross references to docgen manual. --- doc/docgen.txt | 12 ++++++------ doc/nimrodc.txt | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/docgen.txt b/doc/docgen.txt index 0e897dcd59..2ef06e578f 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -11,12 +11,12 @@ Introduction ============ -This document describes the documentation generation tools built into the -*Nimrod compiler*, which can generate HTML and JSON output from input .nim -files and projects, as well as HTML and LaTeX from input RST (reStructuredText) -files. The output documentation will include module dependencies (``import``), -any top-level documentation comments (##), and exported symbols (*), including -procedures, types, and variables. +This document describes the `documentation generation tools`:idx: built into +the `Nimrod compiler `_, which can generate HTML and JSON output +from input .nim files and projects, as well as HTML and LaTeX from input RST +(reStructuredText) files. The output documentation will include module +dependencies (``import``), any top-level documentation comments (##), and +exported symbols (*), including procedures, types, and variables. Documentation Comments diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index f5fbf3ebbe..52e0a6eaf4 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -538,6 +538,13 @@ on Linux:: nimrod c --dynlibOverride:lua --passL:liblua.lib program.nim +Nimrod documentation tools +========================== + +Nimrod provides the `doc`:idx: and `doc2`:idx: commands to generate HTML +documentation from ``.nim`` source files. Only exported symbols will appear in +the output. For more details `see the docgen documentation `_. + Nimrod idetools integration =========================== From 2ed7849921f0413c175094ff15b1cf71d931ed1c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Mon, 6 Jan 2014 14:55:03 +0100 Subject: [PATCH 06/19] Transforms docgen sample to be generated from source. --- doc/docgen.txt | 4 +- doc/docgen_sample.nim | 12 ++ doc/docgen_samples/sample.html | 282 -------------------------------- doc/docgen_samples/sample2.html | 282 -------------------------------- tools/nimweb.nim | 14 ++ 5 files changed, 28 insertions(+), 566 deletions(-) create mode 100644 doc/docgen_sample.nim delete mode 100644 doc/docgen_samples/sample.html delete mode 100644 doc/docgen_samples/sample2.html diff --git a/doc/docgen.txt b/doc/docgen.txt index 2ef06e578f..acd09f2eb1 100644 --- a/doc/docgen.txt +++ b/doc/docgen.txt @@ -97,7 +97,7 @@ Partial Output:: proc helloWorld*(times: int) ... -Output can be viewed in full here `sample.html `_. +Output can be viewed in full here: `docgen_sample.html `_. The next command, called ``doc2``, is very similar to the ``doc`` command, but will be run after the compiler performs semantic checking on the input nimrod module(s), which allows it to process macros. @@ -110,7 +110,7 @@ Partial Output:: proc helloWorld(times: int) {.raises: [], tags: [].} ... -The full output can be seen here `sample2.html `_. +The full output can be seen here: `docgen_sample2.html `_. As you can see, the tool has extracted additional information provided to it by the compiler beyond what the ``doc`` command provides, such as pragmas attached implicitly by the compiler. This type of information is not available from diff --git a/doc/docgen_sample.nim b/doc/docgen_sample.nim new file mode 100644 index 0000000000..8759931873 --- /dev/null +++ b/doc/docgen_sample.nim @@ -0,0 +1,12 @@ +## This module is a sample. + +import strutils + +proc helloWorld*(times: int) = + ## Takes an integer and outputs + ## as many "hello world!"s + + for i in 0 .. times-1: + echo "hello world!" + +helloWorld(5) diff --git a/doc/docgen_samples/sample.html b/doc/docgen_samples/sample.html deleted file mode 100644 index 4efa40905e..0000000000 --- a/doc/docgen_samples/sample.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - -Module sample - - - - -
-

Module sample

- -
-This module is a sample. - -
-

Procs

-
-
proc helloWorld*(times: int)
-
-Takes an integer and outputs as many "hello world!"s -
- -
- -
- -Generated: 2013-12-21 11:45:42 UTC -
- - diff --git a/doc/docgen_samples/sample2.html b/doc/docgen_samples/sample2.html deleted file mode 100644 index 3ff5e27e54..0000000000 --- a/doc/docgen_samples/sample2.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - -Module sample - - - - -
-

Module sample

- -
-This module is a sample. - -
-

Procs

-
-
proc helloWorld(times: int) {.raises: [], tags: [].}
-
-Takes an integer and outputs as many "hello world!"s -
- -
- -
- -Generated: 2013-12-21 11:45:52 UTC -
- - diff --git a/tools/nimweb.nim b/tools/nimweb.nim index c5d510eacc..0e519b4b82 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -204,6 +204,18 @@ proc Exec(cmd: string) = echo(cmd) if os.execShellCmd(cmd) != 0: quit("external program failed") +proc buildDocSamples(c: var TConfigData, destPath: string) = + ## Special case documentation sample proc. + ## + ## The docgen sample needs to be generated twice with different commands, so + ## it didn't make much sense to integrate into the existing generic + ## documentation builders. + const src = "doc"/"docgen_sample.nim" + Exec("nimrod doc $# -o:$# $#" % + [c.nimrodArgs, destPath / "docgen_sample.html", src]) + Exec("nimrod doc2 $# -o:$# $#" % + [c.nimrodArgs, destPath / "docgen_sample2.html", src]) + proc buildDoc(c: var TConfigData, destPath: string) = # call nim for the documentation: for d in items(c.doc): @@ -352,7 +364,9 @@ proc main(c: var TConfigData) = copyDir("web/assets", "web/upload/assets") buildNewsRss(c, "web/upload") buildAddDoc(c, "web/upload") + buildDocSamples(c, "web/upload") buildDoc(c, "web/upload") + buildDocSamples(c, "doc") buildDoc(c, "doc") buildPdfDoc(c, "doc") From 37229df7fc044fe108d2f4d88f127141cabeb6a6 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 22 Jan 2014 17:32:38 +0100 Subject: [PATCH 07/19] next steps for closure iterators --- compiler/ast.nim | 1 + compiler/lambdalifting.nim | 136 +++++++++++------- compiler/lexer.nim | 2 +- compiler/parser.nim | 4 +- compiler/renderer.nim | 5 + compiler/semstmts.nim | 11 +- compiler/transf.nim | 3 + doc/manual.txt | 25 ++-- koch.nim | 9 +- lib/core/macros.nim | 4 +- tests/{block => controlflow}/tblock1.nim | 0 tests/{ifstmt => controlflow}/tnestif.nim | 0 tests/iter/tanoniter1.nim | 32 +++++ tests/iter/titer2.nim | 2 +- tests/{static => metatype}/tstaticparams.nim | 0 tests/{object => objects}/tobjconstr.nim | 0 tests/{object => objects}/tobjconstr2.nim | 0 tests/{object => objects}/tobjcov.nim | 0 tests/{object => objects}/tobject.nim | 0 tests/{object => objects}/tobject2.nim | 0 tests/{object => objects}/tobject3.nim | 0 tests/{operator => objects}/tofopr.nim | 0 tests/{object => objects}/toop.nim | 0 tests/{object => objects}/toop1.nim | 0 tests/{operator => parser}/toprprec.nim | 0 tests/{operator => parser}/tprecedence.nim | 0 .../tdrdobbs_examples.nim | 0 todo.txt | 1 + 28 files changed, 164 insertions(+), 71 deletions(-) rename tests/{block => controlflow}/tblock1.nim (100%) rename tests/{ifstmt => controlflow}/tnestif.nim (100%) create mode 100644 tests/iter/tanoniter1.nim rename tests/{static => metatype}/tstaticparams.nim (100%) rename tests/{object => objects}/tobjconstr.nim (100%) rename tests/{object => objects}/tobjconstr2.nim (100%) rename tests/{object => objects}/tobjcov.nim (100%) rename tests/{object => objects}/tobject.nim (100%) rename tests/{object => objects}/tobject2.nim (100%) rename tests/{object => objects}/tobject3.nim (100%) rename tests/{operator => objects}/tofopr.nim (100%) rename tests/{object => objects}/toop.nim (100%) rename tests/{object => objects}/toop1.nim (100%) rename tests/{operator => parser}/toprprec.nim (100%) rename tests/{operator => parser}/tprecedence.nim (100%) rename tests/{important => showoff}/tdrdobbs_examples.nim (100%) diff --git a/compiler/ast.nim b/compiler/ast.nim index 0e351a31ac..82cc2c96ff 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -287,6 +287,7 @@ const sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't # require RC ops + sfClosureCreated* = sfDiscriminant # for transf-lambdalifting interaction const # getting ready for the future expr/stmt merge diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ed92fefb40..2189a1d677 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -116,9 +116,9 @@ type TDep = tuple[e: PEnv, field: PSym] TEnv {.final.} = object of TObject attachedNode: PNode - closure: PSym # if != nil it is a used environment + closure: PSym # if != nil it is a used environment capturedVars: seq[PSym] # captured variables in this environment - deps: seq[TDep] # dependencies + deps: seq[TDep] # dependencies up: PEnv tup: PType @@ -149,7 +149,19 @@ proc newInnerContext(fn: PSym): PInnerContext = new(result) result.fn = fn initIdNodeTable(result.localsToAccess) - + +proc getStateType(iter: PSym): PType = + var n = newNodeI(nkRange, iter.info) + addSon(n, newIntNode(nkIntLit, -1)) + addSon(n, newIntNode(nkIntLit, 0)) + result = newType(tyRange, iter) + result.n = n + rawAddSon(result, getSysType(tyInt)) + +proc createStateField(iter: PSym): PSym = + result = newSym(skField, getIdent(":state"), iter, iter.info) + result.typ = getStateType(iter) + proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv = new(result) result.deps = @[] @@ -170,6 +182,9 @@ proc addField(tup: PType, s: PSym) = proc addCapturedVar(e: PEnv, v: PSym) = for x in e.capturedVars: if x == v: return + # XXX meh, just add the state field for every closure for now, it's too + # hard to figure out if it comes from a closure iterator: + if e.tup.len == 0: addField(e.tup, createStateField(v.owner)) e.capturedVars.add(v) addField(e.tup, v) @@ -189,6 +204,7 @@ proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node var deref = newNodeI(nkHiddenDeref, info) deref.typ = a.typ.sons[0] + assert deref.typ.kind == tyTuple let field = getSymFromList(deref.typ.n, b.name) assert field != nil, b.name.s addSon(deref, a) @@ -220,18 +236,30 @@ proc getHiddenParam(routine: PSym): PSym = assert hidden.kind == nkSym result = hidden.sym +proc getEnvParam(routine: PSym): PSym = + let params = routine.ast.sons[paramsPos] + let hidden = lastSon(params) + if hidden.kind == nkSym and hidden.sym.name.s == paramName: + result = hidden.sym + proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = s.kind in {skProc, skMethod, skConverter} and + result = (s.kind in {skProc, skMethod, skConverter} or + s.kind == skIterator and s.typ.callConv == ccClosure) and s.skipGenericOwner == outerProc #s.typ.callConv == ccClosure proc addClosureParam(i: PInnerContext, e: PEnv) = - var cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info) - incl(cp.flags, sfFromGeneric) - cp.typ = newType(tyRef, i.fn) - rawAddSon(cp.typ, e.tup) + var cp = getEnvParam(i.fn) + if cp == nil: + cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info) + incl(cp.flags, sfFromGeneric) + cp.typ = newType(tyRef, i.fn) + rawAddSon(cp.typ, e.tup) + addHiddenParam(i.fn, cp) + else: + e.tup = cp.typ.sons[0] + assert e.tup.kind == tyTuple i.closureParam = cp - addHiddenParam(i.fn, i.closureParam) #echo "closure param added for ", i.fn.name.s, " ", i.fn.id proc dummyClosureParam(o: POuterContext, i: PInnerContext) = @@ -344,6 +372,7 @@ proc transformOuterConv(n: PNode): PNode = proc makeClosure(prc, env: PSym, info: TLineInfo): PNode = result = newNodeIT(nkClosure, info, prc.typ) result.add(newSymNode(prc)) + if prc.kind == skIterator: incl(prc.flags, sfClosureCreated) if env == nil: result.add(newNodeIT(nkNilLit, info, getSysType(tyNil))) else: @@ -366,10 +395,10 @@ proc transformInnerProc(o: POuterContext, i: PInnerContext, n: PNode): PNode = else: # captured symbol? result = idNodeTableGet(i.localsToAccess, n.sym) - of nkLambdaKinds: - result = transformInnerProc(o, i, n.sons[namePos]) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef: + of nkLambdaKinds, nkIteratorDef: + if n.typ != nil: + result = transformInnerProc(o, i, n.sons[namePos]) + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: # don't recurse here: discard else: @@ -400,8 +429,9 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = if inner.closureParam != nil: let ti = transformInnerProc(o, inner, body) if ti != nil: n.sym.ast.sons[bodyPos] = ti - of nkLambdaKinds: - searchForInnerProcs(o, n.sons[namePos]) + of nkLambdaKinds, nkIteratorDef: + if n.typ != nil: + searchForInnerProcs(o, n.sons[namePos]) of nkWhileStmt, nkForStmt, nkParForStmt, nkBlockStmt: # some nodes open a new scope, so they are candidates for the insertion # of closure creation; however for simplicity we merge closures between @@ -437,8 +467,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = searchForInnerProcs(o, it.sons[L-1]) else: internalError(it.info, "transformOuter") - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: # don't recurse here: # XXX recurse here and setup 'up' pointers discard @@ -535,10 +564,10 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = assert result != nil, "cannot find: " & local.name.s # else it is captured by copy and this means that 'outer' should continue # to access the local as a local. - of nkLambdaKinds: - result = transformOuterProc(o, n.sons[namePos]) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef: + of nkLambdaKinds, nkIteratorDef: + if n.typ != nil: + result = transformOuterProc(o, n.sons[namePos]) + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: # don't recurse here: discard of nkHiddenStdConv, nkHiddenSubConv, nkConv: @@ -607,11 +636,14 @@ type tup: PType proc newIterResult(iter: PSym): PSym = - result = iter.ast.sons[resultPos].sym - when false: + if resultPos < iter.ast.len: + result = iter.ast.sons[resultPos].sym + else: + # XXX a bit hacky: result = newSym(skResult, getIdent":result", iter, iter.info) result.typ = iter.typ.sons[0] incl(result.flags, sfUsed) + iter.ast.add newSymNode(result) proc interestingIterVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags @@ -663,36 +695,40 @@ proc transfIterBody(c: var TIterContext, n: PNode): PNode = let x = transfIterBody(c, n.sons[i]) if x != nil: n.sons[i] = x -proc getStateType(iter: PSym): PType = - var n = newNodeI(nkRange, iter.info) - addSon(n, newIntNode(nkIntLit, -1)) - addSon(n, newIntNode(nkIntLit, 0)) - result = newType(tyRange, iter) - result.n = n - rawAddSon(result, getSysType(tyInt)) - -proc liftIterator*(iter: PSym, body: PNode): PNode = - var c: TIterContext +proc initIterContext(c: var TIterContext, iter: PSym) = c.iter = iter c.capturedVars = initIntSet() - c.tup = newType(tyTuple, iter) - c.tup.n = newNodeI(nkRecList, iter.info) + var cp = getEnvParam(iter) + if cp == nil: + c.tup = newType(tyTuple, iter) + c.tup.n = newNodeI(nkRecList, iter.info) + + cp = newSym(skParam, getIdent(paramName), iter, iter.info) + incl(cp.flags, sfFromGeneric) + cp.typ = newType(tyRef, iter) + rawAddSon(cp.typ, c.tup) + addHiddenParam(iter, cp) + + c.state = createStateField(iter) + addField(c.tup, c.state) + else: + c.tup = cp.typ.sons[0] + assert c.tup.kind == tyTuple + if c.tup.len > 0: + c.state = c.tup.n[0].sym + else: + c.state = createStateField(iter) + addField(c.tup, c.state) - var cp = newSym(skParam, getIdent(paramName), iter, iter.info) - incl(cp.flags, sfFromGeneric) - cp.typ = newType(tyRef, iter) - rawAddSon(cp.typ, c.tup) c.closureParam = cp - addHiddenParam(iter, cp) - - c.state = newSym(skField, getIdent(":state"), iter, iter.info) - c.state.typ = getStateType(iter) - addField(c.tup, c.state) - if iter.typ.sons[0] != nil: c.resultSym = newIterResult(iter) - iter.ast.add(newSymNode(c.resultSym)) + #iter.ast.add(newSymNode(c.resultSym)) + +proc liftIterator*(iter: PSym, body: PNode): PNode = + var c: TIterContext + initIterContext c, iter result = newNodeI(nkStmtList, iter.info) var gs = newNodeI(nkGotoState, iter.info) @@ -716,12 +752,14 @@ proc liftIterator*(iter: PSym, body: PNode): PNode = proc liftIterSym*(n: PNode): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) - result = newNodeIT(nkStmtListExpr, n.info, n.typ) let iter = n.sym assert iter.kind == skIterator + if sfClosureCreated in iter.flags: return n + + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + var env = copySym(getHiddenParam(iter)) env.kind = skLet - var v = newNodeI(nkVarSection, n.info) addVar(v, newSymNode(env)) result.add(v) @@ -766,7 +804,7 @@ proc liftForLoop*(body: PNode): PNode = # static binding? var env: PSym if call[0].kind == nkSym and call[0].sym.kind == skIterator: - # createClose() + # createClosure() let iter = call[0].sym assert iter.kind == skIterator env = copySym(getHiddenParam(iter)) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 97414ddb73..0e7df13cd7 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.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. diff --git a/compiler/parser.nim b/compiler/parser.nim index d255949a4b..2b845f3da1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.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. @@ -31,7 +31,7 @@ type TParser*{.final.} = object # a TParser object represents a module that # is being parsed currInd: int # current indentation - firstTok: bool + firstTok, strongSpaces: bool lex*: TLexer # the lexer that is used for parsing tok*: TToken # the current token inPragma: int diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b5e3c0e74d..6588caa049 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1251,6 +1251,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(META|") gsub(g, n.sons[0]) put(g, tkParRi, ")") + of nkGotoState, nkState: + var c: TContext + initContext c + putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto" + gsons(g, n, c) else: #nkNone, nkExplicitTypeListCall: internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index fc1706200e..a26d898361 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -868,6 +868,8 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode = return semStmt(c, x) proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = + # XXX semProcAux should be good enough for this now, we will eventually + # remove semLambda result = semProcAnnotation(c, n) if result != nil: return result result = n @@ -949,11 +951,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, checkSonsLen(n, bodyPos + 1) var s: PSym var typeIsDetermined = false + var isAnon = false if n[namePos].kind != nkSym: assert phase == stepRegisterSymbol if n[namePos].kind == nkEmpty: s = newSym(kind, idAnon, getCurrOwner(), n.info) + isAnon = true else: s = semIdentDef(c, n.sons[0], kind) n.sons[namePos] = newSymNode(s) @@ -996,11 +1000,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, rawAddSon(s.typ, nil) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) - if s.kind == skIterator: s.typ.flags.incl(tfIterator) + if s.kind == skIterator: + s.typ.flags.incl(tfIterator) var proto = searchForProc(c, s.scope, s) if proto == nil: - s.typ.callConv = lastOptionEntry(c).defaultCC + if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure + else: s.typ.callConv = lastOptionEntry(c).defaultCC # add it here, so that recursive procs are possible: if sfGenSym in s.flags: discard elif kind in OverloadableSyms: @@ -1078,6 +1084,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner() if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) + if isAnon: result.typ = s.typ proc determineType(c: PContext, s: PSym) = if s.typ != nil: return diff --git a/compiler/transf.nim b/compiler/transf.nim index f22433972c..973e8848a5 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -636,6 +636,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = s.ast.sons[bodyPos] = n.sons[bodyPos] #n.sons[bodyPos] = liftLambdas(s, n) #if n.kind == nkMethodDef: methodDef(s, false) + if n.kind == nkIteratorDef and n.typ != nil: + return liftIterSym(n.sons[namePos]).PTransNode result = PTransNode(n) of nkMacroDef: # XXX no proper closure support yet: @@ -708,6 +710,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = # XXX comment handling really sucks: if importantComments(): PNode(result).comment = n.comment + of nkClosure: return PTransNode(n) else: result = transformSons(c, n) var cnst = getConstExpr(c.module, PNode(result)) diff --git a/doc/manual.txt b/doc/manual.txt index c903732331..9f84bc951f 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2261,8 +2261,8 @@ from different modules having the same name. using sdl.SetTimer Note that ``using`` only *adds* to the current context, it doesn't remove or -replace, **neither** does it create a new scope. What this means is that if you -apply this to multiple variables the compiler will find conflicts in what +replace, **neither** does it create a new scope. What this means is that if one +applies this to multiple variables the compiler will find conflicts in what variable to use: .. code-block:: nimrod @@ -2275,7 +2275,7 @@ variable to use: echo b When the compiler reaches the second ``add`` call, both ``a`` and ``b`` could -be used with the proc, so you get ``Error: expression '(a|b)' has no type (or +be used with the proc, so one gets ``Error: expression '(a|b)' has no type (or is ambiguous)``. To solve this you would need to nest ``using`` with a ``block`` statement so as to control the reach of the ``using`` statement. @@ -2368,8 +2368,8 @@ The `addr`:idx: operator returns the address of an l-value. If the type of the location is ``T``, the `addr` operator result is of the type ``ptr T``. An address is always an untraced reference. Taking the address of an object that resides on the stack is **unsafe**, as the pointer may live longer than the -object on the stack and can thus reference a non-existing object. You can get -the address of variables, but you can't use it on variables declared through +object on the stack and can thus reference a non-existing object. One can get +the address of variables, but one can't use it on variables declared through ``let`` statements: .. code-block:: nimrod @@ -2764,7 +2764,7 @@ First class iterators There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators. An `inline iterator`:idx: is an iterator that's always inlined by the compiler leading to zero overhead for the abstraction, but may result in a heavy -increasee in code size. Inline iterators are second class +increase in code size. Inline iterators are second class citizens; one cannot pass them around like first class procs. In contrast to that, a `closure iterator`:idx: can be passed around: @@ -2835,7 +2835,10 @@ a `collaborative tasking`:idx: system: 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. +iterator that has already finished its work. + +One always has to + Type sections @@ -2923,9 +2926,9 @@ in an implicit try block: finally: close(f) ... -The ``except`` statement has a limitation in this form: you can't specify the -type of the exception, you have to catch everything. Also, if you want to use -both ``finally`` and ``except`` you need to reverse the usual sequence of the +The ``except`` statement has a limitation in this form: one can't specify the +type of the exception, one has to catch everything. Also, if one wants to use +both ``finally`` and ``except`` one needs to reverse the usual sequence of the statements. Example: .. code-block:: nimrod @@ -3353,7 +3356,7 @@ currently matched type. These instances can act both as variables of the type, when used in contexts, where a value is expected, and as the type itself, when used in a contexts, where a type is expected. -Please note that the ``is`` operator allows you to easily verify the precise +Please note that the ``is`` operator allows one to easily verify the precise type signatures of the required operations, but since type inference and default parameters are still applied in the provided block, it's also possible to encode usage protocols that doesn't reveal implementation details. diff --git a/koch.nim b/koch.nim index 1814a0efc3..35a86a597c 100644 --- a/koch.nim +++ b/koch.nim @@ -42,7 +42,7 @@ Possible Commands: csource [options] builds the C sources for installation zip builds the installation ZIP package inno [options] builds the Inno Setup installer (for Windows) - tests run the testsuite + tests [options] run the testsuite update updates nimrod to the latest version from github (compile koch with -d:withUpdate to enable) temp options creates a temporary compiler for testing @@ -260,11 +260,14 @@ when defined(withUpdate): # -------------- tests -------------------------------------------------------- +template `|`(a, b): expr = (if a.len > 0: a else: b) + proc tests(args: string) = # we compile the tester with taintMode:on to have a basic # taint mode test :-) - exec("nimrod cc --taintMode:on tests/testament/tester") - exec(getCurrentDir() / "tests/testament/tester".exe & " all") + exec "nimrod cc --taintMode:on tests/testament/tester" + exec quoteShell(getCurrentDir() / "tests/testament/tester".exe) & " " & + (args|"all") proc temp(args: string) = var output = "compiler" / "nimrod".exe diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 7cb084653e..3b36e31e0b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -390,7 +390,7 @@ proc treeRepr*(n: PNimrodNode): string {.compileTime.} = res.add(($n.kind).substr(3)) case n.kind - of nnkEmpty: nil # same as nil node in this representation + of nnkEmpty: discard # same as nil node in this representation of nnkNilLit: res.add(" nil") of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) @@ -415,7 +415,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} = add(result, "(") case n.kind - of nnkEmpty: nil # same as nil node in this representation + of nnkEmpty: discard # same as nil node in this representation of nnkNilLit: add(result, "nil") of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) diff --git a/tests/block/tblock1.nim b/tests/controlflow/tblock1.nim similarity index 100% rename from tests/block/tblock1.nim rename to tests/controlflow/tblock1.nim diff --git a/tests/ifstmt/tnestif.nim b/tests/controlflow/tnestif.nim similarity index 100% rename from tests/ifstmt/tnestif.nim rename to tests/controlflow/tnestif.nim diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim new file mode 100644 index 0000000000..9db5ab8eca --- /dev/null +++ b/tests/iter/tanoniter1.nim @@ -0,0 +1,32 @@ +discard """ + output: '''1 +2 +3 +4 +1 +2''' +""" + +proc factory(a, b: int): iterator (): int = + iterator foo(): int = + var x = a + while x <= b: + yield x + inc x + return foo + +proc factory2(a, b: int): iterator (): int = + return iterator (): int = + var x = a + while x <= b: + yield x + inc x + +let foo = factory 1, 4 + +for f in foo(): + echo f + +let foo2 = factory2 1,2 + +for f in foo2(): echo f diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim index dab2713e8f..f8967109e2 100644 --- a/tests/iter/titer2.nim +++ b/tests/iter/titer2.nim @@ -1,6 +1,6 @@ discard """ output: '''true''' - cmd: "nimrod cc --gc:none --hints:on $# $#" + cmd: "nimrod cc --gc:none --hints:on --warnings:off $# $#" """ import hashes diff --git a/tests/static/tstaticparams.nim b/tests/metatype/tstaticparams.nim similarity index 100% rename from tests/static/tstaticparams.nim rename to tests/metatype/tstaticparams.nim diff --git a/tests/object/tobjconstr.nim b/tests/objects/tobjconstr.nim similarity index 100% rename from tests/object/tobjconstr.nim rename to tests/objects/tobjconstr.nim diff --git a/tests/object/tobjconstr2.nim b/tests/objects/tobjconstr2.nim similarity index 100% rename from tests/object/tobjconstr2.nim rename to tests/objects/tobjconstr2.nim diff --git a/tests/object/tobjcov.nim b/tests/objects/tobjcov.nim similarity index 100% rename from tests/object/tobjcov.nim rename to tests/objects/tobjcov.nim diff --git a/tests/object/tobject.nim b/tests/objects/tobject.nim similarity index 100% rename from tests/object/tobject.nim rename to tests/objects/tobject.nim diff --git a/tests/object/tobject2.nim b/tests/objects/tobject2.nim similarity index 100% rename from tests/object/tobject2.nim rename to tests/objects/tobject2.nim diff --git a/tests/object/tobject3.nim b/tests/objects/tobject3.nim similarity index 100% rename from tests/object/tobject3.nim rename to tests/objects/tobject3.nim diff --git a/tests/operator/tofopr.nim b/tests/objects/tofopr.nim similarity index 100% rename from tests/operator/tofopr.nim rename to tests/objects/tofopr.nim diff --git a/tests/object/toop.nim b/tests/objects/toop.nim similarity index 100% rename from tests/object/toop.nim rename to tests/objects/toop.nim diff --git a/tests/object/toop1.nim b/tests/objects/toop1.nim similarity index 100% rename from tests/object/toop1.nim rename to tests/objects/toop1.nim diff --git a/tests/operator/toprprec.nim b/tests/parser/toprprec.nim similarity index 100% rename from tests/operator/toprprec.nim rename to tests/parser/toprprec.nim diff --git a/tests/operator/tprecedence.nim b/tests/parser/tprecedence.nim similarity index 100% rename from tests/operator/tprecedence.nim rename to tests/parser/tprecedence.nim diff --git a/tests/important/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim similarity index 100% rename from tests/important/tdrdobbs_examples.nim rename to tests/showoff/tdrdobbs_examples.nim diff --git a/todo.txt b/todo.txt index 15ce93df75..943d982c4d 100644 --- a/todo.txt +++ b/todo.txt @@ -3,6 +3,7 @@ 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 - document new templating symbol binding rules - make '--implicitStatic:on' the default From 3f87326247b142df4eff99a92c6529b33bb79b81 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 01:41:26 +0100 Subject: [PATCH 08/19] closure iterators almost work --- compiler/lambdalifting.nim | 370 ++++++++++++++++++------------------- compiler/transf.nim | 12 +- 2 files changed, 190 insertions(+), 192 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 2189a1d677..352b406936 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -130,25 +130,16 @@ type TOuterContext {.final.} = object fn: PSym # may also be a module! currentEnv: PEnv + isIter: bool # first class iterator? capturedVars, processed: TIntSet localsToEnv: TIdTable # PSym->PEnv mapping localsToAccess: TIdNodeTable lambdasToEnv: TIdTable # PSym->PEnv mapping up: POuterContext -proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = - new(result) - result.fn = fn - result.capturedVars = initIntSet() - result.processed = initIntSet() - initIdNodeTable(result.localsToAccess) - initIdTable(result.localsToEnv) - initIdTable(result.lambdasToEnv) - -proc newInnerContext(fn: PSym): PInnerContext = - new(result) - result.fn = fn - initIdNodeTable(result.localsToAccess) + closureParam, state, resultSym: PSym # only if isIter + tup: PType # only if isIter + proc getStateType(iter: PSym): PType = var n = newNodeI(nkRange, iter.info) @@ -162,6 +153,92 @@ proc createStateField(iter: PSym): PSym = result = newSym(skField, getIdent(":state"), iter, iter.info) result.typ = getStateType(iter) +proc newIterResult(iter: PSym): PSym = + if resultPos < iter.ast.len: + result = iter.ast.sons[resultPos].sym + else: + # XXX a bit hacky: + result = newSym(skResult, getIdent":result", iter, iter.info) + result.typ = iter.typ.sons[0] + incl(result.flags, sfUsed) + iter.ast.add newSymNode(result) + +proc addHiddenParam(routine: PSym, param: PSym) = + var params = routine.ast.sons[paramsPos] + # -1 is correct here as param.position is 0 based but we have at position 0 + # some nkEffect node: + param.position = params.len-1 + addSon(params, newSymNode(param)) + 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 getEnvParam(routine: PSym): PSym = + let params = routine.ast.sons[paramsPos] + let hidden = lastSon(params) + if hidden.kind == nkSym and hidden.sym.name.s == paramName: + result = hidden.sym + +proc addField(tup: PType, s: PSym) = + var field = newSym(skField, s.name, s.owner, s.info) + let t = skipIntLit(s.typ) + field.typ = t + field.position = sonsLen(tup) + addSon(tup.n, newSymNode(field)) + rawAddSon(tup, t) + +proc initIterContext(c: POuterContext, iter: PSym) = + c.fn = iter + c.capturedVars = initIntSet() + + var cp = getEnvParam(iter) + if cp == nil: + c.tup = newType(tyTuple, iter) + c.tup.n = newNodeI(nkRecList, iter.info) + + cp = newSym(skParam, getIdent(paramName), iter, iter.info) + incl(cp.flags, sfFromGeneric) + cp.typ = newType(tyRef, iter) + rawAddSon(cp.typ, c.tup) + addHiddenParam(iter, cp) + + c.state = createStateField(iter) + addField(c.tup, c.state) + else: + c.tup = cp.typ.sons[0] + assert c.tup.kind == tyTuple + if c.tup.len > 0: + c.state = c.tup.n[0].sym + else: + c.state = createStateField(iter) + addField(c.tup, c.state) + + c.closureParam = cp + if iter.typ.sons[0] != nil: + c.resultSym = newIterResult(iter) + #iter.ast.add(newSymNode(c.resultSym)) + +proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = + new(result) + result.fn = fn + result.capturedVars = initIntSet() + result.processed = initIntSet() + initIdNodeTable(result.localsToAccess) + initIdTable(result.localsToEnv) + initIdTable(result.lambdasToEnv) + result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure + if result.isIter: initIterContext(result, fn) + +proc newInnerContext(fn: PSym): PInnerContext = + new(result) + result.fn = fn + initIdNodeTable(result.localsToAccess) + proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv = new(result) result.deps = @[] @@ -171,14 +248,6 @@ proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv = result.up = up result.attachedNode = n -proc addField(tup: PType, s: PSym) = - var field = newSym(skField, s.name, s.owner, s.info) - let t = skipIntLit(s.typ) - field.typ = t - field.position = sonsLen(tup) - addSon(tup.n, newSymNode(field)) - rawAddSon(tup, t) - proc addCapturedVar(e: PEnv, v: PSym) = for x in e.capturedVars: if x == v: return @@ -221,27 +290,6 @@ proc newCall(a, b: PSym): PNode = result.add newSymNode(a) result.add newSymNode(b) -proc addHiddenParam(routine: PSym, param: PSym) = - var params = routine.ast.sons[paramsPos] - # -1 is correct here as param.position is 0 based but we have at position 0 - # some nkEffect node: - param.position = params.len-1 - addSon(params, newSymNode(param)) - 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 getEnvParam(routine: PSym): PSym = - let params = routine.ast.sons[paramsPos] - let hidden = lastSon(params) - if hidden.kind == nkSym and hidden.sym.name.s == paramName: - result = hidden.sym - proc isInnerProc(s, outerProc: PSym): bool {.inline.} = result = (s.kind in {skProc, skMethod, skConverter} or s.kind == skIterator and s.typ.callConv == ccClosure) and @@ -334,7 +382,9 @@ proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) = var s = n.sym if interestingVar(s) and i.fn.id != s.owner.id: captureVar(o, i, s, n.info) - elif isInnerProc(s, o.fn) and tfCapturesEnv in s.typ.flags and s != i.fn: + elif s.kind in {skProc, skMethod, skConverter} and + s.skipGenericOwner == o.fn and + tfCapturesEnv in s.typ.flags and s != i.fn: # call to some other inner proc; we need to track the dependencies for # this: let env = PEnv(idTableGet(o.lambdasToEnv, i.fn)) @@ -342,7 +392,7 @@ proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) = if o.currentEnv != env: discard addDep(o.currentEnv, env, i.fn) internalError(n.info, "too complex environment handling required") - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkClosure: discard else: for k in countup(0, sonsLen(n) - 1): gatherVars(o, i, n.sons[k]) @@ -398,7 +448,8 @@ proc transformInnerProc(o: POuterContext, i: PInnerContext, n: PNode): PNode = of nkLambdaKinds, nkIteratorDef: if n.typ != nil: result = transformInnerProc(o, i, n.sons[namePos]) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkClosure: # don't recurse here: discard else: @@ -467,7 +518,8 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = searchForInnerProcs(o, it.sons[L-1]) else: internalError(it.info, "transformOuter") - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkClosure: # don't recurse here: # XXX recurse here and setup 'up' pointers discard @@ -526,12 +578,61 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = result.add(newAsgnStmt(indirectAccess(env, field, env.info), newSymNode(getClosureVar(o, e)), env.info)) +proc interestingIterVar(s: PSym): bool {.inline.} = + result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags + +proc transformOuterProc(o: POuterContext, n: PNode): PNode + +proc transformYield(c: POuterContext, n: PNode): PNode = + inc c.state.typ.n.sons[1].intVal + let stateNo = c.state.typ.n.sons[1].intVal + + var stateAsgnStmt = newNodeI(nkAsgn, n.info) + stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) + 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 = transformOuterProc(c, n.sons[0]) + addSon(a, newSymNode(c.resultSym)) + 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(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) + + result = newNodeI(nkStmtList, n.info) + result.add(stateAsgnStmt) + result.add(retStmt) + result.add(stateLabelStmt) + +proc transformReturn(c: POuterContext, n: PNode): PNode = + result = newNodeI(nkStmtList, n.info) + var stateAsgnStmt = newNodeI(nkAsgn, n.info) + stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) + stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) + result.add(stateAsgnStmt) + result.add(n) + +proc outerProcSons(o: POuterContext, n: PNode) = + for i in countup(0, sonsLen(n) - 1): + let x = transformOuterProc(o, n.sons[i]) + if x != nil: n.sons[i] = x + proc transformOuterProc(o: POuterContext, n: PNode): PNode = if n == nil: return nil case n.kind of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: discard of nkSym: var local = n.sym + + if o.isIter and interestingIterVar(local) and o.fn.id == local.owner.id: + if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local) + return indirectAccess(newSymNode(o.closureParam), local, n.info) + var closure = PEnv(idTableGet(o.lambdasToEnv, local)) if closure != nil: # we need to replace the lambda with '(lambda, env)': @@ -567,17 +668,44 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = of nkLambdaKinds, nkIteratorDef: if n.typ != nil: result = transformOuterProc(o, n.sons[namePos]) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkClosure: # don't recurse here: discard of nkHiddenStdConv, nkHiddenSubConv, nkConv: let x = transformOuterProc(o, n.sons[1]) if x != nil: n.sons[1] = x result = transformOuterConv(n) + of nkYieldStmt: + if o.isIter: result = transformYield(o, n) + else: outerProcSons(o, n) + of nkReturnStmt: + if o.isIter: result = transformReturn(o, n) + else: outerProcSons(o, n) else: - for i in countup(0, sonsLen(n) - 1): - let x = transformOuterProc(o, n.sons[i]) - if x != nil: n.sons[i] = x + outerProcSons(o, n) + +proc liftIterator(c: POuterContext, body: PNode): PNode = + let iter = c.fn + result = newNodeI(nkStmtList, iter.info) + var gs = newNodeI(nkGotoState, iter.info) + gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info)) + result.add(gs) + var state0 = newNodeI(nkState, iter.info) + state0.add(newIntNode(nkIntLit, 0)) + result.add(state0) + + let newBody = transformOuterProc(c, body) + if newBody != nil: + result.add(newBody) + else: + result.add(body) + + 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 liftLambdas*(fn: PSym, body: PNode): PNode = # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs @@ -601,8 +729,11 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode = if resultPos < sonsLen(ast) and ast.sons[resultPos].kind == nkSym: idTablePut(o.localsToEnv, ast.sons[resultPos].sym, o.currentEnv) searchForInnerProcs(o, body) - discard transformOuterProc(o, body) - result = ex + if o.isIter: + result = liftIterator(o, ex) + else: + discard transformOuterProc(o, body) + result = ex proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = if body.kind == nkEmpty or gCmd == cmdCompileToJS: @@ -617,144 +748,11 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = # ------------------- iterator transformation -------------------------------- -discard """ - iterator chain[S, T](a, b: *S->T, args: *S): T = - for x in a(args): yield x - for x in b(args): yield x - - let c = chain(f, g) - for x in c: echo x - - # translated to: - let c = chain( (f, newClosure(f)), (g, newClosure(g)), newClosure(chain)) -""" - -type - TIterContext {.final, pure.} = object - iter, closureParam, state, resultSym: PSym - capturedVars: TIntSet - tup: PType - -proc newIterResult(iter: PSym): PSym = - if resultPos < iter.ast.len: - result = iter.ast.sons[resultPos].sym - else: - # XXX a bit hacky: - result = newSym(skResult, getIdent":result", iter, iter.info) - result.typ = iter.typ.sons[0] - incl(result.flags, sfUsed) - iter.ast.add newSymNode(result) - -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 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: discard - of nkYieldStmt: - inc c.state.typ.n.sons[1].intVal - let stateNo = c.state.typ.n.sons[1].intVal - - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) - 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, if retVal.isNil: n.sons[0] else: retVal) - retStmt.add(a) - else: - retStmt.add(emptyNode) - - var stateLabelStmt = newNodeI(nkState, n.info) - stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) - - result = newNodeI(nkStmtList, n.info) - result.add(stateAsgnStmt) - result.add(retStmt) - result.add(stateLabelStmt) - of nkReturnStmt: - result = newNodeI(nkStmtList, n.info) - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt))) - result.add(stateAsgnStmt) - result.add(n) - else: - for i in countup(0, sonsLen(n)-1): - let x = transfIterBody(c, n.sons[i]) - if x != nil: n.sons[i] = x - -proc initIterContext(c: var TIterContext, iter: PSym) = - c.iter = iter - c.capturedVars = initIntSet() - - var cp = getEnvParam(iter) - if cp == nil: - c.tup = newType(tyTuple, iter) - c.tup.n = newNodeI(nkRecList, iter.info) - - cp = newSym(skParam, getIdent(paramName), iter, iter.info) - incl(cp.flags, sfFromGeneric) - cp.typ = newType(tyRef, iter) - rawAddSon(cp.typ, c.tup) - addHiddenParam(iter, cp) - - c.state = createStateField(iter) - addField(c.tup, c.state) - else: - c.tup = cp.typ.sons[0] - assert c.tup.kind == tyTuple - if c.tup.len > 0: - c.state = c.tup.n[0].sym - else: - c.state = createStateField(iter) - addField(c.tup, c.state) - - c.closureParam = cp - if iter.typ.sons[0] != nil: - c.resultSym = newIterResult(iter) - #iter.ast.add(newSymNode(c.resultSym)) - -proc liftIterator*(iter: PSym, body: PNode): PNode = - var c: TIterContext - initIterContext c, iter - - result = newNodeI(nkStmtList, iter.info) - var gs = newNodeI(nkGotoState, iter.info) - gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info)) - result.add(gs) - var state0 = newNodeI(nkState, iter.info) - state0.add(newIntNode(nkIntLit, 0)) - result.add(state0) - - let newBody = transfIterBody(c, body) - if newBody != nil: - result.add(newBody) - else: - result.add(body) - - 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 liftIterSym*(n: PNode): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) let iter = n.sym assert iter.kind == skIterator - if sfClosureCreated in iter.flags: return n + #if sfClosureCreated in iter.flags: return n result = newNodeIT(nkStmtListExpr, n.info, n.typ) diff --git a/compiler/transf.nim b/compiler/transf.nim index 973e8848a5..cda6110056 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -113,8 +113,8 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = result[1] = ri proc transformSymAux(c: PTransf, n: PNode): PNode = - if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: - return liftIterSym(n) + #if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: + # return liftIterSym(n) var b: PNode var tc = c.transCon if sfBorrow in n.sym.flags: @@ -636,8 +636,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = s.ast.sons[bodyPos] = n.sons[bodyPos] #n.sons[bodyPos] = liftLambdas(s, n) #if n.kind == nkMethodDef: methodDef(s, false) - if n.kind == nkIteratorDef and n.typ != nil: - return liftIterSym(n.sons[namePos]).PTransNode + #if n.kind == nkIteratorDef and n.typ != nil: + # return liftIterSym(n.sons[namePos]).PTransNode result = PTransNode(n) of nkMacroDef: # XXX no proper closure support yet: @@ -741,8 +741,8 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = var c = openTransf(module, "") result = processTransf(c, n, prc) result = liftLambdas(prc, result) - if prc.kind == skIterator and prc.typ.callConv == ccClosure: - result = lambdalifting.liftIterator(prc, result) + #if prc.kind == skIterator and prc.typ.callConv == ccClosure: + # result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) From d01ff8994b8805a6558c87f1f58c789e42c93694 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 08:47:22 +0100 Subject: [PATCH 09/19] closure iterators work --- compiler/lambdalifting.nim | 54 +++++++++++++++++++-------- doc/manual.txt | 16 +++++++- tests/closure/tnamedparamanonproc.nim | 4 +- todo.txt | 35 +++++++++-------- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 352b406936..a9c4a87578 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -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) diff --git a/doc/manual.txt b/doc/manual.txt index 9f84bc951f..faf62dcee5 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/tests/closure/tnamedparamanonproc.nim b/tests/closure/tnamedparamanonproc.nim index 272b84e914..94e32894f3 100644 --- a/tests/closure/tnamedparamanonproc.nim +++ b/tests/closure/tnamedparamanonproc.nim @@ -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 diff --git a/todo.txt b/todo.txt index 943d982c4d..d0aec9c8cc 100644 --- a/todo.txt +++ b/todo.txt @@ -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 From 1465a7b08096d825f77a85e5594266d78ff022a9 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 12:22:34 +0100 Subject: [PATCH 10/19] small code cleanups --- compiler/ast.nim | 1 - compiler/lambdalifting.nim | 4 +--- web/news.txt | 2 ++ 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 82cc2c96ff..0e351a31ac 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -287,7 +287,6 @@ const sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't # require RC ops - sfClosureCreated* = sfDiscriminant # for transf-lambdalifting interaction const # getting ready for the future expr/stmt merge diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index a9c4a87578..01eb494636 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -422,7 +422,6 @@ proc transformOuterConv(n: PNode): PNode = proc makeClosure(prc, env: PSym, info: TLineInfo): PNode = result = newNodeIT(nkClosure, info, prc.typ) result.add(newSymNode(prc)) - if prc.kind == skIterator: incl(prc.flags, sfClosureCreated) if env == nil: result.add(newNodeIT(nkNilLit, info, getSysType(tyNil))) else: @@ -776,8 +775,7 @@ proc liftIterSym*(n: PNode): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) let iter = n.sym assert iter.kind == skIterator - #if sfClosureCreated in iter.flags: return n - + result = newNodeIT(nkStmtListExpr, n.info, n.typ) var env = copySym(getHiddenParam(iter)) diff --git a/web/news.txt b/web/news.txt index 01b6d18b9a..a045eb8803 100644 --- a/web/news.txt +++ b/web/news.txt @@ -74,6 +74,8 @@ Language Additions evaluable at compile-time. - Support for user-defined type classes has been added. - The *command syntax* is supported in a lot more contexts. +- Anonymous iterators are now supported and iterators can capture variables + of an outer proc. Tools improvements From 3e5cb6644f4df51c3f3131108fc7836c431b53c2 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 17:19:12 +0100 Subject: [PATCH 11/19] typeClasses now documented in grammar.txt --- compiler/parser.nim | 4 ++++ doc/grammar.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/compiler/parser.nim b/compiler/parser.nim index 2b845f3da1..1084c3b4a3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1048,6 +1048,7 @@ proc parseTypeDesc(p: var TParser): PNode = proc parseTypeDefAux(p: var TParser): PNode = #| typeDefAux = simpleExpr + #| | 'generic' typeClass result = simpleExpr(p, pmTypeDef) proc makeCall(n: PNode): PNode = @@ -1672,6 +1673,9 @@ proc parseTypeClassParam(p: var TParser): PNode = result = p.parseSymbol proc parseTypeClass(p: var TParser): PNode = + #| typeClassParam = ('var')? symbol + #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? + #| &IND{>} stmt result = newNodeP(nkTypeClassTy, p) getTok(p) var args = newNode(nkArgList) diff --git a/doc/grammar.txt b/doc/grammar.txt index b002747fa8..54c2217d87 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -97,6 +97,7 @@ primary = typeKeyw typeDescK / 'bind' primary typeDesc = simpleExpr typeDefAux = simpleExpr + | 'generic' typeClass macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt | IND{=} 'elif' expr ':' stmt | IND{=} 'except' exprList ':' stmt @@ -163,6 +164,9 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? objectPart = IND{>} objectPart^+IND{=} DED / objectWhen / objectCase / 'nil' / declColonEquals object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart +typeClassParam = ('var')? symbol +typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? + &IND{>} stmt distinct = 'distinct' optInd typeDesc typeDef = identWithPragma genericParamList? '=' optInd typeDefAux indAndComment? From f332ac1d375cc4e17003ae630a0c6ca28c3f06b7 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 19:23:41 +0100 Subject: [PATCH 12/19] renderer knows about nkStaticTy and nkTypeClassTy --- compiler/renderer.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6588caa049..1afb5961ec 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -426,6 +426,8 @@ proc lsub(n: PNode): int = of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("Distinct") + of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) + + len("static[]") of nkTypeDef: result = lsons(n) + 3 of nkOfInherit: result = lsub(n.sons[0]) + len("of_") of nkProcTy: result = lsons(n) + len("proc_") @@ -701,6 +703,19 @@ proc gproc(g: var TSrcGen, n: PNode) = gcoms(g) dedent(g) +proc gTypeClassTy(g: var TSrcGen, n: PNode) = + var c: TContext + initContext(c) + putWithSpace(g, tkGeneric, "generic") + gsons(g, n[0], c) # arglist + gsub(g, n[1]) # pragmas + gsub(g, n[2]) # of + gcoms(g) + indentNL(g) + gcoms(g) + gstmts(g, n[3], c) + dedent(g) + proc gblock(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) @@ -1054,6 +1069,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) else: put(g, tkShared, "shared") + of nkStaticTy: + put(g, tkStatic, "static") + put(g, tkBracketLe, "[") + if n.len > 0: + gsub(g, n.sons[0]) + put(g, tkBracketRi, "]") of nkEnumTy: if sonsLen(n) > 0: putWithSpace(g, tkEnum, "enum") @@ -1256,6 +1277,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = initContext c putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto" gsons(g, n, c) + of nkTypeClassTy: + gTypeClassTy(g, n) else: #nkNone, nkExplicitTypeListCall: internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') From de538deb7af602130c805661b49d976ab9493613 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 23 Jan 2014 20:43:56 +0100 Subject: [PATCH 13/19] should compile on netbsd --- lib/pure/os.nim | 2 +- lib/pure/osproc.nim | 8 ++++---- lib/pure/sockets.nim | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 448ecc1e38..1f42d0d58e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1586,7 +1586,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} = # little heuristic that may work on other POSIX-like systems: result = string(getEnv("_")) if len(result) == 0: - result = string(ParamStr(0)) + result = string(paramStr(0)) # POSIX guaranties that this contains the executable # as it has been executed by the calling process if len(result) > 0 and result[0] != DirSep: # not an absolute path? diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6c43dc2d9e..676707abbc 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -660,8 +660,8 @@ elif not defined(useNimRtl): else: - Pid = fork() - if Pid < 0: osError(osLastError()) + pid = fork() + if pid < 0: osError(osLastError()) if pid == 0: ## child process: @@ -685,14 +685,14 @@ elif not defined(useNimRtl): if env == nil: discard execv(command, a) else: - discard execve(command, a, ToCStringArray(env)) + discard execve(command, a, toCStringArray(env)) else: var x = addCmdArgs(command, args) var a = toCStringArray(["sh", "-c"], [x]) if env == nil: discard execv("/bin/sh", a) else: - discard execve("/bin/sh", a, ToCStringArray(env)) + discard execve("/bin/sh", a, toCStringArray(env)) # too risky to raise an exception here: quit("execve call failed: " & $strerror(errno)) # Parent process. Copy process information. diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index b3cc38ee70..fd64031181 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -192,7 +192,7 @@ when defined(Posix): of AF_UNIX: result = posix.AF_UNIX of AF_INET: result = posix.AF_INET of AF_INET6: result = posix.AF_INET6 - else: nil + else: discard proc toInt(typ: TType): cint = case typ @@ -200,7 +200,7 @@ when defined(Posix): of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET of SOCK_RAW: result = posix.SOCK_RAW - else: nil + else: discard proc toInt(p: TProtocol): cint = case p @@ -210,7 +210,7 @@ when defined(Posix): of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 of IPPROTO_RAW: result = posix.IPPROTO_RAW of IPPROTO_ICMP: result = posix.IPPROTO_ICMP - else: nil + else: discard else: proc toInt(domain: TDomain): cint = From 869e7d93e07b77f245abb58874234f4cb17445de Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 14:20:40 +0100 Subject: [PATCH 14/19] nativeStackTrace might work again --- lib/system/excpt.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 1964e4d3dc..a3f6669d42 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -11,7 +11,7 @@ # use the heap (and nor exceptions) do not include the GC or memory allocator. var - errorMessageWriter*: (proc(msg: string): void {.tags: [FWriteIO].}) + errorMessageWriter*: (proc(msg: string) {.tags: [FWriteIO].}) ## Function that will be called ## instead of stdmsg.write when printing stacktrace. ## Unstable API. @@ -80,9 +80,9 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: type TDl_info {.importc: "Dl_info", header: "", final, pure.} = object - dli_fname: CString + dli_fname: cstring dli_fbase: pointer - dli_sname: CString + dli_sname: cstring dli_saddr: pointer proc backtrace(symbols: ptr pointer, size: int): int {. From c4cb795081db776fe7ea2b96840c22dafebc2214 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 14:21:27 +0100 Subject: [PATCH 15/19] updated asyncio and ftpclient modules --- lib/pure/asyncio.nim | 18 +++++++++--------- lib/pure/ftpclient.nim | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 3c2a5c17a4..f13cadaa2d 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -139,27 +139,27 @@ type proc newDelegate*(): PDelegate = ## Creates a new delegate. new(result) - result.handleRead = (proc (h: PObject) = nil) - result.handleWrite = (proc (h: PObject) = nil) - result.handleError = (proc (h: PObject) = nil) + result.handleRead = (proc (h: PObject) = discard) + result.handleWrite = (proc (h: PObject) = discard) + result.handleError = (proc (h: PObject) = discard) result.hasDataBuffered = (proc (h: PObject): bool = return false) - result.task = (proc (h: PObject) = nil) + result.task = (proc (h: PObject) = discard) result.mode = fmRead proc newAsyncSocket(): PAsyncSocket = new(result) result.info = SockIdle - result.handleRead = (proc (s: PAsyncSocket) = nil) + result.handleRead = (proc (s: PAsyncSocket) = discard) result.handleWrite = nil - result.handleConnect = (proc (s: PAsyncSocket) = nil) - result.handleAccept = (proc (s: PAsyncSocket) = nil) - result.handleTask = (proc (s: PAsyncSocket) = nil) + result.handleConnect = (proc (s: PAsyncSocket) = discard) + result.handleAccept = (proc (s: PAsyncSocket) = discard) + result.handleTask = (proc (s: PAsyncSocket) = discard) result.lineBuffer = "".TaintedString result.sendBuffer = "" -proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, +proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket = ## Initialises an AsyncSocket object. If a socket cannot be initialised diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index d9f9dfd3d1..f136e0016b 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -95,7 +95,7 @@ type EInvalidReply* = object of ESynch EFTP* = object of ESynch -proc FTPClient*(address: string, port = TPort(21), +proc ftpClient*(address: string, port = TPort(21), user, pass = ""): PFTPClient = ## Create a ``PFTPClient`` object. new(result) @@ -315,7 +315,7 @@ proc listDirs*(ftp: PFTPClient, dir: string = "", assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"] if not async: - while not ftp.job.prc(ftp, false): nil + while not ftp.job.prc(ftp, false): discard result = splitLines(ftp.job.lines) ftp.deleteJob() else: return @[] @@ -390,7 +390,7 @@ proc list*(ftp: PFTPClient, dir: string = "", async = false): string = assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"]) if not async: - while not ftp.job.prc(ftp, false): nil + while not ftp.job.prc(ftp, false): discard result = ftp.job.lines ftp.deleteJob() else: @@ -405,7 +405,7 @@ proc retrText*(ftp: PFTPClient, file: string, async = false): string = assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"] if not async: - while not ftp.job.prc(ftp, false): nil + while not ftp.job.prc(ftp, false): discard result = ftp.job.lines ftp.deleteJob() else: @@ -460,7 +460,7 @@ proc retrFile*(ftp: PFTPClient, file, dest: string, async = false) = ftp.job.filename = file.normalizePathSep if not async: - while not ftp.job.prc(ftp, false): nil + while not ftp.job.prc(ftp, false): discard ftp.deleteJob() proc doUpload(ftp: PFTPClient, async = false): bool = @@ -518,7 +518,7 @@ proc store*(ftp: PFTPClient, file, dest: string, async = false) = assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"] if not async: - while not ftp.job.prc(ftp, false): nil + while not ftp.job.prc(ftp, false): discard ftp.deleteJob() proc close*(ftp: PFTPClient) = @@ -554,10 +554,10 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) = ftp.handleEvent(ftp, r) -proc AsyncFTPClient*(address: string, port = TPort(21), +proc asyncFTPClient*(address: string, port = TPort(21), user, pass = "", handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} = - (proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = nil)): PAsyncFTPClient = + (proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient = ## Create a ``PAsyncFTPClient`` object. ## ## Use this if you want to use asyncio's dispatcher. @@ -604,7 +604,7 @@ when isMainModule: ftp.close() echo d.len else: assert(false) - var ftp = AsyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev) + var ftp = asyncFTPClient("picheta.me", user = "test", pass = "asf", handleEvent = hev) d.register(ftp) d.len.echo() @@ -618,7 +618,7 @@ when isMainModule: when isMainModule and false: - var ftp = FTPClient("picheta.me", user = "asdasd", pass = "asfwq") + var ftp = ftpClient("picheta.me", user = "asdasd", pass = "asfwq") ftp.connect() echo ftp.pwd() echo ftp.list() From 9af1ab5f4331b4857f6bcc57a8c7318faea36a7e Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 14:22:05 +0100 Subject: [PATCH 16/19] fixes a minor parser bug --- compiler/parser.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 1084c3b4a3..3765557b9d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1209,8 +1209,7 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = if p.tok.tokType == tkComment: skipComment(p, result) addSon(result, ast.emptyNode) - elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or - p.tok.tokType == tkEof: + elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p): # NL terminates: addSon(result, ast.emptyNode) else: From 9d4c4592fa49aa1b571a158025ae3a7155b2414b Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 14:22:38 +0100 Subject: [PATCH 17/19] more efficient calls for closure iterators --- compiler/ccgcalls.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 1c6eea6218..84c5bf4199 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -146,7 +146,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = proc addComma(r: PRope): PRope = result = if r == nil: r else: con(r, ~", ") - const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)" + const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)" + const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists var op: TLoc initLocExpr(p, ri.sons[0], op) var pl: PRope @@ -164,9 +165,10 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = if i < length - 1: app(pl, ~", ") template genCallPattern {.dirty.} = - lineF(p, cpsStmts, CallPattern & ";$n", op.r, pl, pl.addComma, rawProc) + lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc) let rawProc = getRawProcType(p, typ) + let callPattern = if tfIterator in typ.flags: PatIter else: PatProc if typ.sons[0] != nil: if isInvalidReturnType(typ.sons[0]): if sonsLen(ri) > 1: app(pl, ~", ") @@ -190,7 +192,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.t, OnUnknown) - list.r = ropef(CallPattern, op.r, pl, pl.addComma, rawProc) + list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc) genAssignment(p, d, list, {}) # no need for deep copying else: genCallPattern() From f16881ad162a12833aba7598d65684d9e63600c2 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 14:24:33 +0100 Subject: [PATCH 18/19] disable internalError so that nimbuild compiles again --- compiler/lambdalifting.nim | 2 ++ compiler/semdestruct.nim | 5 ++++- lib/system.nim | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 01eb494636..00fa045562 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -655,6 +655,8 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = # 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: + #if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) + # XXX why doesn't this work? let createdVar = generateIterClosureCreation(o, closure, closure.attachedNode) return makeClosure(local, createdVar, n.info) diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index 9dbbf29402..fb05826cb7 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -116,7 +116,10 @@ proc generateDestructor(c: PContext, t: PType): PNode = let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) if stmt != nil: addLine(stmt) else: - internalAssert false + # XXX just skip it for now so that the compiler doesn't crash, but + # please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is + # possible. Any thread example seems to trigger this. + discard # base classes' destructors will be automatically called by # semProcAux for both auto-generated and user-defined destructors diff --git a/lib/system.nim b/lib/system.nim index de23a71fa1..09e44a45a9 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1330,7 +1330,7 @@ iterator `||`*[S, T](a: S, b: T, annotation=""): T {. ## such isn't aware of the parallelism in your code! Be careful! Later ## versions of ``||`` will get proper support by Nimrod's code generator ## and GC. - nil + discard {.push stackTrace:off.} proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} = From 8a7f50889c18283670801f442d6778fdff362c70 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 25 Jan 2014 23:03:43 +0100 Subject: [PATCH 19/19] fixes #838 --- compiler/semtypinst.nim | 4 ++-- compiler/vm.nim | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index ac14179cd5..9c6421315b 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -116,8 +116,8 @@ proc hasGenericArguments*(n: PNode): bool = (n.sym.kind == skType and n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) else: - for s in n.sons: - if hasGenericArguments(s): return true + for i in 0..