( iteration control stacks We create two new stacks - a small stack to hold the "current" value of the loop, or the "i" stack, and a larger stack to hold any extra state, as well as the cp of a word that moves to the next value, which we call the "next" stack. With these two new stacks, we can create a generic loop construct for iterating over streaming values. Not only that, but those values can be arbitrarily filtered and transformed simply by pushing a new value onto the iter-next stack which calls out to the previous one. ) uservar itop 8 cells userallot { userhere @ } const itop-init uservar nexttop 24 cells userallot { userhere @ } const nexttop-init ' task-init :chain >r itop-init itop r@ !far nexttop-init nexttop r@ !far r ( i -- v ) POP AX SHL AX 1 # MOV BX @[ SS: r MOV BX @[ SS: r@ @] INC BX INC BX MOV @[ SS: r ( v -- ) POP AX MOV BX @[ SS: r@ @] DEC BX DEC BX MOV @[ SS: i nexttop :push >next : r ; { ( Because we dereference pointers on the return stack, we must run this from the caller's segment. Copy the definition into the host segment. ) : EACH_ r ; : each ' EACH_ , here 0 , ; immediate : continue ' GOTO_ , dup cell - , ; immediate : next ['] continue here swap ! ; immediate :timm each t, EACH_ patchpt ; : CONTINUE t, GOTO_ dup cell - w>t ; :timm continue CONTINUE ; :timm next CONTINUE patch!t ; } 0 var, cancelled : cancel 1 cancelled ! :| nextdrop get-next if idrop then 0 cancelled ! 0 swap |; >next ; { : break ' cancel , ['] continue ; immediate :timm break t, cancel CONTINUE ; } : nothing :| 0 1 |; >next ; : single >i :| nextdrop :| idrop 0 1 |; >next 1 1 |; >next ; : times ( n -- ) >i :| i 1 then 1 |; >next ; : links ( p -- ) dup if >i :| i 1 then 1 |; >next else nothing then ; : +for? ( n -- f ) i 1 then ; : for ( start lim -- ) >next 1- >i :| 1 +for? 2 |; >next ; : for+ ( start lim inc -- ) >next >next 1 nextpeek - >i :| 2 nextpeek +for? 3 |; >next ; : chars ( st -- ) 1- >i :| i 1 else drop 0 then 1 |; >next ; :asm tail ( TODO: support CREATE words ) LODSW MOV BX AX INC BX INC BX MOV SI BX NEXT : gen-save-args ( extra-args... extra-arg-count -- ) begin dup while swap >next 1- repeat drop ; : gen-save ( 0 0 extra-args... extra-arg-count -- 1 cnext ) >r r@ gen-save-args rot drop drop 1 swap ; : gen-restore ( arg-count -- args... ) begin dup while r rswap ub@ gen-restore ; : _suspend ( cpresume -- ) rswap r@ ub@ swap >r gen-save next >next ; : GENSTART_ next :| ' noop _resume |; >next ; { var gen-arg-count :timm (( t:| t, GENSTART_ gen-arg-count @ >t ; :timm )) t|; t, execute 0 gen-arg-count ! ; : +arg 1 gen-arg-count !+ ; :timm +arg +arg ; : -arg -1 gen-arg-count !+ ; :timm -arg -arg ; :timm >arg t, >next +arg ; : :yield } create immediate target , startcolon does> @ w>t gen-arg-count @ >t ; } :yield yield0 :| ' noop _resume |; _suspend ; :yield yield >i :| ' idrop _resume idrop |; _suspend ; :yield yield> >i :| ' idrop _resume next >i :| :| idrop i cancel-now |; _resume idrop i |; _suspend 1+ ; :yield filter if :| ' cancel-now _resume |; _suspend else r then ; : . i [ key 0 lit ] + draw-char ; : .all each i . next ; : .first each i . break i . next ; : multiple-of >arg (( each dup i swap % not filter next drop )) ; : increased >arg (( each dup i + map next drop )) ; : even+ 2 multiple-of increased ;