Control Structures in zeptoforth using Quotations - tabemann/zeptoforth GitHub Wiki
In addition to standard Forth control structures, zeptoforth has control structures based on execution tokens, normally created with quotations. Quotations are blocks of code within other word definitions or quotations which are bracketed with [:
and ;]
which compile a literal containing their execution token within their containing word definition or quotation.
These control structures live in the lambda
module. In the below it will be assumed that lambda import
will have been executed beforehand.
Note that in the words mentioned below, passed-in execution tokens when executed have full access to the data stack, which they may manipulate without restriction, with all control state being stored on the return stack.
The simplest quotation control structure is qif
( flag if-xt -- ). It takes an execution token and a flag off the stack and executes if-xt if and only if flag is non-zero. Take the following example:
: test ( n -- ) 0> [: ." greater than zero " ;] qif ; ok
1 test greater than zero ok
-1 test ok
Another simple quotation control structure is qifelse
( flag if-xt else-xt -- ). It takes two execution tokens and a flag off the stack and executes if-xt if and only if flag is non-zero, else it executes else-xt. Take the following example:
: test ( n -- ) ok
0> ok
[: ." greater than zero " ;] ok
[: ." less than zero " ;] qifelse ok
; ok
1 test greater than zero ok
-1 test less than zero ok
A simple loop quotation control structure is provided by quntil
( ? loop-xt -- ? ). It takes an execution token off the stack with the signature ( ? -- ? flag ) and executes it repeatedly until it returns a non-zero value. Take the following example:
: test ( n -- ) [: 1- dup . dup 0= ;] quntil drop ; ok
10 test 9 8 7 6 5 4 3 2 1 0 ok
A sightly more elaborate loop quotation control structure is provided by qwhile
( ? while-xt repeat-xt -- ? ). It takes two execution tokens off the stack, while-xt having the signature ( ? -- ? flag ) which defines the loop condition and returns non-zero until looping concludes and repeat-xt having the signature ( ? -- ? ) and which is executed until the loop condition returns false. Take the following example:
: test ( n -- ) [: dup 0<> ;] [ 1- dup . ;] qwhile drop ; ok
10 test 9 8 7 6 5 4 3 2 1 0 ok
0 test ok
An infinite loop quotation control strucure is provided by qagain
( ? loop-xt -- ? ). It takes an execution token, loop-xt ( ? -- ? ), off the stack and executes it in an infinite loop. The only way to exit the loop is to raise an exception within it. Take the following example:
: test ( n -- ) ok
[: 1- dup . dup 0= [: [: ." exited" cr ;] ?raise ;] qif ;] ok
qagain ok
; ok
10 test 9 8 7 6 5 4 3 2 1 0 exited
Functionality equivalent to the standard ?do-loops is provided by qcount
( ? limit start iter-xt -- ? ). It takes an execution token, a starting value, and a limit off the stack and executes iter-xt which has the signature ( ? i -- ? ) with successive values starting at start until it reaches limit, non-inclusive; note that if start equals limit then iter-xt is not executed. Take the following example:
: test ( n -- ) 0 ['] . qcount ; ok
10 test 0 1 2 3 4 5 6 7 8 9 ok
0 test ok
Functionality equivalent to the standard ?do+loops is provided by qcount+
( ? limit start iter-xt -- ? ). It takes an execution token, a starting value, and a limit off the stack and executes iter-xt which has the signature ( ? i -- ? increment ) with values starting at start which are incremented by the returned value increment until they reach or pass limit (this is non-inclusive if increment is positive but inclusive if increment is negative); note that if start equals limit then iter-xt is not executed. Take the following example:
: test-up ( n -- ) 0 [: . 2 ;] qcount+ ; ok
: test-down ( n -- ) 0 swap [: . -2 ;] qcount+ ; ok
10 test-up 0 2 4 6 8 ok
10 test-down 10 8 6 4 2 0 ok
Iterating over arrays of small elements is provided by citer
, hiter
, iter
, and 2iter
respectively, which iterate over bytes, halfwords, words, and double words respectively and have the signature ( ? addr count iter-xt -- ? ), where addr is the starting address of the array, count is the array element count, and iter-xt is an execution token with the signature ( ? c -- ? ), ( ? h -- ? ), ( ? x -- ? ), and ( ? d -- ? ) respectively. count elements are iterated over in order starting from addr and are passed to iter-xt. Take the following example:
create array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
: test ( -- ) array 10 ['] . iter ; ok
test 0 1 2 3 4 5 6 7 8 9 ok
Iterating over sequences of small elements provided by a getter is provided by iter-get
and 2iter-get
, which iterate over words and double words respectively and have the signature ( ? get-xt count iter-xt -- ?), where get-xt is an execution token with the signatures ( ? i -- ? x ) and ( ? i -- ? d ) respectively, count is an element count, and iter-xt is an execution token with the signatures ( ? x -- ? ) and ( ? d -- ? ) respectively. Indices from 0 to count - 1 are passed to get-xt in that order and the returned value is the passed to iter-xt. Take the following example:
: test ( -- ) [: 2 * ;] 10 ['] . iter-get ; ok
test 0 2 4 6 8 10 12 14 16 18 ok
Iterating over array elements while also being provided an index to each element is accomplished with citeri
, hiteri
, iteri
, and 2iteri
, which have the signature ( ? addr count iter-xt -- ? ), and where iter-xt has a signature of ( ? c i -- ? ), ( ? h i -- ? ), ( ? x i -- ? ), and ( ? d i -- ? ) respectively. count elements, along with indices from 0 to count - 1, are passed to iter-xt. Take the following:
create array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
: test ( -- ) array 10 [: + . ;] iteri ; ok
test 0 2 4 6 8 10 12 14 16 18 ok
Iterating over sequences of small elements provided by a getter while also being provided an index to each element is provided by iteri-get
and 2iteri-get
, which iterate over words and double words respectively and have the signature ( ? get-xt count iter-xt -- ?), where get-xt is an execution token with the signatures ( ? i -- ? x ) and ( ? i -- ? d ) respectively, count is an element count, and iter-xt is an execution token with the signatures ( ? x i -- ? ) and ( ? d i -- ? ) respectively. Indices from 0 to count - 1 are passed to get-xt in that order and the returned value is the passed along with its index to iter-xt. Take the following example:
: test ( -- ) [: 2 * ;] 10 [: + . ;] iteri-get ; ok
test 0 3 6 9 12 15 18 21 24 27 ok
Mapping from one array to another is accomplished with cqmap
, hqmap
, qmap
, and 2qmap
. Note that the name qmap
is due to there being a map
module with which it would otherwise conflict. These words have the signature ( ? src-addr dst-addr count map-xt -- ? ), where map-xt has the signature ( ? c -- ? c' ), ( ? h -- ? h' ), ( ? x -- ? x' ) and ( ? d -- ? d' ) respectively. They fetch each element from index 0 to count - 1 starting at src-addr, apply map-xt to it, and store the result at the same index starting at dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- ) src-array dst-array element-count [: 2 * ;] qmap ; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array element-count array. 0 2 4 6 8 10 12 14 16 18 ok
Mapping from one sequence of small elements as defined by a getter and storing them with a setter is accomplished with qmap-get-set
and 2qmap-get-set
. These words have the signature ( ? get-xt count map-xt set-xt -- ? ), where get-xt has the signature ( ? i -- ? x ) and ( ? i -- ? d ) respectively, map-xt has the signature ( ? x -- ? x' ) and ( ? d -- ? d' ) respectively, and set-xt has the signature ( ? x i -- ? ) and ( ? d i -- ? ) respectively. Indices 0 through count - 1 are passed to get-xt, whose results are passed to map-xt, whose results along with said indices are passed to set-xt. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- ) ok
[: cells src-array + @ ;] element-count ok
[: 2 * ;] [: cells dst-array + ! ;] qmap-get-set ok
; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array element-count array. 0 2 4 6 8 10 12 14 16 18 ok
Mapping from one array to another along with providing an index is accomplished with cqmapi
, hqmapi
, qmapi
, and 2qmapi
. Note that the name qmap
is due to there being a map
module with which it would otherwise conflict. These words have the signature ( ? src-addr dst-addr count map-xt -- ? ), where map-xt has the signature ( ? c i -- ? c' ), ( ? h i -- ? h' ), ( ? x i -- ? x' ) and ( ? d i -- ? d' ) respectively. They fetch each element from index 0 to count - 1 starting at src-addr, apply map-xt to it and its index, and store the result at the same index starting at dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- ) src-array dst-array element-count [: swap 2 * + ;] qmapi ; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array element-count array. 0 3 6 9 12 15 18 21 24 27 ok
Mapping from one sequence of small elements as defined by a getter along with providing an index and storing them with a setter is accomplished with qmap-get-set
and 2qmap-get-set
. These words have the signature ( ? get-xt count map-xt set-xt -- ? ), where get-xt has the signature ( ? i -- ? x ) and ( ? i -- ? d ) respectively, map-xt has the signature ( ? x i -- ? x' ) and ( ? d i -- ? d' ) respectively, and set-xt has the signature ( ? x i -- ? ) and ( ? d i -- ? ) respectively. Indices 0 through count - 1 are passed to get-xt, whose results along with said indices are passed to map-xt, whose results along with said indices are passed to set-xt. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- ) ok
[: cells src-array + @ ;] element-count ok
[: swap 2 * + ;] [: cells dst-array + ! ;] qmapi-get-set ok
; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array element-count array. 0 3 6 9 12 15 18 21 24 27 ok
Filtering elements of one array and storing the filtered elements into another array, returning the total number of filtered elements, is accomplished with cfilter, hfilter, filter, and 2filter. These words have the signature ( ? src-addr dst-addr count filter-xt -- ? count' ), where filter-xt has the signature ( ? c -- ? flag ), ( ? h -- ? flag ), ( ? x -- ? flag ), and ( ? d -- ? flag ) respectively. Indices from 0 through count - 1 are fetched starting from src-addr and passed to filter-xt; when filter-xt returns a true response, the value is stored in the next available location starting from dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- count ) src-array dst-array element-count [: 2 umod 0= ;] filter ; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array swap array. 0 2 4 6 8 ok
Filtering small elements of a sequence defined by a getter and passing the filtered elements to a setter, returning the total number of filtered elements, is accomplished filter-get-set and 2filter-get-set. These words have the signature ( ? get-xt count filter-xt set-xt -- ? ), where get-xt has the signature ( ? i -- ? x ) and ( ? i -- ? d ) respectively, filter-xt has the signature ( ? x -- ? flag ) and ( ? d -- ? flag ) respectively, and set-xt has the signature ( ? x i -- ? ) and ( ? d i -- ? ) respectively. Indices from 0 through count - 1 are passed to get-xt and the results are passed to filter-xt; when filter-xt returns a true response, the value is stored in the next available location starting from dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- count ) ok
[: cells src-array + @ ;] element-count ok
[: 2 umod 0= ;] [: cells dst-array + ! ;] filter-get-set ok
; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array swap array. 0 2 4 6 8 ok
Filtering elements of one array, providing the index of each element, and storing the filtered elements into another array, returning the total number of filtered elements, is accomplished with cfilteri, hfilteri, filteri, and 2filteri. These words have the signature ( ? src-addr dst-addr count filter-xt -- ? count' ), where filter-xt has the signature ( ? c i -- ? flag ), ( ? h i -- ? flag ), ( ? x i -- ? flag ), and ( ? d i -- ? flag ) respectively. Indices from 0 through count - 1 are fetched starting from src-addr and passed along with their indices to filter-xt; when filter-xt returns a true response, the value is stored in the next available location starting from dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- count ) src-array dst-array element-count [: + 4 umod 0= ;] filteri ; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array swap array. 0 2 4 6 8 ok
Filtering small elements of a sequence defined by a getter, providing the index of each element, and passing the filtered elements to a setter, returning the total number of filtered elements, is accomplished filter-get-set and 2filter-get-set. These words have the signature ( ? get-xt count filter-xt set-xt -- ? ), where get-xt has the signature ( ? i -- ? x ) and ( ? i -- ? d ) respectively, filter-xt has the signature ( ? x i -- ? flag ) and ( ? d i -- ? flag ) respectively, and set-xt has the signature ( ? x i -- ? ) and ( ? d i -- ? ) respectively. Indices from 0 through count - 1 are passed to get-xt and the results are passed along with their indices to filter-xt; when filter-xt returns a true response, the value is stored in the next available location starting from dst-addr. Take the following example:
create src-array 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ok
10 constant element-count ok
element-count cells buffer: dst-array ok
: test ( -- count ) ok
[: cells src-array + @ ;] element-count ok
[: + 4 umod 0= ;] [: cells dst-array + ! ;] filteri-get-set ok
; ok
: array. ( addr count -- ) ['] . iter ; ok
test dst-array swap array. 0 2 4 6 8 ok