There are many possible models for describing an actor's behavior. One simple model is an imperative stack-oriented machine with a dictionary (similar to FORTH).
Program source is provided as a stream of words (whitespace separated in text format). If the word parses as a number the value is pushed on the data stack. Otherwise the word is looked up in the current dictionary. If the associated value is a block it is executed, otherwise the value is pushed on the data stack. The data stack holds parameters for executing blocks and their return values. Some blocks also consume words from the source stream.
An actor's behavior is described with a block.
The message received by the actor is the contents of the data stack.
The SEND
primitive sends the current stack contents,
clearing the stack.
Values may be saved in the dictionary
by binding them to a word.
All dictionary changes are local to the executing behavior.
Blocks are essentially functional closures. Executing a word definition is like applying the function to the parameters on the stack (consuming them). Result values (if any) are left on the stack.
The data stack contains universal integer values, usually interpreted as signed 2's-complement numbers. Numeric operations do not overflow, but rather wrap around forming a ring, which may be interpreted as either signed or unsigned. The number of bits is not specified.
The quartet program TRUE 1 LSR DUP NOT .? .?
prints the minimum and maximum signed value representations.
Program source is represented by only two types, Words and Numbers. Values on the data stack can have richer types. Stack values can be Words, Numbers, Blocks, Procedures, and Actors (Boolean values are just Numbers). Any value that can appear on the stack can also be bound to a Word in the dictionary.
The following primitive definitions are assumed to be present in the initial dictionary.
Input | Operation | Output | Description |
---|---|---|---|
block | CREATE |
actor | Create a new actor with block behavior |
...message actor | SEND |
— | Send message to actor |
block | BECOME |
— | Replace current actor's behavior with block |
— | SELF |
actor | Push the current actor's address on the data stack |
— | FAIL |
— | Abort processing and revert to prior state |
— | STEP |
bool | Dispatch an actor message, report success |
— | RUN |
— | Dispatch all actor messages |
Input | Operation | Output | Description |
---|---|---|---|
value | = word |
— | Bind value to word in the current dictionary |
— | ' word |
word | Push (literal) word on the data stack |
— | @ word |
value | Lookup value bound to word in the current dictionary |
— | [ ... ] |
block | Create block (quoted) value |
[ ... | ( ... ) |
[ ... value | Immediate (unquoted) value |
Input | Operation | Output | Description |
---|---|---|---|
— | TRUE |
TRUE | All bits set (1) |
— | FALSE |
FALSE | All bits clear (0) |
value | ZERO? |
bool | TRUE if value = 0; otherwise FALSE |
bool | IF [ ] |
— | Execute block based on condition |
bool | IF-ELSE [ ] [ ] |
— | Choose block based on condition |
bool | WHILE [ ] |
— | Execute block while condition holds |
Input | Operation | Output | Description |
---|---|---|---|
v | DROP |
— | Drop the top element |
v | DUP |
v v | Duplicate the top element (same as 1 PICK ) |
v2 v1 | SWAP |
v1 v2 | Swap the top two elements (same as 2 ROLL ) |
vn ... v1 n | PICK |
vn ... v1 vn | Duplicate element n |
vn ... v1 n | ROLL |
vn-1 ... v1 vn | Rotate stack elements (negative for reverse) |
— | DEPTH |
n | Number of items on the data stack |
Input | Operation | Output | Description |
---|---|---|---|
— | INF |
INF | Infinity/Undefined (only MSB set) |
n | NEG |
-n | Numeric negation (2's complement) |
n m | ADD |
n+m | Numeric addition |
n m | SUB |
n-m | Numeric subtraction |
n m | MUL |
n*m | Numeric multiplication |
n m | DIVMOD |
r q | Numeric division/modulus |
c b a | FMA |
a*b+c | Fused multiply/add |
n m | COMPARE |
n-m | Compare numeric values |
n | LT? |
bool | TRUE if n < 0; otherwise FALSE |
n | EQ? |
bool | TRUE if n = 0; otherwise FALSE |
n | GT? |
bool | TRUE if n > 0; otherwise FALSE |
n | NOT |
~n | Bitwise not (inversion) |
n m | AND |
n&m | Bitwise and |
n m | OR |
n|m | Bitwise or |
n m | XOR |
n^m | Bitwise xor |
n m | LSL |
n<<m | Logical shift left |
n m | LSR |
n>>m | Logical shift right |
n m | ASR |
n>>m | Arithmetic shift right (sign-extend) |
Input | Operation | Output | Description |
---|---|---|---|
address | ? |
value | Load value from address |
value address | ! |
— | Store value into address |
address | ?? |
value | Atomic load value from volatile address |
value address | !! |
— | Atomic store value into volatile address |
address count | DUMP |
— | Hex dump count values from address |
Input | Operation | Output | Description |
---|---|---|---|
— | WORDS |
— | Print list of defined words |
— | USAGE |
— | Print resource usage report |
code | EMIT |
— | Print ascii character code |
— | ... |
— | Print stack contents (non-destructive) |
value | .? |
— | Print internal representation of value |
value | . |
— | Print value |
Print countdown from 5 to 1
5 DUP GT? WHILE [ DUP . 1 SUB DUP GT? ] DROP
Clear the stack
# ... CLEAR --
[ DEPTH GT? WHILE [ DROP DEPTH GT? ] ] = CLEAR
Print symbol based on numeric sign
# n CMP --
[ DUP EQ? IF-ELSE [ DROP ' = . ] [ DUP LT? IF [ ' < . ] GT? IF [ ' > . ] ] ] = CMP
Recursive factorial
# n fact n!
[ DUP ZERO? IF-ELSE [ DROP 1 ] [ DUP 1 SUB fact MUL ] ] = fact
Mutually-recursive even/odd
# n even? bool
[ DUP ZERO? IF-ELSE [ DROP TRUE ] [ 1 SUB odd? ] ] = even?
# n odd? bool
[ DUP ZERO? IF-ELSE [ DROP FALSE ] [ 1 SUB even? ] ] = odd?
Curried function constructor
# n \n+ block
[ = n [ n ADD ] ] = \n+
+1 \n+ = 1+ # increment
-1 \n+ = 1- # decrement
Actor that displays any message it receives
[ ... 10 EMIT ] CREATE = println
' Hello ' World println SEND
STEP .
Actor that prints the number of messages it receives
[ = n [ n 1 ADD DUP . count_beh BECOME ] ] = count_beh
0 count_beh CREATE = counter
counter SEND
counter SEND
counter SEND
RUN
[ ] = sink_beh
@ sink_beh CREATE = sink
[ # cust
UART0_RXDATA ?? # read UART receive fifo status/data
DUP LT? IF-ELSE [
DROP
SELF SEND # retry
] [
16#FF AND SWAP SEND
]
] DUP = serial_read_beh CREATE = serial_read
[ # cust octet
UART0_TXDATA ?? # read UART transmit fifo status
LT? IF-ELSE [
SELF SEND # retry
] [
UART0_TXDATA !! # write UART fifo data
SELF SWAP SEND
]
] DUP = serial_write_beh CREATE = serial_write
[ # octet
DUP = octet 16#0D COMPARE
EQ? IF [
' Stop dispatcher SEND
]
SELF octet serial_write SEND
@ serial_busy_beh BECOME
] = serial_echo_beh
[ # $serial_write
serial_write COMPARE
EQ? IF [
SELF serial_read SEND
@ serial_echo_beh BECOME
]
] = serial_busy_beh
@ serial_echo_beh CREATE = serial_echo
[ # output
= output
[ # cust octet
SWAP = cust
SELF SWAP output SEND
SELF cust SEND
output empty-Q serial_buffer_beh BECOME
]
] = serial_empty_beh
[ # output queue
= queue
= output
[ # $output | cust octet
DUP output COMPARE
EQ? IF-ELSE [ # $output
DROP
queue Q-empty IF-ELSE [
output serial_empty_beh BECOME
] [
queue Q-TAKE
output SWAP serial_buffer_beh BECOME
SELF SWAP SEND
]
] [ # cust octet
queue Q-PUT
output SWAP serial_buffer_beh BECOME
SELF SWAP SEND
]
]
] = serial_buffer_beh
serial_write serial_empty_beh CREATE = serial_buffer
serial_echo serial_read SEND # start echo listen-loop