Copyright 2007, 2020 Anthony Howe. All rights reserved.
Post4 is a hosted indirect threaded Forth dialect written in C, based on the "Forth 200x Draft 18.1, 2018-08-27". Post4 aims to implement the fewest possible built-in words in C, those that are needed to interact with memory and I/O, leaving the remaining standard words to be implemented in Forth.
usage: post4 [-V][-b file][-c file][-d size][-i file][-r size] [script [args ...]]
-b file block file; default ./.post4.blk or $HOME/.post4.blk
-c file word definition file; default post4.p4 from $POST4_PATH
-d size data stack size in cells; default 64
-i file include file; can be repeated; searches $POST4_PATH
-r size return stack size in cells; default 64
-V build and version information
If script is "-", read it from standard input.
The environment variable POST4_PATH
provides a colon separated search path for the post4.p4
core word definitions file. If POST4_PATH
is undefined, then an OS specific default path is used. A specific word definition file can be specified with -c
.
By default a user block file, .post4.blk
, is opened from the current directory or user's HOME
directory. This can be overridden with the -b
option.
Post4 reads input from standard input and writes to standard output, which can be redirected:
echo "123 69 + ." | post4
Post4 cell size is equivalent to C's intptr_t
, for current systems are either 32 or 64 bit values. Therefore Post4 (currently) treats d
and ud
types same as n
and u
.
Simple demostration on how to access the command line arguments.
$ post4 dumpargs.p4 hello world, tell us more!
There are actually three block editor word sets:
- An
ed(1)
like block line editor usingLIST
,PRINT
, andCHANGE
. - An interactive single line editor using
EDIT
; left & right cursor keys, tab toggles insert or replace mode, delete backspace, ESC quits. - A full "screen" block editor using
ED
; all the commands of the interactive single line editor, plus up & down cursor keys,CTRL+G
goto block,CTRL+P
&CTRL+N
block, andESC
menu.
NOTE: that EDIT
and ED
are hard coded with ANSI terminal escape sequences.
$ p4
ok INCLUDE ed.p4
ok ED
life.p4 - Conway's Game of Life
Rules
The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, live or dead. Every cell interacts with its eight neighbours, which are the cells that are directly horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur:
- Any live cell with fewer than two live neighbours dies, as if caused by under population.
- Any live cell with more than three live neighbours dies, as if by overcrowding.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any dead cell with exactly three live neighbours becomes a live cell.
The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed - births and deaths happen simultaneously, and the discrete moment at which this happens is sometimes called a tick (in other words, each generation is a pure function of the one before). The rules continue to be applied repeatedly to create further generations.
$ post4 life.p4
Pseudo-random number generators. One is the simple code example from the ISO C11 draft April 12, 2011,
\ Example from the ISO C11 draft April 12, 2011
SRAND ( -- aaddr )
Random number seed variable.
RAND ( -- u )
Return random number between 0..32767.
\ Pseudo-Random Sequence Generator for 32-Bit CPUs
\ Bruce Schneier, Dr. Dobb's Journal, v. 17, n. 2, February 1992, pp. 34-40.
RANDA ( -- aaddr )
RANDB ( -- aaddr )
RANDC ( -- aaddr )
Random number seed variables for three Linear Feedback Shift
Registers.
RANDOMXOR ( bits -- u )
Return random number u generated from bits, XORing the bits from the
three generators.
RANDOMMAJ ( bits -- u )
Return random number u generated from bits, based on the majority of
one or zero returned from the three generators.
Example:
$ post4
ok incl rand.p4
ok 12345 srand !
ok rand . rand . rand . CR
21468 9988 22117
ok 12345 randa ! 54321 randb ! $deadbeef randc !
ok 16 randomxor U. 16 randomxor U. 16 randomxor U. CR
52917 27383 64651
ok 16 randommaj U. 16 randommaj U. 16 randommaj U. CR
7 9885 36906
ok
( x
aaddr
-- )
Store x
at aaddr
.
( ud1
-- ud2
)
Divide ud1
by the number in BASE
giving the quotient ud2
and the remainder n
. (n
is the least significant digit of ud1
.) Convert n
to external form and append the resulting character to the pictured numeric output string.
( xd
-- caddr
u
)
Drop xd
. Make the pictured numeric output string available as a character string. caddr
and u
specify the resulting character string. A program may replace characters within the string.
( ud1
-- 0
)
Convert one digit of ud1
according to the rule for #
. Continue conversion until the quotient is zero (0).
( <spaces>name
-- xt
)
Find name
and place its execution token on the stack. Throw undefined word (-13) if not found.
( ccc<paren>
-- ) immediate
Parse and ignore characters up to the closing right parenthesis, which an empty string or span multiple lines.
( n1|u1
n2|u2
-- n3|u3
)
Multiply the top two stack values.
( n1
n2
dsor
-- quot
)
Multiply n1
by n2
then divide by divisor dsor
giving the quotient quot
.
( n1
n2
dsor
-- rem
quot
)
Multiply n1
by n2
then divide by divisor dsor
giving the remainder rem
and quotient quot
.
( n1|u1
n2|u2
-- n3|u3
)
Add the top two stack values.
( n|u
aaddr
-- )
Add n|u
to the cell at aaddr
.
( addr
-- addr'
)
Define unaligned structure field name
, which when executed adds the field offset to addr
giving addr'
.
Structure name defined last:
0 \ initial total byte count
1 CELLS +FIELD p.x \ single cell field named p.x
1 CELLS +FIELD p.y \ single cell field named p.y
CONSTANT point \ save structure size
Structure name defined first:
BEGIN-STRUCTURE point \ create the named structure
1 CELLS +FIELD p.x \ A single cell filed named p.x
1 CELLS +FIELD p.y \ A single cell field named p.y
END-STRUCTURE
( step
-- )
Add step
to the loop index. If the loop index did not cross the boundary between the loop limit minus one and the loop limit, continue execution at the beginning of the loop. Otherwise, end the current loop and continue execution immediately following the loop.
: SOMEWORD ... limit first DO ... step +LOOP ... ;
( x
-- )
Align and reserve one cell of data-space and store x
there.
( n1|u1
n2|u2
-- n3|u3
)
Subtract the top two stack values.
( caddr
u
-- caddr
u'
)
Trim trailing spaces from end of string caddr
u
.
( n
-- )
Display n
in free field format.
( ccc<quote>
-- )
Display ccc
.
( ccc<paren>
-- ) immediate
Parse and display text until a closing parenthesis.
( dend
dsor
-- quot
)
Divide the dividend dend
by the divisor dsor
leaving the symmetric quotient quot
on top of the stack. This is the same as the SM/REM
quotient.
( dend
dsor
-- rem
quot
)
Divide the dividend dend
by the divisor dsor
leaving the remainder rem
and quotient quot
on top the stack. This is the same as the SM/REM
remainder.
( caddr
u
n
-- caddr'
u'
)
Adjust the character string at caddr u
by offset n
characters, by adding n
to caddr
and subtracting n
from length u
.
( n
-- bool
)
bool
is true if and only if n
is less than zero (0).
( nu
-- bool
)
bool
is true if nu
is not equal to zero (0).
( nu
-- bool
)
bool
is true if nu
is greater to zero (0).
( x
-- bool
)
bool
is true if and only if x
is equal to zero (0).
( n1|u1
-- n2|u2
)
Add one (1) to n1|u1
giving the sum n2|u2
.
( n1|u1
-- n2|u2
)
Subtract one (1) from n1|u1
giving the difference n2|u2
.
( lo
hi
aaddr
-- )
Store the cell pair lo hi
at aaddr
as hi lo
.
( x1
-- x2
)
x2
is the result of shifting x1
one bit toward the most-significant bit, filling the vacated least-significant bit with zero (0).
( x1
-- x2
)
x2
is the result of shifting x1
one bit toward the least-significant bit, leaving the most-significant bit unchanged.
( aaddr
-- lo
hi
)
Fetch the cell pair hi lo
stored at aaddr
and place on the stack as lo hi
.lo hi.
( x1
x2
-- )
Drop cell pair x1 x2
from the stack.
( x1
x2
-- x1
x2
x1
x2
)
Duplicate cell pair x1 x2
.
( x1
x2
x3
x4
-- x1
x2
x3
x4
x1
x2
)
Copy cell pair x1 x2
to the top of the stack.
( x1
x2
x3
x4
-- x3
x4
x1
x2
)
Exchange the top two cell pairs.
( lo
hi
<spaces>name
-- )
Create name
with two cells of data assigned hi
and lo
. When name
is executed, the values lo
hi
are pushed to the stack. See TO
.
Start definition of word name
. The current definition shall not be findable in the dictionary until it is ended (or until the execution of DOES>
).
End definition of word.
( n1
n2
-- bool
)
bool
is true if and only if n1
is less than n2
.
( -- )
Initialise the pictured numeric output conversion process.
( x1
x2
-- bool
)
bool
is true if and only if x1
is bit-for-bit the same as x2
.
( n1
n2
-- bool
)
bool
is true if and only if n1
is greater than n2
.
( xt
-- aaddr
)
aaddr
is the data-field address corresponding to xt
. Will throw -31
if xt
is not a word defined by CREATE
.
( -- aaddr
)
aaddr
is the address of a cell containing the offset in characters from the start of the
input buffer to the start of the parse area.
( x
-- )(R: -- x
)
Move top of the data stack to the return stack.
( aaddr
-- )
Display the value stored at aaddr
.
( n1|u1
n2|u2
-- ) (R: -- loop-sys
)
Mark the start of ?DO ... +LOOP
or ?DO ... LOOP
.
( x
-- x
x
)
Duplicate x
if it is non-zero.
( -- )
: SOMEWORD ... limit first DO ... test ?LEAVE ... LOOP ... ;
( aaddr
-- x
)
Fetch from aaddr
the value x
stored there.
( i*x
-- ) ( R: j*x
-- )
Throw -1. Clear the data stack and perform a QUIT
, which clears the return stack too.
( i*x
x1
-- | i*x
) ( R: j*x
-- | j*x
)
If x1
is not equal to zero (0), display the message that follows up to the closing double-quote and perform an ABORT
.
( n
-- u
)
Return the absolute value of n
.
( <spaces>name
-- xt
)
Return the execution token xt
of a deferred name
. See DEFER
.
( caddr
+n1
-- +n2
)
Accept up to +n1
(between 1 and +32767) characters into the buffer given by caddr
; the number of characters actually received, +n2
, is pushed.
( -- )
Loop back to matching BEGIN
indefinitely.
BEGIN
\ loop body
AGAIN
( -- )
Jump ahead over a block of code. A building block for several flow control words.
AHEAD
\ not executed
THEN
\ continue
( -- )
If the data-space pointer is not aligned, reserve enough space to align it.
( addr
-- aaddr
)
aaddr
is the first cell aligned address greater than or equal to addr
.
( n
-- )
If n
is greater than zero (0), reserve n
address units of data space. If n
is less than zero (0), release |n|
address units of data space. If n
is zero (0), leave the data-space pointer unchanged.
( x1
x2
-- x3
)
Bit-wise and of the top two stack values.
( col
row
-- )
Position cursor on the terminal, row
and col
are 0 based.
( -- aaddr
)
aaddr
is the address of a cell containing the current number conversion radix, between 2..36.
( -- )
Mark the start of BEGIN ... AGAIN
or BEGIN ... WHILE ... REPEAT
loops.
( <spaces>name
-- addr
0
)
( -- char
) constant
char
is the character value for a space, since Forth words are delimited by space. A space can also be specified with '\s'
.
( -- aaddr
)
aaddr
is the address of a cell containing zero (0) or the number of the mass-storage block being interpreted. If BLK
contains zero (0), the input source is not a block and can be identified by SOURCE-ID
. If a program alters BLK
, the results are undefined.
( blk_num
-- aaddr
)
BLOCK
assigns a block buffer, aaddr
, writing the old contents to mass storage if dirty, before reading the new contents of block blk_num
from mass storage. The block buffer pointed to by aaddr
is now the current block buffer assigned to blk_num
.
( blk_num
-- aaddr
)
BUFFER
assigns a block buffer, aaddr
, writing the old contents to mass storage if dirty, but does not read anything. The block buffer pointed to by aaddr
is now the current block buffer assigned to blk_num
.
( char
caddr
-- )
Store char
at caddr
.
( ccc<quote>
-- caddr
)
When interpreting, copy the string ccc
as-is to a transient buffer and return counted string caddr
. When compiling, append the string ccc
as-is to the current word so when executed it leaves the counted string caddr
on the stack. See also S"
.
( char
-- )
Reserve one character of data space and store char
there.
( caddr
-- char
)
Fetch from caddr
the character char
stored there.
( -- )
CASE
test1 OF ... ENDOF
...
testN OF ... ENDOF
default action
ENDCASE
( i*x
xt
-- j*x
0
| i*x
n
)
Push an exception frame on the exception stack and then execute the execution token xt
(as with EXECUTE
) in such a way that control can be transferred to a point just after CATCH
if THROW
is executed during the execution of xt
.
If the execution of xt
completes normally (ie. the exception frame pushed by this CATCH
is not popped by an execution of THROW
) pop the exception frame and return zero (0) on top of the data stack, above whatever stack items would have been returned by xt EXECUTE
. Otherwise, the remainder of the execution semantics are given by THROW
.
( aaddr1
-- aaddr2
)
Add the size in address units of a cell to aaddr1
giving aaddr2
.
( n1
-- n2
)
n2
is the size in address units of n1
cells.
( addr
-- addr'
)
Define character structure field name
, which when executed adds the field offset to addr
giving addr'
.
( <spaces>ccc
-- char
)
Parse text
placing the first character of text on the stack.
( caddr1
-- caddr2
)
Add the size in address units of a character.
( n1
-- n2
)
n2
is the size in address units of n1
characters.
( src
dst
u
-- )
( src
dst
u
-- )
( caddr1
u1
caddr2
u2
-- n
)
Compare the two strings lexicographically. Return n
as 1, zero (0), or -1, according to whether the string caddr1
u1
is greater than, equal to, or less than the string caddr2
u2
.
( -- )
( x
<spaces>name
-- )
Define the word name
to represent the constant value x
. When name
is executed, the value x
is pushed on to the stack.
( caddr1
-- caddr2
u
)
Return the character string caddr2 u
specification for the counted string caddr1
.
( -- )
Write a newline to standard output.
( <spaces>name
-- )
Create a new word definition. When name
is executed, the aaddr
of its data space is pushed onto the stack. CREATE
can be used to define new data structure, eg.
\ Define how to make an array.
: array ( n "<spaces>name" -- ) CREATE CELLS ALLOT ;
\ Define a new array of 3 cells.
3 array fred
\ Store 123 into fred[1].
123 fred 1 CELLS + !
Or when combined with DOES>
make new "defining" words, eg.
\ General structure:
: word1 CREATE ( build data space of word2 ) DOES> ( actions applied to data of word2 ) ;
\ Make a new defining word CONSTANT.
: CONSTANT CREATE , DOES> @ ;
\ Use CONSTANT to define a word representing a value.
377 CONSTANT monaco
\ Use the new word.
monaco ( -- 377 )
( xu
xu-1
... x1
x0
u
-- xu
xu-1
... x1
x0
xu
)
Remove u
. Copy the xu
to the top of the control-stack. 0 CS-PICK
equivalent to DUP
, 1 CS-PICK
equivalent to OVER
.
( xu
xu-1
... x0
u
-- xu-1
... x0
xu
)
Left rotate the control-stack u
cells.
( -- )
Set the numeric conversion radix to ten (decimal).
( <spaces>name
-- )
Create a definition for name
, which when executed will execute the saved execution token; default ABORT
. (Think indirect word execution.)
ok DEFER foo
ok foo
-1 thrown: ABORT
ok : hello S\" Hello world! woot woot\n" TYPE ;
ok ' hello IS foo
ok foo
Hello world! woot woot
ok ACTION-OF foo EXECUTE
Hello world! woot woot
ok
( xt2
xt1
-- )
Set the deferred word xt1
to execute xt2
when called.
( xt1
-- xt2
)
Get the execute token xt2
stored by deferred word xt1
.
( -- u
)
Number of cells on the data stack before u
was placed there.
( n1|u1
n2|u2
-- ) (R: -- loop-sys
)
Mark the start of DO ... +LOOP
or DO ... LOOP
.
Define the execution semantics for the most recently defined word by CREATE
. Throws -31 if DOES>
is applied to a word not defined by CREATE
.
\ General structure:
: word1 CREATE ( build data space of word2 ) DOES> ( actions applied to data of word2 ) ;
\ Make a new defining word CONSTANT.
: CONSTANT CREATE , DOES> @ ;
\ Use CONSTANT to define a word representing a value.
377 CONSTANT monaco
\ Use the new word.
monaco ( -- 377 )
( x
-- )
Remove the top of the stack.
( addr
u
-- )
Display the contents of u
consecutive addresses starting at addr
.
( x
-- x
x
)
Duplicate x
.
( -- )
test IF
\ execute for true
ELSE
\ execute for false
THEN
\ continue
( char
-- )
Write the character octet to standard output.
( -- )
Mark all block buffers as free without saving any dirty buffers.
( addr
0
-- )
Terminate definition of a structure started by BEGIN-STRUCTURE
.
( x
--- )
Discard the case selector x
and continue execution.
CASE
test1 OF ... ENDOF
...
testN OF ... ENDOF
default action
ENDCASE
( -- )
CASE
test1 OF ... ENDOF
...
testN OF ... ENDOF
default action
ENDCASE
( i*x
caddr
u
-- j*x
)
Save the current input source specification and make the string described by caddr
and u
both the input source and buffer, reset >IN to zero (0), and interpret. When the parse area is empty, restore the prior input source specification.
( i*x
xt
-- j*x
)
Remove the execution token xt
from the stack and perform the semantics identified by it.
( -- )(R: ip
-- )
Return control from the currently executing word to its caller. Before executing EXIT
within a do-loop, a program shall discard the loop-control parameters by executing UNLOOP
.
( -- false
) constant
Return false value, equivalent to zero (0).
( addr
-- addr'
)
Define cell aligned structure field name
, which when executed adds the field offset to addr
giving addr'
.
( caddr
u
char
-- )
Fill memory at address caddr
with u
consecutive characters char
.
( -- )
Save and free all dirty block buffers.
( dend
dsor
-- mod
quot
)
Floored division of the dividend dend
by the divisor dsor
leaving the modulus mod
and quotient quot
. In floored division the modulus mod
carries the sign of the divisor dsor
.
Dividend Divisor Remainder Quotient
10 7 3 1
-10 7 4 -2
10 -7 -4 -2
-10 -7 -3 1
( -- addr
)
addr
is the data-space pointer that will next be assigned by ,
, ALIGN
, ALLOT
, C,
, COMPILE,
. See RESERVE
below.
( -- )
Set the numeric conversion radix to sixteen (hexdecimal).
( char
-- )
Append char
to the picture numeric output string.
( caddr
u
-- )
Append string caddr
u
to the pictured numeric output string.
( -- n|u
)
n|u
is a copy of the current (innermost) loop index.
( bool
-- )
If bool
is zero (0) (FALSE
), continue execution following the matching ELSE
or THEN
.
test IF
\ execute for true
THEN
\ continue
test IF
\ execute for true
ELSE
\ execute for false
THEN
\ continue
( -- )
Make the most recent definition an immediate word.
( <spaces>filepath
-- )
( caddr
u
-- )
( x1
-- x2
)
Take the one's complement of x1
.
( xt
<spaces>name
-- )
Set deferred word name
to the execution token xt
. See DEFER
.
( -- n|u
)
n|u
is a copy of the next-outer loop index.
( -- char
)
Receive one character char
. Characters received by KEY
are not displayed.
( -- ) (R: loop-sys
-- )
Leave the current loop, resume execution following the +LOOP
or LOOP
.
( blk_num
-- )
List the block given by blk_num
, 1 based, and save blk_num
in SCR
on success.
( x
-- )
Compile x
into the definition so that it is later pushed onto the stack during execution of the definition.
: SOMEWORD ... [ x ] LITERAL ... ;
( i*x
blk_num
-- j*x
)
Save the current input-source specification. Store blk_num
in BLK
(thus making block blk_num
the input source and setting the input buffer to encompass its contents), set >IN
to zero (0), and interpret. When the parse area is exhausted, restore the prior input source specification. Other stack effects are due to the words interpreted.
Add n
to the loop index. If the loop index did not cross the boundary between the loop limit minus one and the loop limit, continue execution at the beginning of the loop. Otherwise, end the current loop and continue execution immediately following the loop.
: SOMEWORD ... limit first DO ... LOOP ... ;
Or
: SOMEWORD ... limit first ?DO ... LOOP ... ;
( x1
u
-- x2
)
Logical left shift of x1
by u
bits, putting u
zeroes into the least significant bits.
( n1
n2
-- d
)
d
(lo
hi
on the stack) is the signed product of n1
times n2
.
( <spaces>name
-- )
Create name
in the dictionary, which when executed will remove name
and restore the dictionary to the state it had just prior to the name
being added.
( n1
n2
-- n3
)
n3
is the greater of n1
and n2
.
( n1
n2
-- n3
)
n3
is the lesser of n1
and n2
.
( dend
dsor
-- rem
)
Divide the dividend dend
by the divisor dsor
leaving the remainder rem
.
( src
dst
u
-- )
If u
is greater than zero (0), copy from src
the contents of u
consecutive address units to dst
.
( u
-- )
Wait at least u
milliseconds.
( n1
-- n2
)
Negate n1
, giving its arithmetic inverse n2
.
( x1
x2
-- x2
)
Drop the first item below the top of stack.
( i*x
n
–– ) (R: –– i*x
n
)
( –– i*x
n
) (R: i*x
n
–– )
( x1
x2
-- | x1
)
CASE
test1 OF ... ENDOF
...
testN OF ... ENDOF
default action
ENDCASE
( x1
x2
-- x3
)
Bit-wise or of the top two stack values.
( x1
x2
-- x1
x2
x1
)
Copy the second value x1
below the stack to the top.
( -- aaddr
)
A character buffer space available to developers and //not// used by standard words.
( -- )
Clear the terminal (advance next page).
( char
ccc<char>
-- caddr
u
)
Parse ccc
delimited by the delimiter char
. caddr
and u
are the address and length within the input buffer of the parsed string. If the parse area was empty, the resulting string has a zero (0) length.
( <spaces>name<space>
-- caddr
u
)
Skip leading space delimiters. Parse name delimited by a space.
( xu
xu-1
... x1
x0
u
-- xu
xu-1
... x1
x0
xu
)
Remove u
. Copy the xu
to the top of the stack. 0 PICK
equivalent to DUP
, 1 PICK
equivalent to OVER
.
( <spaces>name
-- )
Parse and find name
appending the compilation semantics of name
to the current definition. For an immediate word, it is compiled into the definition, instead of executed. Otherwise compile the word during the definition of another word.
An example:
: [CHAR] CHAR POSTPONE LITERAL ; IMMEDIATE
( -- )(R: i*x
-- )
Empty the return stack, reset SOURCE-ID
to zero (0), set the console as the input source, enter interpretation state, and start REPL:
- Accept a line from the input source into the input buffer, set
>IN
to zero (0), and interpret. - Display the prompt if in interpretation state, all processing has been completed, and no ambiguous condition exists.
( -- x
)(R: x
-- )
Move top of the return stack to the data stack.
( -- x
)(R: x
-- x
)
Copy top of return stack to the data stack.
( -- )
Call the current definition. The behaviour of RECURSE
following a DOES>
is undefined.
: FACTORIAL ( +n1 -- +n2)
DUP 2 < IF DROP 1 EXIT THEN DUP 1- RECURSE *
;
( -- )
Loop back to matching BEGIN
.
BEGIN
\ test expression
WHILE
\ loop body while true
REPEAT
\ continue once test is false
( -- bool
)
Attempt to fill the input buffer from the input source, returning a true if successful.
When the input source is the user input device, attempt to receive input into the terminal input buffer. If successful, make the result the input buffer, set >IN
to zero (0), and return true. Receipt of a line containing no characters is considered successful. If there is no input available from the current input source, return false.
When the input source is a string from EVALUATE
, return false and perform no other action.
When the input source is a block, make the next block the input source and current input buffer by adding one to the value of BLK
and setting >IN
to zero (0). Return true if the new value of BLK
is a valid block number, otherwise false.
( xu
xu-1
... x0
u
-- xu-1
... x0
xu
)
Left rotate the stack u
cells.
( x1
x2
x3
-- x2
x3
x1
)
Rotate the top three stack entries.
( x1
u
-- x2
)
Logical right shift of x1
by u
bits, putting u
zeroes into the most significant bits.
( ccc<quote>
-- caddr
u
)
When interpreting, copy the string ccc
as-is to a transient buffer and return caddr u
. When compiling, append the string ccc
as-is to the current word so when executed it leaves caddr u
of the string on the stack. Note as an extension strings are also NUL
terminated to facilitate use of host environment functions.
( ccc<quote>
-- caddr
u
)
When interpreting, copy the escaped string ccc
to a transient buffer and return caddr u
. When compiling, append the escaped string ccc
to the current word so when executed it leaves caddr u
of the string on the stack. Note as an extension strings are also NUL
terminated to facilitate use of host environment functions.
( -- )
Save all the dirty block buffers to the block file.
( -- aaddr
)
aaddr
is the address of a cell containing the block number from the most recent LIST
.
( caddr1
u1
caddr2
u2
-- caddr3
u3
bool
)
Search the string caddr1
u1
for the string caddr2
u2
. If bool
is true, a match was found at caddr3
with u3
characters remaining. If bool
is false there was no match and caddr3
u3
are just caddr1
u1
.
( n
-- )
If n
is negative, add a minus sign to the beginning of the pictured numeric output string. An ambiguous condition exists if SIGN
executes outside of a <# #>
delimited number conversion.
( caddr
u
-- )
Compile the string caddr
and length u
into the definition so that it is later pushed onto the stack during execution of the definition.
\ Simplistic version of S"
: S"
[CHAR] " PARSE POSTPONE SLITERAL
; IMMEDIATE
( dend
dsor
-- rem
quot
)
Symmetric division of the dividend dend
by the divisor dsor
leaving the remainder rem
and quotient quot
. In symmetric division the remainder rem
carries the sign of the dividend dend
.
Dividend Divisor Remainder Quotient
10 7 3 1
-10 7 -3 -1
10 -7 3 -1
-10 -7 -3 1
( -- caddr
u
)
Push the string address caddr
and the number of characters u
in the input buffer.
( -- )
Display a space.
( n
-- )
If n
is greater than zero (0), display n
spaces.
( -- aaddr
)
Return the aaddr
address of the compilation state; true when compiling, otherwise false when interpreting.
( x1
x2
-- x2
x1
)
Exchange the top two stack items.
( u1
u2
-- )
LOAD
in sequence blocks u1
through to u2
inclusive.
( -- sec
min
hour
day
month
year
)
Time current local system time. sec
{0..59}, min
{0..59}, hour
{0..23}, day
{1..31}, month
{1..12}, year
eg. 1970.
( -- )
See IF
.
AHEAD
\ not executed
THEN
\ continue
test IF
\ execute for true
THEN
\ continue
test IF
\ execute for true
ELSE
\ execute for false
THEN
\ continue
( k*x
n
-- k*x
| i*x
n
)
If n
is non-zero, pop the top most exception frame from the exception stack, along with everything on the return stack above that frame. Then restore the input source specification in use before the corresponding CATCH
and adjust the depths of all stacks so that they are the same as the depths saved in the exception frame (i
is the same number as the i
in the input arguments to the corresponding CATCH
), put n
on top of the data stack, and transfer control to a point just after the CATCH
that pushed that exception frame.
If the top of the stack is non-zero and there is no exception frame on the exception stack the system may display an implementation-dependent message giving information about the condition associated with the THROW
code n
. Subsequently, the system shall perform the function of ABORT
.
( i*x
<spaces>name
-- )
See VALUE
and 2VALUE
.
( -- true
) constant
Return a true flag, a single-cell value with all bits set.
( x1
x2
-- x2
x1
x2
)
Copy the first (top) stack item below the second stack item.
( caddr
u
-- )
If u
is greater than zero (0), display the character string specified by caddr
and u
.
( u
-- )
Display u in free field format.
( u1
u2
-- bool
)
bool
is true if and only if n1
is less than n2
.
( u1
u2
-- bool
)
bool
is true if and only if n1
is greater than n2
.
( u1
u2
-- ud
)
ud
(lo
hi
on the stack) is the unsigned product of u1
times u2
.
( dend
dsor
-- mod
quot
)
Divide dend
by dsor
, giving the quotient quot
and the remainder mod
. All values and arithmetic are unsigned.
( -- ) ( R: loop-sys
-- )
Drop the loop control for the current loop. An UNLOOP
is needed for each nesting level before a definition may leave by means of EXIT
.
limit first DO
\ stuff
\ test expression
IF
\ other stuff
UNLOOP EXIT
THEN
\ more stuff
LOOP
\ continue
( -- u
)
u
is the amount of data space remaining in the region addressed by HERE
, in address units.
( -- )
Mark the current block as dirty.
( x
-- )
Loop back to BEGIN
until x
equals zero (0).
BEGIN
\ loop body
\ test expression
UNTIL
( x
<spaces>name
-- )
Create name
with one cell of data assigned x
. When name
is executed, the value x
is pushed to the stack. See TO
.
69 VALUE position
: change ( new -- old ) position SWAP TO position ;
( <spaces>name
-- )
Create name
with one cell of data. When name
is executed push the aaddr
of the data cell.
VARIABLE name \ define name
123 name ! \ store value to variable
name @ \ fetch value from variable
( x
-- )
If x
equals zero (0), continue execution following REPEAT
.
BEGIN
\ test expression
WHILE
\ loop body while true
REPEAT
\ continue
( char
<chars>ccc<char>
-- caddr
)
Skip leading delimiters. Parse characters ccc
delimited by char
.
( x1
x2
-- x3
)
Bit-wise exclusive-or of the top two stack values.
( -- ) immediate Enter interpretation state.
( <spaces>name
-- xt
)
Place name's execution token xt on the stack.
( <spaces>ccc
-- char
)
Place char, the value of the first character of name, on the stack.
( <spaces>name
-- bool
) immediate
Return TRUE
if name
is word that can be found in the dictionary; otherwise FALSE
.
( <spaces>name ...
-- ) immediate
Discard space delimited words, including nested [IF]...[THEN]
and [IF]...[ELSE]...[THEN]
, until the matching (same level) [THEN]
is found.
( bool
| bool
<spaces>name ...
-- ) immediate
If bool
is false then discard space delimited words, including nested [IF]...[THEN]
and [IF]...[ELSE]...[THEN]
, until the matching (same level) [ELSE]
or [THEN]
is found.
( -- ) immediate
End conditional source block.
( <spaces>name
-- bool
) immediate
Return FALSE
if name
is word that can be found in the dictionary; otherwise TRUE
.
( ccc<LF>
-- ) immediate
Parse and discard the remainder of the parse area.
( -- )
Enter compilation state.
( x
-- )
Display value x
in several bases.
( -- )
Dump the return stack.
( -- u
) constant
Size of an character in octets.
( -- u
) constant
Size of a cell in octets.
( -- u
) constant
Maximum size of a counted string in characters. This is a deviation from ENVIRONMENT?
queries.
( -- u
) constant
Size of a numeric picture buffer in characters. This is a deviation from ENVIRONMENT?
queries.
( -- u
) constant
Size of a pad buffer in characters. This is a deviation from ENVIRONMENT?
queries.
( -- u
)
Offset into the current data-space for the word being compiled. Similar to the word HERE
, except expressed as an offset from the start of the data-space when the current word was created. During the compiliation of a word in C based implementations, the data-space region may be relocated when its enlarged by ,
, ALIGN
, ALLOT
, C,
, COMPILE,
thus invalidating previous values of HERE
on the stack. Providing an offset into the current data-region allows for computing relative locations.
( -- argv
argc
)
Return the number of arguments on the command line argc
and the NULL terminated array of C string pointers argv
. See example dumpargs.p4
.
( -- u
) constant
Size of one address unit in bits. This is a deviation from ENVIRONMENT?
queries.
( -- )
Set the numeric conversion radix to 2 (binary).
( -- u
)
Number of blocks u
currently in the block file, one through to u
. The block file can be extended by writing to block u'
, the file will be extended with intervening blank blocks from the current end upto but not including block u'
, which the actual bock write of u'
will fill.
( exit_code -- )
Terminate and return to the host OS an exit code; zero (0) for normal/success, non-zero an error occurred.
( ccc<quote>
-- caddr
)
When interpreting, copy the escaped string ccc
to a transient buffer and return counted string caddr
. When compiling, append the escaped string ccc
to the current word so when executed it leaves the counted string caddr
on the stack. See also S\"
.
( caddr1
-- caddr2
)
Subtract the size in address units of a character.
( aaddr1
-- aaddr2
)
Subtract the size in address units of a cell from aaddr1
giving aaddr2
.
( caddr -- )
Print the counted string. See also puts
.
( -- u
)
System clock time in seconds from the epoch.
( key
k
-- value
v
)
Lookup the environment variable string key
k
. Return string value
v
; if length v
is -1
, then the environment variable key
was not found and value
is invalid.
S" HOME" env puts CR
S" USER" env puts CR
( -- false
) constant
True if floored division is the default. This is a deviation from ENVIRONMENT?
queries.
( -- )
Increment variable SCR
and list next block.
( xu
xu-1
... x0
u
-- x0
xu
xu-1
... x1
)
Right rotate the stack u
cells; ROLL
in the opposite direction.
( -- u
) constant
Maximum value of any character. Currently Post4 only supports ASCII and addressable units are octets. This is a deviation from ENVIRONMENT?
queries.
( -- d
) constant
Largest usable signed double integer. This is a deviation from ENVIRONMENT?
queries.
( -- u
) constant
Largest usable signed integer. This is a deviation from ENVIRONMENT?
queries.
( -- u
) constant
Largest usable unsigned integer. This is a deviation from ENVIRONMENT?
queries.
( -- ud
) constant
Largest usable unsigned double integer. This is a deviation from ENVIRONMENT?
queries.
( i*x aaddr -- )
( i*x i -- )
( aaddr -- i*x )
( -- )
Set the numeric conversion radix to eight (octal).
( char
ccc<char>
-- caddr u )
Parse ccc
delimited by the delimiter char
. caddr
and u
are the address and length within the input buffer of the parsed C-style escaped string. If the parse area was empty, the resulting string has a zero (0) length. Supported escapes:
\? delete
\\ backslash
\a bell
\b backspace
\e escape
\f formfeed
\n linefeed
\r carriage-return
\s space
\t tab
\v vertical tab
\0 nul
Backslash followed by any other character escapes that character, ie. \\
is a literal backslash, \"
is a literal double quote. Note that alpha-numeric characters are reserved for future extensions.
( caddr -- )
Print a NUL terminated string.
CREATE greet S\" Hello world.\n"
greet drop puts
( n
-- addr
)
Similar to ALLOT
, reserve n
address-units of data-space and return its start address. While defining a word in C based implementations, like Post4, data-space regions may be relocated when they are enlarged, thus invalidating previous values of HERE
. Therefore:
HERE 100 CELLS ALLOT
Should ALLOT
enlarge and relocate the data-space, the address saved by HERE
on the stack will now point into invalid memory. With reserve
the address of the region just reserved is on top of the stack insuring that the address is valid until the next enlargement of the data-space by reserve
,,
, ALIGN
, ALLOT
, C,
, or COMPILE,
.
( caddr1
u1
caddr2
u2
-- bool
)
Return true if string caddr1
u1
starts with string caddr2
u2
; otherwise false.
( caddr1
u1
caddr2
u2
-- n
)
Compare the two strings lexicographically. Return n
greater than, equal to, or less than zero (0), according to whether the string caddr1
u1
is greater than, equal to, or less than the string caddr2
u2
.
( caddr -- u )
String length of NUL terminated string.
( -- )
Breakpoint.
( -- )
Branch relative. The integer that immediately follows is the relative distance in address units from the integer's address. Used in the definition of flow control words, like AGAIN
and AHEAD
, ELSE
, REPEAT
.
( bool
-- )
Branch relative if zero (0). The integer that immediately follows is the relative distance in address units from the integer's address. Used in the definition of flow control words, like IF
, WHILE
, UNTIL
.
( -- )
Call relative. The integer that immediately follows is the relative distance in address units from the integer's address. Used in the definition of flow control words, like RECURSE
.
( -- aaddr
n
)
Push the data stack base address and current depth. This stack is a fixed size and grows upward.
( -- n
)
Push the data stack's size.
( aaddr
-- )
Store addr
into the data stack pointer.
( -- aaddr
)
Fetch the data stack pointer.
( xt
-- bool
)
Return true if the execution token is for an immediate word.
( n
-- )
Return to the context saved at the start of the REPL (QUIT
) passing n
. Values of n
from -1 to -255 are the Forth 200x standard THROW
codes. Passing -256 is equivalent to BYE
.
( -- aaddr
n
)
Push the return stack base address and current depth. This stack is a fixed size and grows upward.
( -- n
)
Push the return stack's size.
( aaddr
-- )
Store addr
into the return stack pointer.
( -- aaddr
)
Fetch the return stack pointer.
( aaddr
u
-- )
Utility word used to define .S
and .rs
.
This is a list of THROW
codes used internally by Post4.
- -1
ABORT
- -2
ABORT"
- -3 stack overflow
- -4 stack underflow
- -5 return stack overflow
- -6 return stack underflow
- -9 invalid memory address (
SIGSEGV
) - -10 division by zero
- -13 undefined word
- -14 interpreting a compile-only word
- -17 pictured numeric output string overflow
- -21 unsupported operation
- -23 address alignment exception (
SIGBUS
) - -24 invalid numeric argument
- -28 user interrupt (
SIGINT
) - -29 compiler nesting
- -31 word not defined by
CREATE
- -33 block read exception
- -34 block write exception
- -35 invalid block number, such as zero (0)
- -55 floating-point unidentified fault (
SIGFPE
) - -56
QUIT
- -59
ALLOCATE
- -61
ALLOT
orRESIZE
Forth 200x
http://www.forth200x.org/
Forth Standard
https://forth-standard.org/