Hello

Forth

Inching Forth

Last revised 15 February 2000

. means display
The two most common Forth words
Spaces are delimiters
Messages
Comments
Guilty with explanation
Putting 2 and 2 together
Looking for an argument?
Friends
Repeating while
Repeating until
Repeating again
Do you loop?
?Do you loop?
Do you +loop?
If so facto
Re: recursion
Many are numbers, few are characters
Another hand
A little about allot
Comma here !
Meet me at-xy
Does is as does does
Words with :noname
For you a lei
You must remember this

. means display

. is pronounced "dot".

. displays a number as an integer.

In Forth, most of what look like punctuation or other symbols are words ( = routines = procedures = functions).

Some other Forth words are:

U. ("u-dot")
! ("store")
+! ("plus-store")
@ ("fetch")
? ("question")
, ("comma")

For authoritative information about Forth and particular Forth words, see the ANS Forth Document.

Try from the keyboard:
1 .

-1 .

-1 U.

-1 INVERT U.

-1 INVERT INVERT U.

-1 NEGATE .

-1 NEGATE NEGATE .

-1 ABS .

-1 ABS ABS .

\ Name a place
CREATE THERE

THERE U.

\ , a number there
0 ,

\ @ it and . it
THERE @ .

\ ? does @ .
THERE ?

