⇐ STABLE-INDEX | ⇒ tutorial ⇒ glossar ⇒ essay cook book |
I believe in the simplicity and power of stack programming. It takes some time to getting used to, but then it is extreme powerful. The stack is there to communicate between functions, so only the arguments should be there. Maybe one or two calculated termporarly values. Stackoperations are only for accessing TOS (top of stack) and NOS (next of stack). You can't access the 3rd element easily. If you are in this situation then rethink about the stack flow. If you have to do stack gymnastics (#\@$) then rethink about you stack usage. Use registers or global memory to store values more permanentely. It is not C (or C like) where all variables are given as arguments.
For an example you can look at block 99 which contains
an obvious implementation of a command shell. I will comment it here.
For more information see block 0 which contains an index
for all blocks, block 8 for Terminal ANSI sequences and
block 9 for the instruction set documentation.
Note that the following code is slightly older, blocks are
code blocks, data blocks (persistence) is missing here.
In variable k we are holding numbers entered from the keyboard. We try to keep k as long as it is possible but a loaded program might change the value to be invalid, so we check the validity of k. k select k as current variable ;0<(0:) trim k to zero if it is less then 0 ;1000>(0:) trim k to zero if it is greater then 1000 Enter endless loop 1[ k again, select k as current variable Display the menu. For a hierarchy of menues put each submenu into a different block and load them 13,"esc=retry q=quit l=load e=edit c=cls, h=home(block 0)"10, {E27,"[K"} This loop is reading and displaying the current block number from keyboard, until a non number is pressed. The number built so far is stored in variable k. The key which leads to an exit is on top of stack. 1[\13,k;."> "E^#47>@58<&(48-;10*+:0)#0=] #27=(0:) if last key was ESC reset k to 0 #`l=(;8') load (and run) block stored in k #`e=(;7') edit block #`h=(0#:7') set block to 0 and edit it #`q=("quit"10,0 11') quit STABLE #`c=(27,"c") clear screen (ANSI sequence) ]
Mind that you can combine these values with not (~) which reverse true or false. True must be -1 and false 0.
OR AND -1 -1 -1 -1 -1 0 -1 0 0 -1 -1 0 0 0 0 -1A typical example is to combine a loop counter and a value. The loop is finished if either the loop counter has become 0 or a specific value has occurred. Note that a loop counter not equal zero is not -1 and hence not valid for logical-binary operation. To get a valid boolean simply compare it agains zero (0=), then you get -1 if the loop counter ist zero and 0 if it is not
flow1: key is blank, terminate. counter is 0 terminate: terminate flow2: key is blank, terminate. counter is != 0 continue: terminate flow3: key is not blank, continue. counter is 0 terminate: terminate flow4: key is not blank, continue. counter is != 0 continue: continue 1[ flow 1 flow 2 flow 3 flow 4 ^32= ( -1) ( -1) ( 0) ( 0) We want to terminate the loop if 32 has arrived, negate the logic ~ ( 0) ( 0) ( -1) ( -1) a-; ( 0 0) ( 0 n) ( -1 0) ( -1 n) 0= ( 0 -1) ( 0 0) ( -1 -1) ( -1 0) We want to exit the loop if the counter is zero. But if not the flag must become -1 not the current value of the counter ~ ( 0 0) ( 0 -1) ( -1 0) ( -1 -1) & ( 0) ( 0) ( 0) ( 1) ] flow 4 is the only flow which continues the loopNote this idioms:
The IF and ELSE part are working together. The IF part has to left -1 on TOS (top of stack). So with ~ (not) the ELSE part will be skipped.
if/else
1. the value to check is either not zero => if part, or zero => else part We don't need the value for the else part, because it is always zero. So we can save one stack cell. operation stack picture not zero stack picture zero ( value) ( 0) # ( value value) ( 0 0) ( ( value) ( 0) "HELLO" ( value) skipped \1_ ( -1) skipped ) ( -1) ( 0) ~ ( 0) ( -1) ( ( ) ( ) "BYE" skipped ( ) ) in short #("HELLO"\1_)~("BYE") 2. the value on top must be compared Here we need one more stack entry, because we need the value in the else part as well. operation stack picture cmp true stack picture cmp false ( val1) ( val2) # ( val1 val1) ( val2 val2) 32> ( val1 -1) ( val2 0) # ( val1 -1 -1) ( val2 0 0) ( ( val1 -1) ( val2 0) @ ( val1 -1 val1) skipped . ( val1 -1) skipped ) ( val1 -1) ( val2 0) ~ ( val1 0) ( val2 -1) ( ( val1) ( val2) "is " skipped ( val2) . skipped ( ) ) in short: #32>#(@.)~("is ".) 3. nested if conditions Evaluate the next condition only if needed. If one condition evaluates to false, in an and condition series, we can stop the whole cascade, letting the value on stack to know which item failed the test. A 0 value show that all tests are passed successfully. Note that the conditions must be in tail position and one closing bracket is enough. To keep STABLE small ( parses only the next ). Example: ^#32=( ( key1 -1) ( key1 0) ( key1 -1) \#32=( ( key2 -1) skipped ( key2 0) \^#32=("SUPER"\0) ( key3 -1) skipped skipped ( 0) ( key1) ( key2) 0= for booelan ( -1) ( 0) ( 0)switch/case Instead of having an else part which inverse the comparation we are doing a comparation for each case
operation stack effekt true stack effekt false comment ^ ( key) ( key) read key # ( key key) ( key key) dup 27= ( key -1) ( key 0) is ESC key? ( ( key) ( key) # ( key key) skipped . ( key) skipped display key value ) ( key) ( key) # ( key key) ( key key) dup 65= ( key 0) ( key -1) is A key? ( ( key) ( key) # skipped ( key key) , skipped ( key) display key ) ( key) ( key) in short: ^ #27=(#.) #65=(#,) #`Q=("Letter Q pressed"10,) \ drop key from stackswitch/case/default
^ read key, push on stack ( key) #27=("ESC pressed"10,\0) change value to 0 #65=("Letter A pressed"10,\0) change value to 0 #0=("Default action"10,) \ drop key from stack
Now we are reading up to 4 characters from input port
(stdin). Note that each character would be processed
immediately. No [ENTER] key is needed, key will
not be displayed.
Stack effekt: ( --keys f) if f is true then
all 4 chars were read, false (0) otherwise. With this
a loop which places all the cells into memory should
be simple. For simplicity, I would limit the number of cells
to a fixed size.
4 nested conditions, observe that we only have one ) at tail position. This is the only way in STABLE to nest conditions! 0^#32>($256*+^#32>($256*+^#32>($256*+^#32>($256*+0)0=For outputting the chars
keys value on top of stack {Q#255&$256/} extract one byte {R#(,1_)~()}print out byte if not 0 {P define function P QQQ extract 4 bytes from 33 bits cell on stack RRRR print out each character } P print up to 4 chars, keys top of stack
operation stack effekt stack effekt stack effekt 1000 ( 1000) ( 0) ( 1) [ ( 1000) ( 0) ( 1) 1- ( 999) skipped ( 0) # ( 999 999) skipped ( 0 0) ] ( 999) REPEAT ( 0) ( 0) END LOOP
On the STABLE IDE you can select the block number with <number>b and display the content with dump. Note that the first persistent cell starts at 1000, so you have to enter: <1000>d.
To store values the only way is through registers. In this example we are using register a.
100 9'"loading block 100" 1000a:100!"store 100 into first cell of block 100"Now leave STABLE and start it again (or change the block number with the STABLE-IDE, go to block 100 and inspect the value
In the STABLE IDE enter 1e this starts the editor with code block 1 100 9'1000a:100! enter the code, save and exit editor Back to STABLE IDE l load and run the code block 1 100b1000d select block 100 and dump at 1000 1000> 1000: 100 0 0 0 0 1005: 0 0 0 0 0 1010: 0 0 0 0 0 1015: 0 0 0 0 0 Switching back to block 0 <ESC>b1000d select block 0 and dump at 1000 1000> 1000: 0 0 0 0 0 1005: 0 0 0 0 0 1010: 0 0 0 0 0 1015: 0 0 0 0 0 100b1000d select block 100 again and dump at 1000 1000> 1000: 100 0 0 0 0 1004: 0 0 0 0 0 1010: 0 0 0 0 0 1015: 0 0 0 0 0See video demonstation (this video show the address with 1024 instead of the newer 1000. The use 1000 based system is more convenient in STABLE) here (12M)