4word

4word is a stack language, similar to Forth.

4word was designed to be used as a command interpreter over text chat in Common Lisp systems. Programming a stack language over text chat is quite convenient, as complicated expressions may be divided into smaller pieces and intermediate results may be inspected and altered. 4word has been used with Jabber (XMPP) and Microsoft Teams (Microsoft Bot Framework) to set parameters, read system status etc.

The interpreter state is easily serialized and stored in a file system or a database management system, and adding new words from the lisp environment to the vocabulary is easy, too.

How to use the interpreter

The special variables *WORDS* and *STACK* must be bound around calls to the outer interpreter INTERPRET. Hence, the simplest use of the interpreter may look something like this.

(let ((*words* ...)
      (*stack* nil))
  (interpret "1 1 +")
  *stack*)

;; The above returns stack: (2)

Another, more realistic example involves a user's vocabulary and stack. It is the responsibility of the application to load and save these two around calls to the interpreter.

(let ((*words* user-vocabulary)
      (*stack* user-stack))
  (interpret message-text :max-cycles 1000)
  (setf user-vocabulary *words*
        user-stack *stack*))

Vocabulary, overview

The vocabulary are divided into categories in this document for better browsability.

SequencesCompilerVariablesExecutionLow level sequence interfaceCompare objectsBoolean arithmetics and comparisonsNumber arithmetics and comparisonsStack shuffling

Words, ordered by name.

*+,--ROT/2DROP2DUP2NIP2OVER3DROP3DUP:<<=<CONS><IOTA><ITERATOR><VECTOR>=>>=ANDCALLCARCDRDECDECONSDROPDUPDUPDEACHFILTERGETIFINCITERATELENGTHLETLIST>VECTORMAPMAXMINMODNIPNOTNTHOBUNOROVERPICKRBUNREDUCEREVERSE>LISTREVERSE>VECTORROTROUNDSETSET-NTHSWAPSWAPDTIMESTRUNCATE

Vocabulary, detail

Detailed description of the vocabulary, one word per "box". The header includes word name, stack effect and type. A word may be of primitive or threaded type. Some documentation and examples may be provided in addition to the header. The documentation has been colorized to highlight keywords from the stack comment, where consumed objects are of red color and and produced objects are of green color.

Sequences

<VECTOR>( length -- vector )primitive

Creates a VECTOR of length LENGTH.

<IOTA>( length -- iota )primitive

Creates an immutable virtual sequence (IOTA) containing the integers from 0 to LENGTH-1.

TIMES( n quot -- )threaded

For each number from 0 to N-1, place the number on top of stack and call QUOT.

! Times is just shorthand for EACH over a <IOTA>.
3 [ inc ] times
! Stack: 1 2 3
! The same effect as:
3 <iota> [ inc ] each

EACH( iterable quot -- )threaded

For each value from ITERABLE, place value on top of stack and call QUOT.

[ 1 2 3 ] [ inc ] each
! Stack: 2 3 4

MAP( iterable qout -- vector )threaded

For each value from ITERABLE, place value on top of stack and call QOUT, then collect the result into VECTOR.

[ 1 2 3 ] [ inc ] map
! Stack: [ 2 3 4 ]

FILTER( iterable quot -- vector )threaded

For each value from ITERABLE, place value on top of stack and call QUOT. Collect the value into VECTOR if QUOT returned non-NIL.

[ 1 2 3 ] [ 3 < ] filter
! Stack: [ 1 2 ]

REDUCE( iterable identity quot -- aggregate )threaded

Aggregate values from ITERABLE as AGGREGATE using QUOT. The first value from ITERABLE is combined with IDENTITY.

[ 1 2 3 ] 0 [ + ] reduce
! Stack: 6

Compiler

Words may be added to the vocabulary by traditional Forth colon-definitions.

:( -- )primitive

Enter the compiler and compile until a semicolon, then add the compiled word on top of the dictionary.

: factorial ( n -- n! ) <iota> [ 1 + ] map 1 [ * ] reduce ;

Variables

Variables are dynamically bound in frames on the return stack. When a word exists, it's variable bindings goes out of scope.