\ ! ( don't , ) a different number
-1 THERE !

THERE ?

1 THERE !   THERE ?

1 THERE +!  THERE ?

BYE

The two most common Forth words

are : ("colon") and ; ("semicolon").

: and ; make new words from words that already exist.

Probably the third most common Forth word is DUP ("doop").

Try from the keyboard:

: SQUARED  DUP * ;

2 SQUARED .

: CUBED  DUP SQUARED * ;

2 CUBED .

: ^4  SQUARED SQUARED ;

2 ^4 .

: ^16  ^4 ^4 ;

2 ^16 .

2 ^16 SQUARED .

( Forth does not check for overflow. )

Spaces are delimiters

IF THESE BE WORDS SEVEN THERE BE

A-WORD-CAN-HAVE-UP-2-31-CHRCTRS

Some Forths are case sensitive, oTHerS NoT.

Try from the keyboard:

2 BASE !

TRUE U.

FALSE U.

1 0 > U.

0 1 > U.

TRUE TRUE AND U.

TRUE FALSE AND U.

1 TRUE AND U.

1 FALSE AND U.

HEX

TRUE U.

FALSE U.

DECIMAL

65 .

\ EMIT displays a "character"
65 EMIT

66 EMIT

67 EMIT

97 EMIT  98 EMIT  99 EMIT

48 EMIT

49 EMIT

1 48 + EMIT

10 48 + EMIT

10 7 +  48 +  EMIT

1 9 > .

10 9 > .

TRUE 7 AND .

FALSE 7 AND .

0 DUP 9 >  7 AND +  48 +  EMIT

1 DUP 9 >  7 AND +  48 +  EMIT

10 DUP 9 >  7 AND +  48 +  EMIT

11 DUP 9 >  7 AND +  48 +  EMIT

: .digit  DUP 9 >  7 AND +  48 +  EMIT ;

1 .digit  2 .digit  3 .digit

15 .digit 24 .digit 27 .digit 29 .digit 17 .digit


Messages

.( ("dot-paren") displays now.
." ("dot-quote") displays later.
CR ("c-r") goes to the next line, now or later.
Try from the keyboard:
: Wicked-Witch  CR ." SURRENDER DOROTHY " ;

: Dorothy  CR ." There's no place like home. " ;

CR .( From the Wizard of Oz ) Wicked-Witch Dorothy

Comments

( This is a comment ) \ So is this

Forth ignores comments. Don't you.

Always tell what a new word expects and what it returns.

The usual form is: ( expects -- returns ).

Try from the keyboard:
: nonplus  ( n -- n )  1+ 1- ;

0 nonplus .

\ Declaration
: LOVE  ( -- )
   0 PARSE                         \ get love object
   CR TYPE  SPACE                  \ present it
   ." is a beautiful language!" ;  \ with feeling

LOVE C++

LOVE Java

LOVE The next great programming language

Guilty with explanation

ABORT" ("abort-quote") can stop a program and tell you why.

Try from the keyboard:
TRUE 0= .

FALSE 0= .

-1 0= .

1 0= .

0 0= .

: COOL  ( x -- )  
   0= ABORT" Uncool. "
   ." Yeah, cool. " ;


TRUE COOL

FALSE COOL

2 COOL

0 COOL

: FIRE-ME?  ( x -- )
   ABORT" I quit! "
   ." Then how about a raise? " ;

FALSE FIRE-ME?

TRUE FIRE-ME?

0 FIRE-ME?

1 FIRE-ME?

Putting 2 and 2 together

The 2's come first.

Try from the keyboard:
2 2 + .

2 2 - .

\ "star"
2 2 * .

\ "slash"
2 2 / .

2 2* .

2 2/ .

2 2 MOD .

2 2 /MOD . .

2 2 2 */ .

2 2 2 */MOD . .

2 2 = .

2 2 <> .

2 2 AND .

2 2 OR .

2 2 XOR .

2 2 LSHIFT .

2 2 RSHIFT .

2 2 + 2 MIN .

2 2 + 2 MAX .

\ From 0 - for Sister Dorothy A. Flood, CSJ
: AWAY  ( n u -- +n+u | -n-u )
   OVER ( n u n )
   0<  1 OR  *  + ;

0 2 AWAY .

2 0 AWAY .

2 2 AWAY .

-2 2 AWAY .

Looking for an argument?

Find it with .S ("dot-s").

Try from the keyboard:
1 .S

.

1 2 .S

. . 

1 2 3 .S

. . .

1 .S

2 .S

3 .S

+ .S

+ .S

.

\ Check arguments within a word
: .STOP  ( -- )  CR .S  ." Press a key." KEY DROP ;

: .DOWN  ( ... x  -- ... )  .STOP DROP ;

: COUNTDOWN ( -- )
   1 2 3 4 5 6 7 8 9 10
   .DOWN 
   .DOWN
   .DOWN 
   .DOWN
   .DOWN 
   .DOWN
   .DOWN 
   .DOWN
   .DOWN
   .DOWN
   .STOP ;

COUNTDOWN

Friends

Arguments sometimes need to be dropped, duplicated, switched, rotated, and so on. In these cases, these words are friends indeed:

DROP ( x -- )
DUP ( x -- x x )
?DUP ( x|0 -- x x | 0 ) ("question-doop")
SWAP ( x y -- y x )
NIP ( x y -- y )
TUCK ( x y -- y x y )
OVER ( x y -- x y x )
2DROP ( x y -- )
2DUP ( x y -- x y x y )
ROT ( x y z -- y z x ) ("rote")
2SWAP ( w x y z -- y z w x )
2OVER ( w x y z -- w x y z w x )
Try from the keyboard:
.S

1 .S

DROP .S

1 DUP .S

2DROP .S

0 DUP .S

2DROP

1 ?DUP .S

2DROP

0 ?DUP .S

DROP

1 2 .S

SWAP .S

NIP .S

DROP 

1 2 TUCK .S

2DROP DROP

1 2 OVER .S

2DROP DROP

1 2 2DUP .S

2DROP 2DROP

1 2 3 .S

ROT .S

ROT .S

ROT .S

2DROP DROP

1 2 3 4 .S

2SWAP .S

2SWAP .S

2OVER .S

+ + + .S

+ + .S

.

: BINARY  ( -- )  2 BASE ! ;

: IN  ( n -- )  
   BASE @  TUCK ." base "  DECIMAL . 
   ." is "
   DUP        . ." decimal " 
   DUP HEX    . ." hex " 
       BINARY . ." binary " 
   BASE ! ;

DECIMAL 10 IN

99 IN

HEX 10 IN

FF IN

BINARY 10 IN

11 IN

DECIMAL

CHAR A IN

BL IN

Repeating while

As in: BEGIN words1 condition WHILE words2 REPEAT .

While condition <> 0 , words2 and repeat words1.

Use BEGIN WHILE REPEAT only within a word.

Try from the keyboard:
: COUNTDOWN  ( +n -- )
   BEGIN  DUP .  ?DUP WHILE 1- REPEAT ;

10 COUNTDOWN

\ Display bits

\ x CONSTANT "name" makes a constant
\ "name" then returns x
\ Set most significant bit
TRUE 1 RSHIFT INVERT CONSTANT 100...

2 BASE !

100... U.

DECIMAL

: .BITS  ( x -- )
   100...  ( x mask )
   BEGIN  DUP  \ Mask not all zeros?
   WHILE
     2DUP AND 0=  -1 AND  [CHAR] 1 +  EMIT
     1 RSHIFT  \ Shift mask 1 to the right
   REPEAT  2DROP ;

1 .BITS

2 .BITS

3 .BITS

100... .BITS

TRUE .BITS

FALSE .BITS

\ from Wil Baden
: LO-BITS  ( n -- mask )
   TRUE  SWAP LSHIFT INVERT ;
: HI-BITS  ( n -- mask )
   TRUE  SWAP RSHIFT INVERT ;
1 CONSTANT LO-BIT
1 HI-BITS CONSTANT HI-BIT

LO-BIT .BITS

HI-BIT .BITS

HI-BIT .

HI-BIT U.

8 LO-BITS .BITS

8 HI-BITS .BITS


: FACTORIAL  ( +n -- n! )
   1 SWAP  BEGIN ?DUP WHILE TUCK * SWAP 1- REPEAT ;

0 FACTORIAL U.

1 FACTORIAL U.

5 FACTORIAL U.

Repeating until

As in: BEGIN words condition UNTIL .

Repeat words until condition <> 0.

Use BEGIN UNTIL only within a word.

Try from the keyboard:
: COUNTDOWN  ( +n -- )
   BEGIN  DUP .  1- DUP  0< UNTIL  DROP ;

10 COUNTDOWN

\ [CHAR] ("bracket-char") and CHAR to be explained.
: toZ  ( char[acter] -- )
   BEGIN  DUP EMIT  1+ DUP  [CHAR] Z > UNTIL  DROP ;

CHAR A toZ

: ABC's  ( -- )
   [CHAR] A toZ ;  

ABC's

: FROM  ( -- char )  CHAR ;

FROM A toZ 

Repeating again

Never AGAIN .

Try from the keyboard:
: COUNTDOWN  ( +n -- )
   BEGIN  DUP .  1-  DUP 0< IF DROP EXIT THEN  AGAIN ;

10 COUNTDOWN

Do you loop?

As in: limit start DO ... I ... LOOP .

I am - is - optional. I returns start start+1 ... start+n-1.

start should be smaller than limit.

Use DO LOOP only within a word.

Try from the keyboard:
: AYE  ( -- )  ." Yes! " ;

AYE

: AYES  ( +n -- )  0 DO  AYE  LOOP ;

1 AYES

10 AYES

( Avoid 0 AYES )

: I's  ( limit start -- )
   DO  I .  LOOP ;

10 0 I's

0 -10 I's

5  -5 I's

: I-AYES  0 DO I . AYE LOOP ;

10 I-AYES

( Avoid 0 I-AYES )

\ N! again
: FACTORIAL  ( +n -- n! )
   1 TUCK  MAX 1+  1 DO  I *  LOOP ;

0 FACTORIAL U.

1 FACTORIAL U.

5 FACTORIAL U. 

\ Forth doesn't check for overflow

100 FACTORIAL .

?Do you loop?

As in: replace DO with ?DO ("question-do").

?DO doesn't DO if start = limit.

Try from the keyboard:
: AYE  ( -- )  ." Yes! " ;

: AYES  ( u -- )  0 ?DO  AYE  LOOP ;

0 AYES

10 AYES

: UP  ( +n -- )
   1+ 1 ?DO  I .  LOOP ;

10 UP

0 UP

: DOWN  ( +n -- )
   1- DUP -1 ?DO  DUP I - .  LOOP  DROP ;

10 DOWN

0 DOWN

: CLIMB  ( +n -- )
   DUP UP  SPACE  DOWN ;

10 CLIMB

0 CLIMB


\ N! again
: FACTORIAL  ( +n -- n! )  1 SWAP 1+  1 ?DO I * LOOP ;

0 FACTORIAL U.

1 FACTORIAL U.

5 FACTORIAL U. 


\ I seldom use I
\ Redefine CLIMB
: UP  ( +n -- )
   0 TUCK  ?DO  1+ DUP .  LOOP DROP ;

10 UP

0 UP

: DOWN  ( +n -- )
   DUP  0 ?DO  DUP . 1-  LOOP DROP ;

10 DOWN

0 DOWN

: CLIMB  ( +n -- )
   DUP UP  SPACE  DOWN ;

10 CLIMB

0 CLIMB

\ COUNT is an interesting Forth word
\ If u cn COUNT u cn SPELL
: SPELL  ( -- )
   0 PARSE
   0 ?DO  COUNT EMIT  SPACE  LOOP  DROP ;

SPELL www.albany.net/~hello

Do you +loop?

No.

Try from the keyboard (and observe carefully):
: STEP  ( limit start step -- )
   ROT ROT  ( step limit start )
   ?DO  I .  DUP +LOOP  DROP ;

10 0 1 STEP

0 10 -1 STEP

10 0 2 STEP

0 10 -2 STEP

If so facto

As in: condition IF words1 ELSE words2 THEN .

If conditon <> 0 , words1 , else words2 then.

ELSE is optional.

Use IF ELSE THEN only within a word.

Try from the keyboard:
: T|F  ( x -- )
   IF ." True " ELSE ." False " THEN ;

TRUE T|F

FALSE T|F

TRUE TRUE AND T|F

TRUE TRUE XOR T|F

TRUE FALSE AND T|F

TRUE FALSE OR T|F

TRUE FALSE XOR T|F

TRUE 0= T|F

TRUE INVERT T|F

FALSE 0= T|F

FALSE INVERT T|F

0 T|F

0 0= T|F

0 INVERT T|F

1 T|F

1 0= T|F

1 INVERT T|F

TRUE .

TRUE U.

TRUE FALSE < T|F

TRUE FALSE U< T|F

TRUE FALSE > T|F

TRUE FALSE U> T|F

Re: recursion

A Forth word can call itself, but not by its own name. It must say RECURSE .

Real programmers use recursion. I myself have never used it in a real program. See, however, 0123 Forth, Stacks of Forth, and most Quicksorts.

Try from the keyboard:
: COUNTDOWN  ( u -- )
   DUP . ?DUP IF  1- RECURSE  THEN ;

10 COUNTDOWN

\ Greatest common divisor

\ Unsigned MOD
\ UM/MOD is worth looking up
: UMOD  ( u1 u2 - remainder)
   0 SWAP  UM/MOD  DROP ;

-1 2 MOD .

-1 2 UMOD U.

\ Euclid's algorithm with recursion
: GCD  ( u1 u2 - gcd )
   ?DUP IF  TUCK UMOD  RECURSE  THEN ;

10 40 GCD .

40 10 GCD .

\ Display a positive number in binary
: .B  ( +n -- )
   DUP 1 > IF DUP 2/ RECURSE THEN  2 MOD  0 U.R ;

0 .B
1 .B
2 .B
3 .B
4 .B
15 .B
16 .B

\ N! again
: FACTORIAL ( +n -- +n!)
   DUP 2 < IF  DROP 1 EXIT  THEN
   DUP 1-  RECURSE * ;

0 FACTORIAL .

1 FACTORIAL .

5 FACTORIAL .

\ By Marcel Hendrix
: Hello  ( -- ) ." Hello, World! " RECURSE ;

Many are numbers, few are characters

Since this is so, and since x bits can represent 2^x items, fewer bits are needed to represent characters than to represent numbers.

If data space (a passel of bits collected into bytes or other groupings that represent data) is thought of as a series of addresses, then the same amount of space can have more addresses for characters than for numbers.

For example, in some Forth systems:
Addresses:  a0  a1  a2  a3  a4  a5  etc.
Numbers:    n0--->  n1--->  n2--->  etc.
Characters: c0  c1  c2  c3  c4  c5  etc.

In this example, a0 a2 a4 are addresses for numbers.
a0 a1 a2 a3 a4 a5 are addresses for characters.

The address of a number is an "aligned address" (a-addr) The size of a number is one "cell".

The address of a character is a character address (c-addr). The size of a character is one "char" (how do you say char?).

@ ! ? are among the Forth words that expect cell data; cell data are usually interpreted as integers.

Some Forth words that expect character data are:

C@ ( c-addr -- char ) fetches a character
C! ( char c-addr -- ) stores a character
EMIT ( char -- ) . for a character
COUNT ( c-addr1 -- c-addr2 u|char ) fetches a count or a character at c-addr and returns the next c-addr after c-addr; often used before TYPE
TYPE ( c-addr u -- ) EMITs u characters beginning with the character at c-addr
BL ( -- char ) ("b-l") returns the value of the space character
SPACE ( -- ) does BL EMIT
SPACES ( n -- ) does SPACE n times
CHAR ( -- char ) waits for a character, returns it
[CHAR] ( -- char ) ("bracket-char") returns the following character. Use only in a word.
PARSE ( char -- c-addr u ) see GREEK below
Try from the keyboard:
\ Size of a character in your system:

0 CHAR+ U. .( address units)

1 CHARS .

2 CHARS .

\ Size of an number in your system

0 CELL+ U. .( address units)

1 CELLS .

2 CELLS .

\ For O
CREATE TA-DA TRUE TRUE TRUE , , ,

TA-DA ?

\ Number of numbers after 0 in your system
TA-DA @ U.

\ Number of characters after character=0
TA-DA C@ U.

\ A number in bits

2 BASE !

TA-DA @ U.

\ A character in bits
TA-DA C@ U.

DECIMAL

FALSE TA-DA !

TA-DA ?

TRUE TA-DA C!

TA-DA C@ U.

TA-DA ?

1 TA-DA !

2 TA-DA CELL+ !

3 TA-DA 2 CELLS + !

TA-DA ?

TA-DA CELL+ ?

TA-DA CELL+ CELL+ ?

TA-DA 2 CELLS + ?

CHAR A EMIT  BL EMIT  CHAR B EMIT

CHAR A EMIT SPACE CHAR B EMIT

CHAR A EMIT  10 SPACES  CHAR B EMIT

\ PAD  ( -- c-addr )  start of a scratch area for characters
PAD U.

CHAR A PAD C!

PAD C@ .

PAD C@ EMIT

CHAR B  PAD CHAR+ C!

PAD CHAR+ C@ EMIT

CHAR C  PAD 2 CHARS + C!

PAD  2 CHARS +  C@ EMIT

PAD CHAR+ CHAR+ C@ EMIT

PAD .S

COUNT .S

EMIT

.S

DROP

PAD COUNT EMIT  COUNT EMIT  COUNT EMIT DROP

PAD 3 TYPE

\ A count makes a "counted string" of characters

\ At PAD , for example
PAD  .S

\ Store count
3 OVER C!  .S

\ Store characters
CHAR+  CHAR X  OVER C!  .S

CHAR+  CHAR Y  OVER C!  .S

CHAR+  CHAR Z  SWAP C!  .S

PAD .S

COUNT .S

TYPE .S

PAD COUNT TYPE

: .A  ( -- )  [CHAR] A EMIT ;

.A

: .CHAR  ( -- )  CHAR EMIT ;

.CHAR A

.CHAR B

.CHAR ABRACADABRA

\ Ancient texts are even harder to read than Forth
: GREEK
   0 PARSE
   0 ?DO COUNT DUP
        BL <> IF EMIT ELSE DROP THEN LOOP  DROP ;

GREEK THE WRATH OF PELEUS SON O GODDESS SING


Another hand

Sometimes it's convenient to temporarily move an argument to another place.

The words that do this are:

>R ("to-r") moves an argument
R> ("r-from") returns an argument
R@ ("r-fetch") returns a copy of what was moved

These words do the same with two arguments:

2>R
2R>
2R@

All these words can only be used in a word. In that word you must return what is moved. Remember "R" for Return.

There are also special rules, which I won't discuss in this series, when using "R" words with DO loops.

Try from the keyboard:
\ Least common multiple

\ Greatest common divisor
\ Euclid's algorithm without recursion
: GCD  ( +n1 +n2 -- gcd )
   BEGIN  ?DUP WHILE  TUCK MOD  REPEAT ;

10 40 GCD .

\ lcm(x,y) = x*y / gcd(x,y)
: LCM  ( +n1 +n2 -- lcm )
   2DUP GCD >R  *  R> / ;

10 40 LCM .

\ FLIP: Split a string and swap the pieces
\ A "string" is a lineup of characters.
\ A string is commonly identified by its
\ starting address and its length (c-addr u).

\ Swap characters
: CSWAP  ( c-addr1 c-addr2 -- )
   2DUP 2>R  C@ SWAP C@  R> C!  R> C! ;

\ Shorten distance between addresses
: SQUEEZE  ( addr1 addr2 n - addr1+n addr2-n )
   TUCK - >R + R> ;

\ Reverse a string
: TURN  ( c-addr u -- )
   1- CHARS  OVER +  ( start-addr end-addr )
   BEGIN  2DUP U<           \ addr1 U< addr2 ?
   WHILE  2DUP CSWAP
          1 CHARS SQUEEZE 
   REPEAT  
   2DROP ;

\ S" ( -- c-addr u ) ("s-quote") makes a string
S" ABCD"  PAD SWAP CHARS MOVE

PAD 4 TYPE

PAD 4 TURN

PAD 4 TYPE

\ /STRING "slash-string" ( c-addr u n -- c-addr+nchars u-n )
\ "cut-string" would have been nicer
: FLIP  ( c-addr u n -- )
   >R                    \ n to hand
   2DUP OVER R@  TURN    \ turn substring c-addr n
   R> /STRING    TURN    \ turn substring after n
                 TURN ;  \ turn string c-addr u

S" Charles Moore "  PAD SWAP CHARS MOVE

PAD 14 TYPE

PAD 14 8 FLIP

PAD 14 TYPE

PAD 14 6 FLIP

PAD 14 TYPE


A little about allot

CREATE names an aligned address but does not reserve space for data. One way to reserve space is to ALLOT it.

HERE ( -- addr ) returns an address
ALLOT ( n -- ) reserves space beginning at the address returned by HERE and moves the address that will be returned by the next use of HERE beyond that space
Try from the keyboard:
\ Creating may itself take up some space and move "here"
HERE U.

CREATE SOMETHING

HERE U.

SOMETHING U.

2 CELLS U.

SOMETHING 2 CELLS + U.

2 CELLS ALLOT

HERE U.

\ The Forth word VARIABLE could be:

: VARIABLE  ( -- )  CREATE  1 CELLS ALLOT ;

VARIABLE A

A U.

A ?

1 A !

A ?

CREATE INTEGERS 10 CELLS ALLOT

\ Address of ith integer
: INTEGER  ( i -- a-addr )  CELLS INTEGERS + ;

INTEGERS U.

0 INTEGER U.

1 INTEGER U.

\ From low to high
: INITIALIZE  ( -- )
   10 0 DO  I DUP INTEGER !  LOOP ;

: .INTEGERS  ( -- )
   10 0 DO  I INTEGER ?  LOOP ;

INITIALIZE .INTEGERS

\ Simple bubble sort from high to low

\ Is a cell's integer smaller than its next neighbor's?
: SMALLER ( a-addr -- flag )
   DUP @            \ get the integer
   SWAP CELL+ @     \ get the next integer
   <                \ 1st smaller than 2nd?
   ;

0 INTEGER ?

1 INTEGER ?

0 INTEGER SMALLER .

\ Trade values between addresses
: EXCHANGE  ( a-addr1 a-addr2 -- )
   2DUP 2>R  @ SWAP @  R> !  R> ! ;

0 INTEGER  1 INTEGER  EXCHANGE

0 INTEGER ?

1 INTEGER ?

\ Trade a cell's integer for its neighbor's
:  NSWAP  ( a-addr -- )
    DUP CELL+ EXCHANGE ;

0 INTEGER NSWAP

0 INTEGER ?

1 INTEGER ?
      
\ Upwardly mobile, using DO LOOP
: RISE  ( start_address #elements -- )
   0 DO 
      DUP SMALLER IF  DUP NSWAP  THEN  CELL+
   LOOP  DROP ;

: BUBBLE  ( start_address #elements)
   1- BEGIN  2DUP RISE  1- DUP  0= UNTIL  2DROP ;

.INTEGERS

INTEGERS 10 BUBBLE

.INTEGERS

CREATE PROGRAMMER  50 CHARS ALLOT

: FIRSTNAME  ( -- c-addr )  PROGRAMMER ;

: LASTNAME  ( -- c-addr )  PROGRAMMER  25 CHARS + ;

\ FILL  ( c-addr u c )  inserts u chars c beginning at c-addr
\ ACCEPT  ( c-addr u1 -- u2 )  receives up to u1 chars at c-addr
\  returns u2, the number of chars actually entered

\ New entry in a field
: GET  ( c-addr u -- )  2DUP BL FILL  ACCEPT DROP ;

: WHO?  ( -- )
   CR ." Your last name? "  LASTNAME 25 GET
   CR ." Your first name? "  FIRSTNAME 25 GET ;

\ -TRAILING  ( c-addr u1 - c-addr u2 )
\  "dash-trailing" removes trailing spaces 
: HELLO!  ( -- )
   CR ." Hello, "
   FIRSTNAME 25 -TRAILING TYPE  SPACE
   LASTNAME 25 -TRAILING TYPE
   [CHAR] ! EMIT ;

: FORMALITY  ( -- )  WHO? HELLO! ;

FORMALITY

PROGRAMMER 50 TYPE


Comma here !

Inserting data with , ("comma") or C, ("c-comma") also reserves data space:

, ( x -- ) acts like: HERE 1 CELLS ALLOT !
C, ( char -- ) acts like: HERE 1 CHARS ALLOT C!

HERE returns the address where the datum will be inserted. , inserts a number at the address and bumps the address one cell. C, inserts a character at the address and bumps the address one char.

Because , and C, insert different sized data, two words help to ensure that HERE returns an aligned address:

ALIGN ( -- ) ensures that HERE returns an aligned address
ALIGNED ( addr -- a-addr ) returns the first aligned address at or after addr
Try from the keyboard:
CREATE DATA

DATA U.

HERE U.

ALIGN HERE U.

HERE ALIGNED U.

1 , 2 , 3 ,

HERE U.

DATA U.

DATA ?

DATA CELL+ ?

DATA 2 CELLS + ?

CREATE CDATA

CDATA U.

HERE U.

CHAR L C,  CHAR e C,  CHAR o C,

CDATA 3 TYPE

HERE U.

HERE ALIGNED U.

HERE ALIGNED ALIGNED U.

HERE U.

ALIGN HERE U.

ALIGN ALIGN HERE U.

\ Another VARIABLE
: VARIABLE  ( -- )  CREATE  0 , ;

VARIABLE A

A U.

A ?

1 A !

A ?

\ Comma powers of 2
: 2^,  ( +n -- )
   1 SWAP 0 DO  DUP ,  2*  LOOP  DROP ;

\ n may depend on the number of bits in a cell
CREATE 2^N  16 2^,

\ 2 to the +nth power
: 2^  ( n -- 2^n )  CELLS 2^N + @ ;

0 2^ U.

1 2^ U.

8 2^ U.

Meet me at-xy

Better yet, get a Life that uses PAGE and AT-XY . Also see Meme Virtual Reality

Try from the keyboard:
: A  ( -- )  [CHAR] A EMIT ;

10 0 AT-XY A

0 10 AT-XY A

10 10 AT-XY A

PAGE

\ random number generator from Brodie, Starting Forth
VARIABLE RND   HERE RND !
: RANDOM  ( -- u )
   RND @  31421 *  6927 +  DUP RND ! ;
: CHOOSE  ( u - 0...u-1)
   RANDOM UM*  NIP ;

10 CHOOSE .

10 CHOOSE .

10 CHOOSE .

: FISH  ." ><>" ;

FISH

\ FISH eat FISH
\ Previous FISH no longer available to new words
\ 64 and 16 are screen dimensions
: FISH  ( +n -- )
   PAGE
   0 ?DO  64 CHOOSE 16 CHOOSE AT-XY FISH  LOOP ;

10 FISH

20 FISH

30 FISH

Does is as does does

CREATE names a data address.
DOES> ("duz") takes the address as an argument for the code that follows DOES> .

CREATE DOES> is used to make words that make other words that share code.

Try from the keyboard:

\ Yet another VARIABLE, in which DOES> does nothing.
: VARIABLE
   CREATE  ( -- )  0 ,
   DOES>  ( -- a-addr ) ;

VARIABLE X

VARIABLE Y

TRUE X !

X ?

FALSE Y !

Y ?

X @ Y @ X ! Y !

X ?

Y ?

: VSWAP  ( a-addr1 a-addr2 -- )
   2DUP 2>R  @ SWAP @  R> !  R> ! ;

X Y VSWAP

X ?

Y ?

\ CONSTANT could be:
: CONSTANT
   CREATE  ( x -- ) ,    \ comma x
   DOES>  ( -- x )  @ ;  \ fetch x

10 CONSTANT C

20 CONSTANT D

C .

D .

\ CREATE and DOES> don't have to be in the same word.

\ Yet another CONSTANT
: STEPIN  ( x -- )  CREATE , ;
: FETCHIT  ( -- x )  DOES> @ ;
: CONSTANT
   STEPIN  ( x -- )
   FETCHIT  ( -- x )
   ;

TRUE CONSTANT REDUNDANT

REDUNDANT TRUE = .

\ One-dimensional array
: ARRAY
   CREATE ( #elements -- )
    CELLS ALLOT
   DOES>  ( i -- a-addr )
    SWAP CELLS + ;

10 ARRAY A

0 A U.

1 A U.

10 0 A !

0 A ?

20 9 A !

9 A ?

\ Two-dimensional array
: 2ARRAY
   CREATE  ( x y -- )
    OVER ,          \ comma x
    * CELLS ALLOT
   DOES>  ( i j -- a-addr )
    DUP >R
    @             ( i j x )
    * + 1+ CELLS  ( offset )
    R> + ;

5 5 2ARRAY TABLE

0 0 TABLE U.

1 0 TABLE U.

0 1 TABLE U.

1 1 TABLE U.

1 0 0 TABLE !

0 0 TABLE ?

1024 4 4 TABLE !

4 4 TABLE ?

\ String constant
\ Move a string and make it a string for COUNTing
: PLACE  ( c-addr1 u c-addr2 -- )
   2DUP 2>R  CHAR+ SWAP CHARS MOVE  2R> C! ;

S" Nice pad! " PAD PLACE

PAD COUNT TYPE

\ comma a string as a counted string
: S,  ( c-addr u -- )
   HERE  OVER 1+ CHARS ALLOT  PLACE ;

: SCONSTANT  
   CREATE  ( c-addr u -- )  S,
   DOES>  ( -- c-addr u )  COUNT ;

S" How are you today?" SCONSTANT Howdy

Howdy TYPE

\ Array of string constants
\ Get keyboard input
: STRING  ( -- c-addr u )
   PAD DUP 84 ACCEPT ;

\ The array-making word
: SOME  
   CREATE  ( +n -- )
    BEGIN  ?DUP
    WHILE  CR STRING ?DUP
       IF  S, 1-  ELSE DROP THEN
    REPEAT
   DOES>  ( i -- c-addr u )
    SWAP  ( c-addr i )
    0 ?DO  COUNT +  LOOP  COUNT ;

3 SOME B

Bach

Beethoven

Berlioz

0 B TYPE

1 B TYPE

2 B TYPE


Words with :noname

:NONAME ( -- xt ), used instead of : <name>, creates a word with an execution token (xt) but no name. EXECUTEing the execution token evokes the word.

Try from the keyboard:
:NONAME  ( -- ) ." Anonymous" ;

CR  .( Call me )  EXECUTE  CHAR . EMIT



\ "commas" by Wil Baden
: ,S  ( x1 ... xn n -- )
   BEGIN ?DUP WHILE  DUP ROLL ,  1-  REPEAT ;

\ Fetch a value and bump the address
: @+  ( a-addr1 -- a-addr2 x )
   DUP CELL+  SWAP @ ;

\ The menu word
: COURSE 
   CREATE  ( x1 ... xn n -- )  DUP , ,S 
   DOES>  ( -- )  @+  0 ?DO  @+ EXECUTE  LOOP  DROP ;


\ Banquet ordered by Nathan Yuen

:NONAME  CR ." Bird Nest Soup" ;
:NONAME  CR ." Peking Duck with Steamed Buns" ;
:NONAME  CR ." Shrimp Canton" ;
:NONAME  CR ." Ginger Chicken" ;
:NONAME  CR ." Sam See Mein" ;
:NONAME  CR ." Spicy Mongolian Beef" ;
:NONAME  CR ." Black Bean Lobster" ;
:NONAME  CR ." Steamed Fish in Hot Oil" ;
:NONAME  CR ." Roast Pork" ;

9 COURSE MEAL

MEAL

Note: a master chef might have defined:

: DISH"   :NONAME  POSTPONE CR  POSTPONE ." POSTPONE ;  ;

Then:

DISH" Bird Nest Soup"

etc.

For you a lei

Mahalo nui loa to Kalinapuanani Cady for sending me the lyrics to Johnny Noble's song.

\ Begin in memory
: LEI  ( -- )  
   CREATE  \ something lovely
           \ current    next    previous
   HERE CELL+  DUP ,    DUP ,      ,
;

\ Delicate finger work
: NEEDLE  ( first new -- )   
   TUCK OVER ,         \ first is new's next
   OVER CELL+ @ DUP ,  \ last is new's previous
   !                   \ new is last's next
   CELL+ !             \ and first's previous
;

\ Ties a knot
: THREAD  ( lei -- )
   CELL+  ALIGN HERE  NEEDLE  ;

: FIRST  ( lei -- a-addr )
   DUP CELL+  TUCK SWAP ! ;
: LAST  ( lei -- a-addr )
   DUP CELL+ CELL+ @  TUCK SWAP ! ;
: AGAIN  ( lei -- a-addr )  @ ;
: NEXT  ( lei -- a-addr )
   DUP @ @  DUP ROT ! ;
: PREVIOUS  ( lei -- a-addr )
   DUP @ CELL+ @  DUP ROT ! ;

: PLACE  ( c-addr1 u c-addr2 -- )
   2DUP 2>R CHAR+ SWAP CHARS MOVE 2R> C! ;

: S, ( c-addr u -- )
   HERE OVER 1+ CHARS ALLOT PLACE ;

: MEMORY  ( -- )  0 PARSE  S, ;

: RECALL  ( a-addr -- )
   CELL+ CELL+ COUNT TYPE ;

LEI HAWAII

MEMORY HAWAIIAN MEMORIES

: F  HAWAII FIRST RECALL ;
: L  HAWAII LAST RECALL ;
: A  HAWAII AGAIN RECALL ;
: N  HAWAII NEXT RECALL ;
: P  HAWAII PREVIOUS RECALL ;
: O  HAWAII DUP NEXT DROP NEXT RECALL  ;
: U  HAWAII DUP PREVIOUS DROP PREVIOUS RECALL ;

: REMEMBER   HAWAII  THREAD MEMORY ;

REMEMBER Iliahi Street
REMEMBER Royal School
REMEMBER Cathedral School
REMEMBER Our Lady of Peace
REMEMBER DC-3
REMEMBER Wong Family Christmas
REMEMBER Nu`uanu Pali
REMEMBER Boy Scout Camp at Punalu`u
REMEMBER gau
REMEMBER 49th State Fair
REMEMBER St. Louis High School
REMEMBER The House of Intellect
REMEMBER Sister Adele Marie Lemon
REMEMBER Waipi`o

F

You must remember this

A kiss is just a kiss
A cell is just a cell
A char is just a char

But not in Forth.

Here's looking at you, Forth programmer. I think this could be the start of a beautiful friendship.

Play it again.

Albany NY +

Leo Wong hello@albany.net

Forth

Hello