diff options
-rw-r--r-- | cppawk-iter.1 | 1554 |
1 files changed, 1554 insertions, 0 deletions
diff --git a/cppawk-iter.1 b/cppawk-iter.1 new file mode 100644 index 0000000..6618a90 --- /dev/null +++ b/cppawk-iter.1 @@ -0,0 +1,1554 @@ +.\" cppawk: C preprocessor wrapper around awk +.\" Copyright 2022 Kaz Kylheku <kaz@kylheku.com> +.\" +.\" BSD-2 License +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright notice, +.\" this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright notice, +.\" this list of conditions and the following disclaimer in the documentation +.\" and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.de bk +.IP " " +.PP +.. +.TH CPPAWK-ITER 1 "18 April 2022" "cppawk Libraries" "Iteration" + +.SH NAME +iter \- powerful, user-extensible iteration language for Awk + +.SH SYNOPSIS + +.ft B + #include <iter.h> + + \fI// Simple, single-variable iteration\fP + + doarray (\fIkey\fP, \fIvalue\fP, \fIarr\fP) \fI// iterate over Awk assoc array\fP + \fIstatement\fP + + dostring (\fIidx\fP, \fIchr\fP, \fIstr\fP) \fI// iterate over string\fP + \fIstatement\fP + + dofields (\fIidx\fP, \fIval\fP) \fI// iterate over $1, $2, ..\fP + \fIstatement\fP + + \fI// Multi-clause parallel and nested iteration\fP + + loop (\fIclause1\fP, \fIclause2\fP, ...) \fI// Parallel iteration\fP + \fIstatement\fP + + loop_nest (\fIclause1\fP, \fI// Nested iteration\fP + \fIclause2\fP, + parallel (\fIclause3\fP, + \fIclause4\fP, ...), + ...) + \fIstatement\fP + + \fI// Clauses for loop/loop_iter\fP + + \fI// numeric stepping clauses\fP + range (\fIidx\fP, \fIfrom\fP, \fIto\fP) + range_step (\fIidx\fP, \fIfrom\fP, \fIto\fP, \fIstep\fP) + from (\fIidx\fP, \fIfrom\fP) + from_step (\fIidx\fP, \fIfrom\fP, \fIstep\fP) + + \fI// general stepping clause\fP + first_then (\fIvar\fP, \fIfirst\fP, \fIthen\fP) + + \fI// container traversal\fP + str (\fIidx\fP, \fIch\fP, \fIstr\fP) + list (\fIiter\fP, \fIvar\fP, \fIlist\fP) + fields (\fIvar\fP) + keys (\fIkey\fP, \fIarray\fP) + + \fI// collect items into lists\fP + collect (\fIvar\fP, \fIexpr\fP) + collect_plus (\fIvar\fP, \fIexpr\fP) + + \fI// calculating clauses\fP + summing (\fIvar\fP, \fIexpr\fP) + maximizing (\fIvar\fP, \fIexpr\fP) + minimizing (\fIvar\fP, \fIexpr\fP) + argmax (\fImaxvar\fP, \fIargvar\fP, \fIexpr\fP) + argmin (\fIminvar\fP, \fIargvar\fP, \fIexpr\fP) + + \fI// termination control clauses\fP + while (\fIexpr\fP) + until (\fIexpr\fP) + + \fI// parallel grouping combinator\fP + \fI// clause1, clause2, ... are parallel even in a loop_nest.\fP + parallel (\fIclause1\fP, \fIclause2\fP, ...) + + \fI// conditional combinator:\fP + \fI// dependent clause steps/tests only whenever test is true\fP + if (\fItest\fP, \fIclause\fP) + +.ft R + +.SH OVERVIEW +.bk +The +.B <iter.h> +header provides constructs for expressing iteration. For simple loops that +occur often \(em iterating over an array, string or the positional +fields \(em several dedicated constructs are provided: +.BR doarray , +.B dostring +and +.BR dofields . +The separate +.B <cons.h> +header also provides simple iteration, for lists. + +In addition, +.B <iter.h> +provides a powerful general iteration facility which allows multiple +variables to be iterated in parallel or nested loops, stepping over +various kinds of spaces, with special clauses for calculation, +collecting lists, or controlling termination. + +Furthermore, a new clauses may easily be defined by the application +programmer, simply by defining six macros according to easy-to-follow +rules. + +Clauses are susceptible to macro expansion: new clauses can be defined +by writing macros that expand to existing clauses. + +In all of the iteration constructs of +.B <iter.h> +the variables which are supplied by the application are subject to assignment; +the constructs do not bind these variables. It is up to the application to +control the scope of these variables. + +In general, immediately after the termination of these looping constructs, +the variables explicitly specified by the application code remain visible, +and continue to hold the values they had immediately prior to loop termination. + +.SH SIMPLE ITERATION +.bk +.SS Macro \fIdoarray\fP +.bk +.B Syntax: + +.ft B + doarray (\fIkey\fP, \fIvalue\fP, \fIarr\fP) + \fIstatement\fP +.ft R + +.B Description: + +The +.B doarray +macro executes the +.I statement +for every element of the associative array +.IR arr . +Prior to each iteration, the variables +.I key +and +.I value +are set to the next key and value to be visited. + +Awk associative arrays are not required to maintain order; thus +.B doarray +does not traverse +.I arr +in any required order. + +.B Example: + +.ft B + \fI// assuming a is prepared like this:\fP + split("a:b:c", \fIa\fP, /:/) + + \fI// possible output is\fP + \fI// 3 c\fP + \fI// 1 a\fP + \fI// 2 b\fP + doarray (\fIk\fP, \fIv\fP, \fIa\fP) + print \fIk\fP, \fIv\fP +.ft R + +.SS Macro \fIdostring\fP +.bk +.B Syntax: + +.ft B + dostring (\fIidx\fP, \fIchr\fP, \fIstr\fP) + \fIstatement\fP +.ft R + +.B Description: + +The +.B dostring +macro evaluates +.I statement +for all successive substrings of length 1 of string +.IR str . +The variable +.I idx +steps from 1 up to the length of +.IR str , +and the +.I chr +variable takes on string values of length 1. On the first iteration, +.I chr +contains the first character of +.IR str , +then on the second iteration the second character and so forth. + +The +.I str +expression is evaluated only once. + +.B Example: + +.ft B + \fI// output is:\fP + \fI// 1 a\fP + \fI// 2 b\fP + \fI// 3 c\fP + dostring (\fIi\fP, \fIch\fP, "abc") + print \fIi\fP, \fIch\fP +.ft R + +.SS Macro \fIdofields\fP +.bk +.B Syntax: + +.ft B + dofields (\fIidx\fP, \fIval\fP) + \fIstatement\fP +.ft R + +.B Description: + +The +.B dofields +macro iterates over the Awk positional fields, executing +.I statement +for each iteration. The +.I idx +variable is initialized to 1. Before every iteration, +.I idx +is compared compared to the current value of +.BR NR . +The iteration proceeds if +.I i +.B <= +.B NR +is true. After the execution of +.IR statement , +.I idx +is incremented by one. + +Before each iteration, +.I val +is set to the value of the positional field indicated by +.IR idx , +namely \fB$\fP\fIidx\fP. + +.B Example: + +.ft B + \fI// set fields, assuming default FS\fP + $0 = "the quick brown fox" + + \fI// output is:\fP + \fI// 1 the\fP + \fI// 2 quick\fP + \fI// 3 brown\fP + \fI// 4 fox\fP + dofields (i, v) + print \fIi\fP, \fIv\fP +.ft P + +.SH THE LOOP MACRO +.bk +.SH Macro \fIloop\fP +.bk +.B Syntax: + +.ft B + loop (\fIclause1\fP, \fIclause2\fP, ...) + \fIstatement\fP +.ft R + +.B Description: +The +.B loop +macro repeatedly executes +.I statement +under the control of one or more clauses: +.IR clause1 , +.iR clause2 , +... + +Each clause contributes to the initial loop conditions, termination +testing, and actions of the loop. Under +.B loop +the clauses act in parallel. The same clauses may be combined into +a nested loop using the +.B loop_nest +macro. The term +.I parallel +here doesn't refer to concurrent processing with threads or processors, +but to the lock-step performance of loop iteration steps. + +Each clause communicates to +.B loop the following: +.IP initialization +What variable are to be be prepared with what initial values. +.IP termination +What conditions will terminate the loop. Prior to each iteration, +the termination test from every clause is interrogated. The loop +.I statement +executes only if all clauses indicate continued execution. If at least +one clause calls for termination, the loop ends. +.IP preparation +Whenever all clauses indicate that the loop continues, each clause +has the opportunity to make some preparation prior to the execution of +the loop, such as calculating the values of some variables. +.IP stepping +After the execution of each statement, each clause has the opportunity +to perform some increment step. +.IP finalization +When the loop terminates, some clauses execute some special code to +bring about a needed final state in their variables, or for some +other reason. +.PP + +The Awk +.B break +and +.B continue +statements are usable inside +.B loop +and behave like they do inside the +.B for +construct. The +.B break +statement terminates the loop, and +.B continue +terminates the +.I statement +only, proceeding to the increment step which prepares for the next +iteration. + +.B Example: + +.ft B + \fI// step variable i from 1 to 5 by 1,\fB + \fI// and variable j from 100 to 500 in steps of 100.\fB + + \fI// output is:\fB + \fI// 1 100\fB + \fI// 2 200\fB + \fI// 3 300\fB + \fI// 4 400\fB + \fI// 5 500\fB + + loop (range (\fIi\fB, 1, 5), + range_step (\fIj\fB, 100, 500, 100)) + { + print \fIi\fB, \fIj\fB + } +.ft R + +More example are given in the documentation of the clauses. + +.SH Macro \fIloop_nest\fP +.bk +.B Syntax: + +.ft B + loop_nest (\fIclause1\fP, \fIclause2\fP, ...) + \fIstatement\fP +.ft R + +.B Description: +The +.B loop_nest +macro has a syntax resembling that of +.BR loop . +Unlike +.BR loop , +it generates a nested loop: the logic of the clauses is arranged +into loop nestings. Each clause controls its own loop, in which +the loops of subsequent clauses are nested. In effect, the +.B loop_nest +syntax is a shorthand for writing: + +.ft B + loop (\fIclause1\fP) + loop (\fIclause2\fP) + loop (\fIclause3\fP) + ... + loop (\fIclauseN\fP) + \fIstatement\fP +.ft R + +There is a special clause called +.B parallel +which is useful inside a +.BR loop_nest . +Detailed documentation for it is given in its own section. The +.B parallel +clause combines multiple clauses into a single clause, in such a way +that those clauses are executed in parallel regardless of which loop +macro is used. Therefore the following equivalence also holds: + +Note: consistently with the semantics of +.B loop_nest +being that of the above shorthand, the +.B break +and +.B continue +statements affect only the innermost loop corresponding to the last clause, +.IR clauseN . +The +.B break +statement only breaks out of that loop, and +.B continue +only skips to the iteration part of that loop. + +Note: the semantics of all clauses such as termination control clauses and list +collection clauses must also be understood in terms of the nesting. +For instance if a collect clause is nested inside another loop which +repeats three times, then that collection will be repeated three times: +the collection variable will be initialized three times, collection +will be performed three times. Only the items collected by the last of +the three repetitions of the collect loop will be retained. Or, if instead +of collect, maximize is used to calculate a maximum value, then three +maxima will be calculated over the three invocations of the maximizing +loop, only the effect of the last of which will be retained in the +variable which receives the maximum value. + +.ft B + loop (\fIclause1\fP, \fIclause2\fP, ..., \fIclause\fP) + \fIstatement\fP +.ft R + +may be achieved using + +.ft B + loop_nest (parallel (\fIclause1\fP, \fIclause2\fP, ..., \fIclauseN\fP)) + \fIstatement\fP +.ft R + +.B Example: + +.ft B + #include <iter.h> + #include <cons.h> + + BEGIN { + loop_nest (list(it, let, list("a", "b", "c")), + range(x, 1, 3)) + print let "-" x + } + + Output: + + a-1 + a-2 + a-3 + b-1 + b-2 + b-3 + c-1 + c-2 + c-3 +.ft R + +.SH LOOP CLAUSES: NUMERIC AND GENERAL STEPPING +.bk +.SS Loop clauses \fIrange\fP and \fIrange_step\fP +.bk +.B Syntax: + +.ft B + range (\fIidx\fP, \fIfrom\fP, \fIto\fP) + range_step (\fIidx\fP, \fIfrom\fP, \fIto\fP, \fIstep\fP) +.ft R + +.B Description: + +The +.B range +loop clause initializes the +.I idx +variable to the value of the +.I from +expression. Prior to each loop iteration, the expression +.BI idx " <= (to) +is tested. If it is false, the loop terminates. +After each execution of +.IR statement , +.I idx +is incremented by 1. +The +.I to +expression is reevaluated at the beginning of each iteration, +so its value may change. + +The +.B range_step +clause is a variation of +.B range +which allows the amount added to +.I idx +to be specified as the +.I step +expression. The +.I step +expression is evaluated after each iteration, so its +value may change. That value is added to +.I idx . + +Note: +.B loop +clauses may not have optional arguments; it is not possible +to write a single loop clause which takes an optional step +size that defaults to 1. + +.SS Loop clauses \fIfrom\fP and \fIfrom_step\fP +.bk +.B Syntax: + +.ft B + from (\fIidx\fP, \fIfrom\fP) + from_step (\fIidx\fP, \fIfrom\fP, \fIstep\fP) +.ft R + +.B Description: + +The +.B from +clause is similar to +.BI range , +except that the +.I to +expression is missing. The clause performs no termination test; +it initializes +.I idx +to the +.I from +value and then executes indefinitely, forever incrementing +.B idx +by one. In order for the loop to terminate, another clause must +be present which requests termination, or else +.B break +must be used to terminate the loop abruptly. + +The +.B from_step +clause is a variant of +.B from +which allows the amount added to +.I idx +at the increment step to be determined by the value of the +.I step +expression, which is reevaluated each time. + +.SH LOOP CLAUSES: CONTAINER TRAVERSAL +.bk +.SS Loop clause \fIstr\fP +.bk +.B Syntax: + +.ft B + str (\fIidx\fP, \fIch\fP, \fIstr\fP) +.ft P + +.B Description: + +The +.B str +loop clause iterates over a string. The +.I str +expression is evaluated once to produce a string. The +.I idx +variable steps from 1 to up to the length of the string. +If the string is empty, the loop terminates without +any iterations taking place. +Prior to each iteration, the +.I ch +variable is set to a one-character-long substring of the +string starting at the +.I idx +position. + +.SS Loop clause \fIlist\fP +.bk +.B Syntax: + +.ft B + list (\fIiter\fP, \fIvar\fP, \fIlist\fP) +.ft R + +.B Description: + +The +.B list +loop clause iterates over the elements of a list. +Note: +the inclusion of the +.B <iter.h> +header does not make visible list manipulation libraries such as +.BR <cons.h> , + +The +.I iter +variable is initialized to +.IR list . +Prior to each iteration, +.I iter +is tested for termination as if using the +.B endp +function. If +.I iter +refers to a nonempty list, and thus iteration may continue, then +.I var +is set to the first item in +.IR iter , +.BI car( iter ) \fR,\fP +prior to the execution of the loop +.IR statement . + +After each iteration, +.B iter +is replaced with +.BI cdr( iter ) \fR.\fP + +.SS Loop clause \fIfields\fP +.bk +.B Syntax: + +.ft B + fields (\fIvar\fP) +.ft R + +.B Description: + +The +.B fields +loop clause iterates over the Awk positional fields. An internal +counter is initialized to 1. Iteration proceeds if this counter is +less than or equal to the current value of +.BR NF . +The counter is incremented by 1 after each iteration. + +Prior to the execution of the loop +.IR statement , +.I var +is set to the field indicated by the internal counter. + +.SS Loop clause \fIkeys\fP +.bk +.B Syntax: + +.ft B + keys (\fIkey\fP, \fIarray\fP) +.ft R + +.B Description: + +The +.B keys +loop clause iterates over the keys (indices) of an Awk associative +array named by the +.I array +parameter. The +.I key +variable is set to each index in turn. The keys are not visited in +any specific, required order. + +.SH LOOP CLAUSES: COLLECTION INTO LISTS +.bk +.SS Loop clauses \fIcollect\fP and \fIcollect_plus\fP +.bk +.B Syntax: + +.ft B + collect (\fIvar\fP, \fIexpr\fP) + collect_plus (\fIvar\fP, \fIexpr\fP) +.ft R + +.B Description: + +The +.B collect +clause initializes +.I var +to an empty bag object as if by using the +.B list_init +macro from +.BR <cons.h> . +The clause provides no termination test; if the only clauses in a +.B loop +are +.B collect +clauses, then it will not terminate. Prior each execution of the +.IR statement , +the +.B collect +clause evaluates +.I expr +and replaces +.B var +with a new bag which contains that value, as if by the expression +.IB var " = list_add(" var ", " expr ) \fR.\fP +When the loop terminates, +.I var +is replaced with a list formed from the bag which it used to hold, +as if by +.IB var " = list_end(" var ) \fR.\fP +The effect is that +.I var +ends up with a list of the values of +.I expr +that were sampled before each iteration of the loop. + +The +.B collect_plus +clause is almost exactly the same as +.B collect +except in regard to the final behavior. When the loop terminates, +.B collect_plus +collects the value of +.I expr +one more time prior to the conversion to list. +The effect is that +.I var +ends up with a list of all the values of +.I expr +that were sampled before each iteration of the loop, as well +as one more sample of +.I expr +taken after loop termination. + +.SH LOOP CLAUSES: CALCULATION +.bk +.SS Loop clause \fIsumming\fP +.bk +.B Syntax: + +.ft B + summing (\fIvar\fP, \fIexpr\fP) +.ft R + +.B Description: + +The +.B summing +clause calculates the sum of the values of +.I expr +over the course of the loop. +The clause contains no provision for termination; if the only +clause in a +.B loop +is +.B summing +then it will not terminate. + +The +.B summing +clause initializes +.B var +to zero. Prior to each execution of the loop's +.IR statement , +.I expr +is evaluated and its value added to to +.IR var . + +The effect is that after the loop terminates, +.I var +ends up with the sum of the samples of the value of +.I expr +from before each iteration of the loop. + +.SS Loop clauses \fIminimizing\fP and \fImaximizing\fP +.bk +.B Syntax: + +.ft B + maximizing (\fIvar\fP, \fIexpr\fP) + minimizing (\fIvar\fP, \fIexpr\fP) +.ft R + +.B Description: + +The +.B minimizing +and +.B maximizing +clauses initialize +.I var +to the value +.IR nil . +(See the +.B cppawk-cons +manual page for +.BR <cons.h> ). + +Prior to each execution of the loop +.IR statement , +.I var +is updated as follows. +If +.I var +is +.BR nil , +then it receives the value of +.IR expr , +thereby establishing that value as the hitherto calculated minimum or +maximum. If +.I var +is not already +.BR nil , +then +.B minimize +updates it with the value of +.I expr +if that value is smaller than +.IR var , +and similarly, +.B maximize +replaces +.I var +with the value of +.I expr +if that value is greater than +.IR var . + +Neither +.B minimize +nor +.B maximize +bring about loop termination. + +The effect of these clauses it to calculate the minimum or +maximum observed of value of +.I expr +as sampled before each execution of the loop statement. +If the loop never executes the +.IR statement , +then +.I var +retains the +.B nil +value indicating that no minimum or maximum had been found. + +.SS Loop clauses \fIargmax\fP and \fIargmin\fP +.bk +.B Syntax: + +.ft B + argmax (\fImaxvar\fP, \fIargvar\fP, \fIexpr\fP) + argmin (\fIminvar\fP, \fIargvar\fP, \fIexpr\fP) +.ft R + +.B Description: + +The +.B argmax +and +.B argmin +clauses calculate the value of a variable +.I argvar +which maximize or minimizes the value of +.IR expr . + +This maximum or minimum value appears in +.I maxvar +or +.IR minvar +respectively. + +The purpose of the +.I argvar +argument is to name the variable that is being maximized +or minimized. It is expected that the +.I expr +expression contains +.I argvar +and thus its value is dependent on +.IR argvar . + +Like +.B minimize +and +.BR maximize , +these clauses never bring about loop termination. + +First, +.I minvar +or +.I maxvar +is initialized to the +.B nil +value. +(See the +.B cppawk-cons +manual page for +.BR <cons.h> ). + +If the loop +.I statement +never executes, then these variables retain the +.B nil +value to indicate that no argument maximum or minimum was calculated. + +Prior to each execution of +.IR statement , +.I expr +is evaluated. If it is the first iteration, then +.I maxvar +or +.I minvar +is set to the value of +.IR argvar . +If it is the second or subsequent iteration, then +.B argmax +sets +.I maxvar +to the value of +.I argvar +if +.I expr +is higher than the previously seen maximum value of +.IR expr . +Likewise, +.B argmin +sets +.I minvar +to the value of +.I argvar +if +.I expr +is lower than the previously seen minimum value of +.IR expr . + +.B Example: + +Find the values of +.I x +where the expression +.BI sin( x ") * cos(" x ) +has a maximum and minimum value, over the +.I x +range 0 to 3.14159 examined in +increments of 0.001. + +Note that the variable used as +.IR argvar , +namely +.I x +is made to vary by the use of a +.I range_step +clause. For +.B argmax +and +.B argmin +to be useful, the argument variable has to vary from one iteration +to the next, and the +.I expr +has to be a function of that variable. + +.ft B + #include <iter.h> + + BEGIN { + loop (range_step (\fIx\fP, 0, 3.14159, 0.001), + argmax (\fImx\fP, \fIx\fP, sin(\fIx\fP) * cos(\fIx\fP)), + argmin (\fImi\fP, \fIx\fP, sin(\fIx\fP) * cos(\fIx\fP))) + ; // empty + + print "max x =", \fImx\fP + print "min x =", \fImi\fP + } +.ft R + +Output: + +.ft B + max x = 0.785 + min x = 2.356 +.ft R + +.SH LOOP CLAUSES: TERMINATION CONTROL +.bk +.SS Loop clauses \fIwhile\fP and \fIuntil\fP +.bk +.B Syntax: + +.ft B + while (\fIexpr\fP) + until (\fIexpr\fP) +.ft R + +.B Description: + +The +.B while +and +.B until +clauses provide a termination test to the loop. + +Prior to each iteration, +.I expr +is evaluated. + +Under the +.B while +clause, if +.I expr +is false. the loop terminates. + +Under the +.B until +clause, if +.I expr +is true, the loop terminates. + +Loop terminations are short circuited among parallel clauses. +So that is to say, if an earlier clause indicates loop +termination, then the termination tests of later clauses are not performed. +Moreover, the preparation actions of +.B no +clause are performed when the loop terminates; only if it has +been confirmed that the +.I statement +is going to be executed, due to the termination tests from all +clauses reporting false, are the preparation actions executed. +Therefore, in any iteration, later termination tests can rely on earlier +termination tests having executed. For instance, if the success of an earlier +termination test implies that a certain variable is safe to use in certain way, +then a later termination test may use it in that way. Likewise, loop +preparations may rely on all termination tests having executed. + +All tests in loop, including +.B while +and +.B until +are top-of-loop tests: tests carried out before every iteration, +including the first. A bottom-of-loop test is one which is carried out after +each iteration, which is logically equivalent to a top-of-loop test which is +unconditionally true before the first iteration, and then turns into a +.I "bona fide" +test. A bottom-of-loop testing version of +.B while +or +.B until +isn't provided in +.B <iter.h> +but can be developed as an application-defined clause. It may also +be simulated with the help of the first_then clause, according +to this pattern: + +.ft B + loop_for (first_then (\fIfirst_iter\fP, 1, 0), + while (\fIfirst_iter\fP || \fIother_condition\fP)) + statement +.ft R + +Here, the +.B first_iter +flag is initialized to 1, and then after the first iteration steps to 0. +Therefore the +.B while +clause's test is always true before the first iteration, and +.I other_condition +isn't tested. + +.SH LOOP CLAUSES: COMBINATORS +.bk +.SS Loop clause \fIparallel\fP +.bk +.B Syntax: + +.ft B + parallel (\fIclause1\fP, \fIclause2\fP, ...) +.ft R + +.B Description: + +The +.B parallel +construct may be used in the +.B loop_nest +macro, to indicate groups of clauses that should not be nested +but treated in parallel. + +The +.B parallel +clause takes one or more arguments which are loop clauses. +It arranges for the argument clauses to be performed +in parallel, just like the way clauses are treated by the +.B loop +construct. + +For instance, the structure: + +.ft B + loop_nest (\fIclause1\fP, + parallel (\fIclause2\fP, \fIclause3\fP), + \fIclause4\fP) + \fIstatement\fP +.ft R + +may be understood as equivalent to: + +.ft B + loop (\fIclause1\fP) + loop (\fIclause2\fP, \fIclause3\fP) + loop (\fIclause4\fP) + \fIstatement\fP +.ft R + +.S Loop clause \fIif\fP +.bk +.B Syntax: + +.ft B + if (\fItest\fP, \fIclause\fP) +.ft R + +.B Description: + +The +.B if +clause activates or deactivates the contained +.I clause +based on the value of the +.I test +expression. + +Firstly, the initializations of +.I clause +are performed unconditionally, as if it were not embedded in +.BR if . + +Prior to every iteration, if the +.I test +expression is false, then +.IR clause 's +tests are not performed, and are assumed to be true. Thus while +.I test +is true, +.I clause +is prevented from being able to terminate the loop. + +Secondly, prior to the execution of the loop +.IR statement , +.I test +is evaluated again. If the expression is false, then the +preparation actions of +.I clause +are skipped. + +Lastly, prior to the execution of the iteration step actions. +expression is false, then the step actions of +.I clause +are skipped. + +Effectively, the +.I clause +is suspended while the +.I test +expression is false. + +.B Example: + +Print a row number before the first element of every row. While +this specific program can be coded much more succinctly, the goal +is to demonstrate how the +.B first_then +clause is activated by the the condition +.IB i " % 10 ==" +.BR 1 . + +.ft B + #include <iter.h> + + function row(\fIpg\fP) + { + if (\fIpg\fP > 1) + print + printf "r%03d", \fIpg\fP + return \fIpg\fP + } + + BEGIN { + loop (range(\fIi\fP, 1, 100), + if (\fIi\fP % 10 == 1, first_then(\fIpg\fP, row(1), row(\fIpg\fP + 1)))) + printf " %3d", \fIi\fP + } +.ft R + +.B Output: + +.ft B + r001 1 2 3 4 5 6 7 8 9 10 + r002 11 12 13 14 15 16 17 18 19 20 + r003 21 22 23 24 25 26 27 28 29 30 + r004 31 32 33 34 35 36 37 38 39 40 + r005 41 42 43 44 45 46 47 48 49 50 + r006 51 52 53 54 55 56 57 58 59 60 + r007 61 62 63 64 65 66 67 68 69 70 + r008 71 72 73 74 75 76 77 78 79 80 + r009 81 82 83 84 85 86 87 88 89 90 + r010 91 92 93 94 95 96 97 98 99 100 +.ft R + +.SH USER-DEFINED CLAUSES +.bk +It is possible to define new clauses for the +.B loop +macro, in application code. + +.SS Definition via Macro + +One method by which a user defined +.B loop +clause is possible is by writing it as a macro. +This is because clauses look like macro invocations +and are susceptible to expansion. + +.B Example: + +Introduce a +.BI repeat( n ) +clause that repeats +.I n +times, where +.I n +is an expression. + +.ft B + #define repeat(n) range(repeat_counter_ ## __LINE__, 1, (n)) +.ft R + +.SS Primary Definition + +An entirely new loop clause is developed by writing six macros, +one of which is required only if the +.I egawk +(Enhanced GNU Awk) implementation of Awk is being used. +The macros have names which are derived from the name of the clause. + +For example, to implement a clause called +.BR myclause , +the following macros must be written: +.BR __temp_myclause , +.BR __init_myclause , +.BR __test_myclause , +.BR __prep_myclause , +.B __fini_myclause +and +.BR __step_myclause . +The +.BR __temp_myclause +macro is not used unless the Awk implementation is +.I egawk . + +All six macros must accept exactly the same arguments, and those +will be the arguments that the clause will accept. They are described +next: + +.IP \fB__temp_\fP +The +.I temp +macro must expand to a comma-terminated list of temporary variable names which +are needed by the clause. If the clause needs no hidden temporary variables, +then this must expand to a terminating comma. Under the +.I egawk +implementation, these variables will be accumulated into a +.B @let +construct which precedes the loop, so that they are introduced as lexical +variables visible only inside the loop. + +.IP \fB__init_\fP +The +.I init +macro must expand to an expression which performs variable initializations. +If the clause requires no initializations, its expansion must be the +numeric token +.BR 1 . + +.IP \fB__test_\fP +The +.I test +macro must expand to an expression whose value is true if, and only if, the +clause wishes the loop to terminate. If the clause does not terminate the +loop, the expansion of this macro must be the numeric token +.BR 1 . + +.IP \fB__prep_\fP +The +.I prep +macro must expand to an expression that the clause needs to evaluate +prior to the execution of the loop's iteration statement. This is evaluated +only if all clauses have indicated that the loop isn't terminating, +and hence the statement is going to be executed. + +.IP \fB__fini_\fP +The +.I fini +macro must expand to an expression that the clause needs to evaluate +in the situation when the loop terminates. If any termination test from +any clause of a +.B loop +indicates that the loop must terminate, then the loop +.I statement +will not be executed any more; instead, the +.I fini +expressions of all the clauses will be evaluated, and then the loop +ends. If a clause does not have any +.I fini +action, then this macro must expand to the token +.BR 1 . + +.IP \fB__step_\fP +The +.I step +macro must expand to an expression which is evaluated after every +execution of the loop +.I statement , +in order to prepare new values of loop variables for the next iteration. +Here is where numeric step variables are incremented and so forth. +If the clause doesn't step, then this must expand to +.BR 1 . + +.SS Example: \fBnull\fP clause + +Suppose we wish to define a clause called +.B null +which takes no arguments and does nothing. A loop which contains +only this clause iterates forever. If the clause is added to any +.BR loop , +the semantics remains unchanged. The entire implementation is +this: + +.ft B + #include <iter.h> + + #define __temp_null , + #define __init_null 1 + #define __test_null 1 + #define __prep_null 1 + #define __fini_null 1 + #define __step_null 1 + + BEGIN { + loop (range (i, 1, 5), + null) // does nothing + print i + } +.ft R + +.SS Example: alpha-numeric stepping. +.bk +Suppose we have a function +.BI nxstr( s ", " u ) +which behaves as follows, on these example inputs: + +.ft B + nxstr("000", "999") -> "001" + nxstr("007", "777") -> "010" + nxstr("abc", "zzz") -> "abd" + nxstr("xxx", "yyy") -> "xxy" + nxstr("xxy", "yyy") -> "xya" + nxstr("yyx", "yyy") -> "yyy" + nxstr("yyy", "yyy") -> 0 +.ft R + +The function +.BI nxstr +implements a relation that could could be called "alpha-numeric step", +where the second argument indicates limiting characters. + +A precise specification follows. +Firstly, both +.I s +and +.I u +are alphanumeric strings of equal length, consisting of nothing but +digits or the 26 letters of the English alphabet, in either upper or lower +case. Furthermore, for every character in +.IR s , +the corresponding character in +.I u +is in the same category: digit, lower case or upper case, and +that corresponding character has a rank at least as high. +For instance, where +.I s +has the character +.B p , +.I u +may have the characters +.BR p , +.BR q , +.BR r ... +but not +.B o +because +.B o +has a lower rank, and not +.B X +or +.B 7 +because they are in a different category. + +The +.I u +argument gives an upper limit. If +.I s +is identical to +.I u +then +.B nxstr +returns 0. Otherwise +.B nxstr +returns the next alphanumeric string derived from +.I s +as follows: if the last character is equal to the corresponding one in +.I s +then it is reset to the leading element of the category, otherwise +it is replaced by its successor. In the case when the character is reset, the +procedure is repeated with the character to the left, to increment the next +digit. If that one is reset, then again, to the left and so forth. + +We would like to implement a loop clause which steps a variable +.I s +through a range of strings, as in +.BI alpha_range( s ", \(dqaa0\(dq, \(dqcc9\(dq" ) +to step through the strings "aa0", "aa1", ... "aa9", "ab0", ... "ab9", ... "cc0", ... "cc9". + +.ft B + #include <iter.h> + + \fI// ... implementation of nxstr goes here ...\fP + + #define __temp_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) 1 + #define __init_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) \fIs\fP = \fIfrom\fP + #define __test_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) \fIs\fP + #define __prep_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) 1 + #define __fini_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) 1 + #define __step_alpha_range(\fIs\fP, \fIfrom\fP, \fIto\fP) \fIs\fP = nxstr(\fIs\fP, \fIto\fP) + + BEGIN { + loop (alpha_range (\fIx\fP, "aa0", "cc9")) + print \fIx\fP + } +.ft R + +A working implementation of +.B nxstr +follows: + +.ft B + // "register nextchar" + function rn(x, y, + c) + { + nextchar[x] = y + if (y in nextchar) { + resetchar[x] = y + for (c = y; nextchar[c] != y; c = nextchar[c]) + resetchar[c] = y + } + } + + BEGIN { + rn("0", "1"); rn("1", "2"); rn("2", "3"); rn("3", "4"); + rn("4", "5"); rn("5", "6"); rn("6", "7"); rn("7", "8"); + rn("8", "9"); rn("9", "0"); + + rn("a", "b"); rn("b", "c"); rn("c", "d"); rn("d", "e"); + rn("e", "f"); rn("f", "g"); rn("g", "h"); rn("h", "i"); + rn("i", "j"); rn("j", "k"); rn("k", "l"); rn("l", "m"); + rn("m", "n"); rn("n", "o"); rn("o", "p"); rn("p", "q"); + rn("q", "r"); rn("r", "s"); rn("s", "t"); rn("t", "u"); + rn("u", "v"); rn("v", "w"); rn("w", "x"); rn("x", "y"); + rn("y", "z"); rn("z", "a"); + + rn("A", "B"); rn("B", "C"); rn("C", "D"); rn("D", "E"); + rn("E", "F"); rn("F", "G"); rn("G", "H"); rn("H", "I"); + rn("I", "J"); rn("J", "K"); rn("K", "L"); rn("L", "M"); + rn("M", "N"); rn("N", "O"); rn("O", "P"); rn("P", "Q"); + rn("Q", "R"); rn("R", "S"); rn("S", "T"); rn("T", "U"); + rn("U", "V"); rn("V", "W"); rn("W", "X"); rn("X", "Y"); + rn("Y", "Z"); rn("Z", "A"); + } + + function nxstr(str, upto, + l, sdig, udig, nxdig) + { + if (str == upto) + return 0 + + len = length(str) + + for (; len > 0; --len) { + sdig = substr(str, len, 1) + udig = substr(upto, len, 1) + + if (sdig == udig) + nxdig = resetchar[sdig] + else + nxdig = nextchar[sdig] + + str = substr(str, 1, len - 1) nxdig substr(str, len + 1) + + if (sdig != udig) + break + } + + return str + } +.ft R + +.SH "SEE ALSO" + +cppawk(1) + +.SH BUGS + +The +.B parallel +clause cannot be used in +.BR loop , +which prevents it from being useful in macro implementations of +clauses. This is because it relies on a macro that is also being used +in the expansion of +.B loop . +This issue is discussed in the BUGS section of the main +.B cppawk +man page. + +.SH AUTHOR +Kaz Kylheku <kaz@kylheku.com> + +.SH COPYRIGHT +Copyright 2022, BSD2 License. |