Updated the documentation, added pointer to hosted version

This commit is contained in:
Linus 2021-05-11 13:27:09 +02:00
parent 66e435aa92
commit 5a92ba298d
3 changed files with 88 additions and 138 deletions

View file

@ -550,54 +550,53 @@
<section title="Loop expansion">
The main chunk of a loop expands into something like the following:
A goof loop expands into something looking like this:
<verbatim>
(let* (&lt;outer-binding&gt; ...
final-function (lambda (&lt;final-binding&gt;) &lt;final-expr&gt;))
(let goof-loop ((&lt;accumulator&gt; &lt;accumulator-init&gt;) ... (&lt;loop-var&gt; &lt;loop-var-init&gt;) ...)
(if (or &lt;check&gt; ...)
<example>
(let* (OUTER-BINDING ...
final-function (lambda (FINAL-BINDING) FINAL-EXPR))
(let goof-loop ((ACCUMULATOR ACCUMULATOR-INIT) ... (LOOP-VAR LOOP-VAR-INIT) ...)
(if (or CHECK...)
(begin
&lt;for-clause-finalizer&gt; ...
(final-function (&lt;accumulator-finalizer&gt; &lt;accumulator&gt;) ...))
(let ((&lt;body-binding&gt; ... &lt;body-binding-expr&gt;) ...)
(let ((&lt;user-binding&gt; ... &lt;user-binding-expr&gt;) ...)
(match-let ((&lt;parenthesised-pattern&gt; &lt;match-expr&gt;))
(if (and &lt;when-expr&gt; ...)
FOR-CLAUSE-FINALIZER ...
(final-function (ACCUMULATOR-FINALIZER ACCUMULATOR) ...))
(let ((BODY-BINDING ... BODY-BINDING-EXPR) ...)
(let ((USER-BINDING ...USER-BINDING-EXPR) ...)
(match-let ((PARENTHESISED-PATTERN MATCH-EXPR))
(if (and WHEN-EXPR ...)
(cond
((or &lt;user-break&gt; ...)
&lt;for-clause-finalizer&gt; ...
(final-function (&lt;accumulator-finalizer&gt; &lt;accumulator&gt;) ...))
((or USER-BREAK ...)
FOR-CLAUSE-FINALIZER ...
(final-function (ACCUMULATOR-FINALIZER ACCUMULATOR) ...))
(else
&lt;loop-body&gt;
(goof-loop &lt;accumulate&gt; ... &lt;loop-var-next&gt; ...))
(goof-loop &lt;accumulator&gt; ... &lt;loop-var-next&gt; ...)))))))))
&lt;outer-binding&gt;: are provided by accumulators or for clauses for bindings that are not passed as an argument to the loop, for example a vector. The vector is bound here, and the index into the vector is the thing iterated over.
LOOP-BODY
(goof-loop ACCUMULATE ... LOOP-VAR-NEXT ...))
(goof-loop ACCUMULATOR ... LOOP-VAR-NEXT ...)))))))))
</example>
OUTER-BINDING: are provided by accumulators or for clauses for bindings that are not passed as an argument to the loop, for example a vector. The vector is bound here, and the index into the vector is the thing iterated over.
&lt;final-binding&gt; and &lt;final-expr&gt;: When the iteration ends, this function is called with the results of the :acc clauses. In the case of (:acc lst-acc (listing ...)), the name of the accumulator is never lst-acc in the loop body, but only in the &lt;final-expr&gt;. In case of (listing ...) the accumulated results are reversed before the final function.
FINAL-BINDING and FINAL-EXPR: When the iteration ends, this function is called with the results of the :acc clauses. In the case of (:acc lst-acc (listing ...)), the name of the accumulator is never lst-acc in the loop body, but only in the FINAL-EXPR. In case of (listing ...) the accumulated results are reversed before the final function.
&lt;accumulator&gt; and &lt;loop-variable&gt;: &lt;accumulator&gt; holds the current state of an accumulator clause. This is not necessarily the same binding as the user provided as the name, as described above. &lt;loop-var&gt; is the current state of a :for clause.
ACCUMULATOR and LOOP-VAR: ACCUMULATOR holds the current state of an accumulator clause. This is not necessarily the same binding as the user provided as the name, as described above. LOOP-VAR is the current state of a :for clause.
&lt;check&gt;: Checks for :for-clauses. In the case of (in-list ...) this would check for (not (pair? ...)).
CHECK: Checks for :for-clauses. In the case of (in-list ...) this would check for (not (pair? ...)).
&lt;for-clause-finalizer&gt;: some :for clauses need to be finalized. In the case of (in-file ...) the open file handle is closed at any point where the iteration stops.
FOR-CLAUSE-FINALIZER: some :for clauses need to be finalized. In the case of (in-file ...) the open file handle is closed at any point where the iteration stops.
&lt;accumulator-finalizer&gt;: &lt;accumulator-finalizer&gt; is any preprocessing done to &lt;accumulator&gt; before passing it on to the final-function. In the case of (listing ...) that would be (reverse ...).
ACCUMULATOR-FINALIZER: ACCUMULATOR-FINALIZER is any preprocessing done to ACCUMULATOR before passing it on to the final-function. In the case of (listing ...) that would be (reverse ...).
&lt;body-binding&gt; and &lt;body-binding-expr&gt;:&lt;body-binding&gt; are the names the user provided for the body bindings. In the case of (:for a (in-list '(1 2 3))) the body binding would be (a (car name-of-loop-variable)). The body binding may be an (ice-9 match) pattern. More on that below.
BODY-BINDING and BODY-BINDING-EXPR: BODY-BINDING are the names the user provided for the body bindings. In the case of (:for a (in-list '(1 2 3))) the body binding would be (a (car name-of-loop-variable)). The body binding may be an (ice-9 match) pattern. More on that below.
&lt;parenthesised-pattern&gt; and &lt;match-expr&gt;: If a &lt;user-binding&gt; is not an identifier, it is presumed to be a match-let pattern. The result is bound to a variable and matched against this match-let.
PARENTHESISED-PATTERN and MATCH-EXPR: If a USER-BINDING is not an identifier, it is presumed to be a match-let pattern. The result is bound to a variable and matched against this match-let.
&lt;when-expr&gt;: the user supplied :when or :unless guard expression.
WHEN-EXPR: the user supplied :when or :unless guard expression.
&lt;user-break&gt;: user-supplied :break guard.
USER-BREAK: user-supplied :break guard.
&lt;loop-body&gt;, &lt;accumulate&gt;, and &lt;loop-var-next&gt;: The user supplied body of the loop. If the loop is not named (i.e: in loops where the user controls the iteration) an expression for the next loop iteration is added to the body. &lt;accumulate&gt; is the expression the accumulator clause provided to accumulate a new value. For (:acc acc (listing elem)) that is (cons elem acc). &lt;loop-var-next&gt; is the expression evaluated to get the next iteration's loop variable. In the case of (in-list lst) that is (cdr lst). If a loop name is provided there is no implicit next loop.
LOOP-BODY, ACCUMULATE, and LOOP-VAR-NEXT: The user supplied body of the loop. If the loop is not named (i.e: in loops where the user controls the iteration) an expression for the next loop iteration is added to the body. ACCUMULATE is the expression the accumulator clause provided to accumulate a new value. For (:acc acc (listing elem)) that is (cons elem acc). LOOP-VAR-NEXT is the expression evaluated to get the next iteration's loop variable. In the case of (in-list lst) that is (cdr lst). If a loop name is provided there is no implicit next loop.
&lt;accumulator-init&gt; and &lt;loop-var-init&gt;: &lt;accumulator-init&gt; are ALL accumulator init values, including the ones in subloops. For (listing ...) that is the empty list. &lt;loop-var-init&gt; is the initial loop vars.
ACCUMULATOR-INIT and LOOP-VAR-INIT: ACCUMULATOR-INIT are ALL accumulator init values, including the ones in subloops. For (listing ...) that is the empty list. LOOP-VAR-INIT is the initial loop vars.
In case of subloops, those are placed instead of &lt;loop-body&gt;. They use the same final-function, and instead of quitting when any &lt;check&gt; triggers they go out to the outer loop.
</verbatim>
In case of subloops, those are placed instead of LOOP-BODY. They use the same final-function, and instead of quitting when any CHECK triggers they go out to the outer loop.
</section>
<section title="Porting">
The bulk of goof-loop is written in portable syntax-rules. That code can be found in `goof-impl.scm` and all files under the `goof` directory. The major non-portable part is the macro that is bound in every loop to the user-given name of the loop. In the guile implementation this is implemented in syntax-case, and should be portable to any r6rs scheme. The guile implementation does a non-hygienic comparison of the variables in the named update, so to not have to deal with unwanted shadowing: