Author: Virgil Dupras <email@example.com>
Date: Thu, 23 Feb 2023 10:14:53 -0500
lib/str: add "rfor" iterator
See doc/lib/str. Also, increase RS space for iterator variables to 12 bytes
(add "k"). It was needed for the rfor iterator. I hope I won't have to increase
it by much more.
This was also the first time I used my scheme for "safe bootlo updates on a live
Dusk machine", that is, to "spitBoot" at an alternate location and have the MBR
bootloader optionally boot from that location. It worked well!
4 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/fs/doc/iter.txt b/fs/doc/iter.txt
@@ -35,8 +35,8 @@ The heavy lifting is done by ":iterator", which is a does word generating
immediate compiling words (in this instance, "for"). When that word is called, a
few things happen:
-1. 8 bytes are reserved on RS for "i" and "j". It is always 8 bytes for all
- iterators, and it's always "i" and "j", even when "j" isn't used.
+1. 12 bytes are reserved on RS for "i" and "j". It is always 12 bytes for all
+ iterators, and it's always "i", "j" and "k", even when they aren't used.
2. A call to "for"'s body is written.
3. Two intertwined ahead jumps are written in a way that allow "unyield" to exit
the loop in cases where the iterator has no yield.
@@ -44,17 +44,17 @@ few things happen:
5. When "next" (an immediate too) is called, a "yield" is compiled, followed by
a backward jump to the beginning of the loop, followed by a forward target
for the exit jump compiled at "for".
-6. De-allocate the 8 bytes reseved for "i" and "j".
+6. De-allocate the 12 bytes reseved for i/j/k.
Iterators are expected to keep PS and RS balanced between yields. For this
-reason, iteration values should exclusively be passed through "i" and "j".
+reason, iteration values should exclusively be passed through i/j/k
-## i and j
+## i, j and k
-"i" and "j" are value-like words (obey "to" semantics) that live on RS. They
-work a lot like local variables, but their offset is calculated from RS top
-rather than from [rcnt]. "i" lives at RS+4 and "j" lives at RS+8. RS+0 is the
-coroutine swapping address.
+"i", "j" and "k" are value-like words (obey "to" semantics) that live on RS.
+They work a lot like local variables, but their offset is calculated from RS top
+rather than from [rcnt]. "i" lives at RS+4, "j" lives at RS+8 and "k" lives at
+RS+12. RS+0 is the coroutine swapping address.
We use RS for those variables for multiple reasons:
diff --git a/fs/doc/lib/str.txt b/fs/doc/lib/str.txt
@@ -1,6 +1,7 @@
# String utilities
-This unit lives at /lib/str.fs and provides a few string-related words.
+This unit lives at /lib/str.fs and provides a few strings an range related
Most strings in Dusk are regular strings as doc/usage describes. However, we
sometimes have to deal with null-terminated strings and this unit also has words
@@ -67,6 +68,12 @@ stringlist ( n "name" "..." -- )
want them in the string) in the quotes.
Example: 3 stringlist mylist "foo" "bar" "hello world!"
+rfor ( a u -- ) i=a j=idx k=u
+ An iterator over the range "a u". It iterates "u" time, yielding an increased
+ "a" as "i". Additionally, an index starting at 0 is maintained at "j".
+ Example: S" hello" c@+ rfor i c@ emit j . next --> h0e1l2l3o4
rtrimright ( n a u -- a u )
Trim "n" characters at the right of range "a u" and return the resulting
diff --git a/fs/lib/str.fs b/fs/lib/str.fs
@@ -14,10 +14,16 @@ create _buf STR_MAXSZ allot
: zstrlen ( zstr -- len )
0 swap $100 [c]? ( idx ) dup 0< if abort" string too long" then ;
-: [str]? ( str a u -- idx ) rot> >r >r ( u ) \ V1=aref V2=str
- V2 c@ - dup 0>= if V1 swap 1+ for ( a )
- dup V2 c@+ = if ( a ) V1 - break then 1+ next drop -1 then
- else drop -1 then ( idx ) 2rdrop ;
+:iterator rfor ( a u -- )
+ ?dup if
+ to k to i 0 to j
+ begin yield 1 to+ i 1 to+ j j k >= until
+ else drop then unyield ;
+: [str]? ( str a u -- idx ) rot >r ( a u ) \ V1=str
+ V1 c@ - dup 0>= if ( a u-adj ) 1+ rfor ( )
+ i V1 c@+ = if j break then next -1 then
+ else 2drop -1 then ( idx ) rdrop ;
: sfind ( str list -- idx ) -1 rot> begin ( idx s a )
rot 1+ rot> ( idx s a )
@@ -43,8 +49,7 @@ create _buf STR_MAXSZ allot
next ( c ) drop 0 then rdrop ;
: rfind ( a u pairs -- idx ) >r \ V1=pairs
- 0 swap for2 ( a ) c@+ V1 rmatch if drop i break then next
- ( a ) drop -1 then rdrop ;
+ rfor ( ) i c@ V1 rmatch if j break then next -1 then rdrop ;
create _ 2 c, ," 09"
: 0-9? ( c -- f ) _ rmatch ;
diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs
@@ -125,19 +125,19 @@ alias execute | immediate
: xtcomp [compile] ] begin word runword compiling not until ;
: ivar, ( off -- ) r', _toptr@ execute, ;
-: i 4 ivar, ; immediate : j 8 ivar, ; immediate
+: i 4 ivar, ; immediate : j 8 ivar, ; immediate : k 12 ivar, ; immediate
: :iterator doer immediate xtcomp does>
- -8 r+, execute, -4 [rcnt] +!
+ -12 r+, execute, -4 [rcnt] +!
[compile] ahead \ jump to loop
[compile] ahead \ exit jump
swap [compile] then [compile] begin ( loop ) ;
0 value _breaklbl
[compile] yield [compile] again [compile] then
- 8 r+, 4 [rcnt] +! 0 to@! _breaklbl ?dup drop ; immediate
+ 12 r+, 4 [rcnt] +! 0 to@! _breaklbl ?dup drop ; immediate
CALLSZ CELLSZ + const BRSZ
: unyield compile BRSZ 0 r', compile +! ; immediate
-: break 12 r+, [compile] ahead to _breaklbl ; immediate
+: break 16 r+, [compile] ahead to _breaklbl ; immediate
:iterator for ( n -- )
?dup if to i begin yield -1 to+ i i not until then unyield ;