library Stack // advanced stack manupulation library "Lists.fif" include // S(a b c - a c 2 a b) would compile to code performing the requested stack manipulation // interface to low-level stack manipulation primitives { (number) 1- abort"index expected" dup 0 < over 255 > or abort"index 0..255 expected" } : (idx) // push(n) : a0 .. an - a0 .. an a0 equivalent to "n pick" // push(0) = dup, push(1) = over { 0 char ) word (idx) } ::_ push( // pop(n) : a0 a1 .. a(n-1) an - an a1 .. a(n-1) // pop(0) = drop, pop(1) = nip { 0 char ) word (idx) } ::_ pop( // xchg(i,j) : equivalent to "i j exch2" { 0 char , word (idx) char ) word (idx) } ::_ xchg( // xchg0(i) : equivalent to "i exch" or "xchg(0,i)" // xchg0(1) = swap { 0 char ) word (idx) 0 } ::_ xchg0( forget (idx) // parser for stack notation expressions ")" 34 hold +" -" constant stk-delims anon constant stk-start anon constant stk-to variable stk-mode { stk-delims 11 (word) } : stk-token 'nop : mk-lit // stk-start vn ... v0 -- stk-start ... v0 i where v[i]=v0 { 0 { 1+ 2dup 2+ pick dup stk-start eq? { 2drop drop 0 true } { eqv? } cond } until } : stk-lookup // stk-start a1 .. an stk-to b1 .. bm -- [a1 .. an] [b1 .. bm] { stk-mode @ 0= abort"identifier expected" } : chk-lit { stk-to list-until-marker stk-mode ! stk-start list-until-marker stk-mode @ stk-mode 0! } : build-stk-effect { stk-start stk-mode 0! { stk-token dup ")" $= { drop true } { dup "-" $= { drop stk-mode @ abort"duplicate -" true stk-mode ! stk-to false } { dup 34 chr $= { chk-lit drop char " word mk-lit false } { dup (number) ?dup { chk-lit 1- { swap mk-lit -rot } if mk-lit nip false } { atom dup `_ eq? { stk-mode @ abort"identifier expected" false } { stk-lookup 0= stk-mode @ = { stk-mode @ { atom>$ +" -?" } { atom>$ +" redefined" } cond abort } { false } cond } cond } cond } cond } cond } cond } until stk-mode @ 0= abort"'-' expected" build-stk-effect } :_ parse-stk-list( // stack operation list construction variable op-rlist { op-rlist null! } : clear-op-list { op-rlist @ list-reverse } : get-op-list { op-rlist @ cons op-rlist ! } : issue-op { minmax `xchg -rot triple } : op-xchg { `push swap pair } : op-push { `lit swap pair } : op-lit { `pop swap pair } : op-pop 0 op-pop constant op-drop { 2dup <> { op-xchg issue-op } if } : issue-xchg { op-push issue-op } : issue-push { op-lit issue-op } : issue-lit { op-pop issue-op } : issue-pop { op-drop issue-op } : issue-drop { ' issue-drop swap times } : issue-drop-# // emulated stack contents variable emul-stk { emul-stk @ count } : emul-depth { emul-depth 1- swap - } : adj-i { emul-depth 1- tuck swap - swap rot - swap } : adj-ij // i j -- { adj-ij 2dup emul-stk @ tuck swap [] swap rot [] rot // i sj si j emul-stk @ -rot []= swap rot []= emul-stk ! } : emul-xchg { emul-stk @ tpop drop emul-stk ! } : emul-drop // i -- { 0 emul-xchg emul-drop } : emul-pop // i -- s[i] { emul-stk @ swap [] } : emul-stk[] // i -- si { adj-i emul-stk[] } : emul-get { 0 emul-get } : emul-tos // v i -- ? Check whether s[i]=v { dup emul-depth < { emul-stk[] eqv? } { 2drop false } cond } : emul[]-eq? // v -- i or -1 Returns maximum i with s[i]=v { emul-stk @ dup count { // v s i ?dup 0= { -1 true } { 1- 2dup [] 3 pick eqv? } cond // v s i' ? } until nip nip } : emul-stk-lookup-rev // i -- { emul-get emul-stk @ swap , emul-stk ! } : emul-push { emul-stk @ swap , emul-stk ! } : emul-lit // show emulated stack contents similarly to .s { emul-stk @ explode dup 1 reverse ' .l swap times cr } : .e // both issue an operation and emulate it { 2dup issue-xchg emul-xchg } : issue-emul-xchg { dup issue-push emul-push } : issue-emul-push { dup issue-lit emul-lit } : issue-emul-lit { dup issue-pop emul-pop } : issue-emul-pop { issue-drop emul-drop } : issue-emul-drop { ' issue-emul-drop swap times } : issue-emul-drop-# // b.. s -- b.. s moves tos value to stk[s] { dup emul-stk[] 2 pick cdr list-member-eqv? { dup adj-i 0 issue-emul-xchg } { dup adj-i issue-emul-pop } cond } : move-tos-to // new s -- ops registered { { over null? not } { // .sl .e get-op-list .l cr // get-op-list list-length 100 > abort"too long" emul-depth over > { over emul-tos swap list-member-eqv? not } { false } cond { // b.. s tos unneeded issue-emul-drop } { over car // b.. s b1 2dup swap emul[]-eq? { drop swap cdr swap 1+ } { dup emul-stk-lookup-rev // b.. s b1 i dup 0< { // b.. s b1 i not found, must be a literal drop dup atom? abort"unavailable value" issue-emul-lit } { dup 3 pick < { // b.. s b1 i found in bottom s stack values nip adj-i issue-emul-push // b.. s dup emul-depth 1- < { move-tos-to } if } { emul-depth 1- over = { // b.. s b1 i found in tos 2drop move-tos-to } { // b.. s b1 i nip over adj-ij issue-emul-xchg } cond } cond } cond } cond } cond } while nip emul-depth swap - issue-emul-drop-# } : generate-reorder-ops // old new -- op-list { emul-stk @ op-rlist @ 2swap swap list>tuple emul-stk ! clear-op-list 0 generate-reorder-ops get-op-list -rot op-rlist ! emul-stk ! } : generate-reorder { parse-stk-list( generate-reorder } :_ SG( // op-list rewriting according to a ruleset // l f l1 l2 -- l' -1 or l f with l' = l2 + (l - l1) { push(3) rot list- { list+ nip nip true } { drop } cond } : try-rule // l f ll -- l' -1 or l f { { dup null? not } { uncons 3 -roll unpair try-rule rot } while drop } : try-ruleset // l ll -- l' { swap { over false swap try-ruleset 0= } until nip } : try-ruleset* // l ruleset -- l' recursive try-ruleset*-everywhere { tuck try-ruleset* dup null? { nip } { uncons rot try-ruleset*-everywhere cons } cond } swap ! LIST( [([xchg 0 1] [xchg 0 2]) ([rot])] [([xchg 0 1] [xchg 1 2]) ([-rot])] [([xchg 0 2] [xchg 1 2]) ([rot])] [([xchg 0 2] [xchg 0 1]) ([-rot])] [([xchg 1 2] [xchg 0 1]) ([rot])] [([xchg 1 2] [xchg 0 2]) ([-rot])] [([xchg 0 1] [rot]) ([xchg 0 2])] [([-rot] [xchg 0 1]) ([xchg 0 2])] [([xchg 0 2] [xchg 1 3]) ([2swap])] [([xchg 1 3] [xchg 0 2]) ([2swap])] [([push 1] [push 1]) ([2dup])] [([push 3] [push 3]) ([2over])] [([pop 0] [pop 0]) ([2drop])] [([pop 1] [pop 0]) ([2drop])] [([xchg 0 1] [push 1]) ([tuck])] [([rot] [-rot]) ()] [([-rot] [rot]) ()] ) constant fift-stack-ruleset { fift-stack-ruleset try-ruleset*-everywhere } : fift-ops-rewrite { SG( fift-ops-rewrite } :_ SGF( // helpers for creating Fift source strings for one fift-op // i j -- s { minmax over { "xchg(" rot (.) $+ +"," swap (.) $+ +")" } { nip dup 1 = { drop "swap" } { ?dup { "xchg0(" swap (.) $+ +")" } { "" } cond } cond } cond } : source- // i -- s { dup 1 = { drop "over" } { ?dup { "push(" swap (.) $+ +")" } { "dup" } cond } cond } : source- // i -- s { dup 1 = { drop "nip" } { ?dup { "pop(" swap (.) $+ +")" } { "drop" } cond } cond } : source- // lit -- s { dup string? { char " chr swap $+ char " hold } { (.) } cond } : source- // dictionary with all fift op compilation/source creation { 0 swap (compile) } : fop-compile ( _( `xchg 2 { fop-compile } { source- swap cons } ) _( `push 1 { fop-compile } { source- swap cons } ) _( `pop 1 { fop-compile } { source- swap cons } ) _( `lit 1 { 1 'nop (compile) } { source- swap cons } ) _( `rot 0 { ' rot fop-compile } { "rot" swap cons } ) _( `-rot 0 { ' -rot fop-compile } { "-rot" swap cons } ) _( `tuck 0 { ' tuck fop-compile } { "tuck" swap cons } ) _( `2swap 0 { ' 2swap fop-compile } { "2swap" swap cons } ) _( `2drop 0 { ' 2drop fop-compile } { "2drop" swap cons } ) _( `2dup 0 { ' 2dup fop-compile } { "2dup" swap cons } ) _( `2over 0 { ' 2over fop-compile } { "2over" swap cons } ) ) box constant fift-op-dict { dup atom? { atom>$ } { drop "" } cond "unknown operation " swap $+ abort } : report-unknown-op variable 'fop-entry-exec // process fift-op according to 'fop-entry-exec // ... op - ... { dup first dup fift-op-dict @ assq { report-unknown-op } ifnot dup second 1+ push(3) count <> abort"incorrect param count" nip swap explode dup roll drop 1- roll // o2 .. on entry 'fop-entry-exec @ execute } : process-fift-op // compile op-list into Fift wordlist // wl op-list -- wl' { { third execute } 'fop-entry-exec ! swap ' process-fift-op foldl } : compile-fift-op* // op-list -- e { fift-ops-rewrite ({) swap compile-fift-op* (}) } : ops>wdef // S( - ) compiles a "word" performing required action { SG( ops>wdef 0 swap } ::_ S( // 1 2 3 S(a b c - c a b a) .s would print 3 1 2 1 // transform op-list into Fift source // ls op -- ls' { fift-ops-rewrite { 3 [] execute } 'fop-entry-exec ! null ' process-fift-op foldl dup null? { drop "" } { { +" " swap $+ } foldr-ne } cond } : ops>$ { SG( ops>$ 1 'nop } ::_ $S( { SG( ops>$ type } :_ .$S( // $S(a b c - b c a c a c) => string "rot 2dup over" // S(a b c - b c a c a c) => compile/execute block { rot 2dup over } // $S(_ x y _ - y x) => string "drop pop(2)" // .$S(x1 x2 - 17 x1) => print string "drop 17 swap" // simplify/transform sequences of stack manipulation operations LIST(. [a b c d e f g h i j]) constant std-stack { stk-start std-stack explode drop stk-to std-stack explode drop } : simplify<{ { build-stk-effect generate-reorder ops>$ } : }>stack // simplify<{ drop drop over over -13 }>stack => string "2drop 2dup -13" // simplify<{ 17 rot }>stack => string "swap 17 swap" // simplify<{ 5 1 reverse }>stack => string "xchg(1,5) xchg(2,4)"