LET( value variable -- )primitive

Allocate VARIABLE and bind to VALUE.

"HELLO" , text let

SET( value variable -- )primitive

Bind an already allocated VARIABLE to VALUE.

"HI" , text let

GET( variable -- value )primitive

Get the VALUE bound to VARIABLE.

, text get

Execution

This category contains a mix of high and low level words. IF and , are frequent in user code while CALL, RBUN and especially OBUN is less common.

,( -- object )primitive

Push OBJECT at program counter + 1 onto the stack.

, test
! Stack: TEST

CALL( quot -- )primitive

Call quotation QUOT.

IF( test then-quot else-quot -- )threaded

If TEST is non-NIL call THEN-QUOT else call ELSE-QUOT.

0 1 < [ , true ] [ , false ] if
! Stack: TRUE

RBUN( x -- )primitive

Reset program counter unless X is NIL.

OBUN( x offset -- )primitive

Offset program counter by OFFSET unless X is NIL.

Low level sequence interface

These words form the foundation upon which the higher level sequence words (MAP, REDUCE etc.) are built. Vectors are the most common kind of sequence in user code, while linked lists (cons cells) are used internally by many of the higher level words.

LENGTH( seq -- length )primitive

Get the LENGTH of sequence SEQ.

SET-NTH( object index seq -- )primitive

Set the OBJECT at index INDEX in a sequence SEQ.

NTH( index seq -- object )primitive

Get the OBJECT at index INDEX in a sequence SEQ.

<CONS>( cdr car -- cons )primitive

Creates a CONS from CDR and CAR.

CAR( cons -- car )primitive

Get CAR of CONS.

CDR( cons -- cdr )primitive

Get CDR of CONS.

DECONS( cons -- cdr car )threaded

Deconstruct CONS as CDR and CAR.

LIST>VECTOR( list -- vector )threaded

REVERSE>LIST( iterable -- list )threaded

REVERSE>VECTOR( iterable -- vector )threaded

<ITERATOR>( iterable -- iterator )threaded

Creates an ITERATOR to iterate over ITERABLE. In fact, ITERATOR is just a simple state-object designed to be used by ITERATE.

ITERATE( iterator -- value value-p )threaded

Get the next VALUE from the ITERATOR. In addition to the VALUE, push a VALUE-P boolean. VALUE-P is NIL when the iteration is complete.

Compare objects

=( x y -- boolean )primitive

Returns T if X and Y are structurally similar (isomorphic) objects, else returns NIL. Common Lisp's EQUAL function is used to compare the objects.

Boolean arithmetics and comparisons

AND( x y -- boolean )threaded

OR( x y -- boolean )threaded

NOT( x -- ^x )threaded

Number arithmetics and comparisons

The vocabulary for number arithmetics and comparisons are far from complete.

INC( x -- x+1 )threaded

DEC( x -- x-1 )threaded

MIN( x y -- z )threaded

MAX( x y -- z )threaded

+( augend addend -- sum )primitive

-( minuend subtrahend -- difference )primitive

*( multiplier multiplicand -- product )primitive

/( dividend divisor -- fraction )primitive

MOD( dividend divisor -- remainder )primitive

ROUND( x -- integer )primitive

TRUNCATE( x -- integer )primitive

<( x y -- boolean )primitive

>( x y -- boolean )primitive

<=( x y -- boolean )threaded

>=( x y -- boolean )threaded

Stack shuffling

The most popular stack shuffling words are built in as primitives.

DROP( x -- )primitive

2DROP( x y -- )primitive

3DROP( x y z -- )primitive

NIP( x y -- y )primitive

2NIP( x y z -- z )primitive

DUP( x -- x x )primitive

2DUP( x y -- x y x y )primitive

3DUP( x y z -- x y z x y z )primitive

DUPD( x y -- x x y )primitive

OVER( x y -- x y x )primitive

2OVER( x y z -- x y z x y )primitive

PICK( x y z -- x y z x )primitive

SWAP( x y -- y x )primitive

SWAPD( x y z -- y x z )primitive

ROT( x y z -- y z x )primitive

-ROT( x y z -- z x y )primitive