Gforth Cheat Sheet Forth programmer’s guide · 937 words grouped by function · from the Gforth Manual

Pages 1–2 are a programmer’s guide to Forth as Gforth implements it: the text interpreter, stacks, defining words, control structures, locals, memory, structures, strings, exceptions, compile-time semantics, word lists, files, floating point, debugging and objects. The remaining pages index every word documented in the Gforth Manual by category. Type the filter box to narrow the index; hover a word for its stack effect and description.

Word set: core / core-ext other standard (float, string, file, tools…) gforth extension library / non-standard obsolescent
Source: Gforth Manual (version 0.7.9, Free Software Foundation) — https://gforth.org/manual/ · guide pages also draw on Leo Brodie, Starting FORTH (FORTH, Inc.). Hover any word for its stack effect, word set and description.

Forth Programmer's Guide

The language as Gforth implements it — syntax, semantics, idioms

Running Gforth

gforthinteractive; loads gforth.figforth file.fsinterpret file, then go interactivegforth file.fs -e byerun and exitgforth -e "1 2 + . cr bye"evaluate Forth on the command linegforth-fast prog.fsfaster engine, terser error messages
Engine optionEffect
-i / --image-file fLoad image f instead of gforth.fi
-p / --path pColon-separated search path (overrides GFORTHPATH)
-m -d -r -f -lSize of dictionary / data / return / FP / locals stack. Units b e k M G T, e.g. -m 16M
--die-on-signalExit on SIGSEGV etc. instead of turning it into a THROW
-h -vHelp · version
--no-rcSkip ~/.gforthrc

Startup files $GFORTH_ENV or ~/.gforthrc0 before the command line is processed; ~/.gforthrc after. gforth-itc is the indirect-threaded engine (compatibility).

Command-line editing

Ctrl-p / Ctrl-nprevious / next history line (also arrows)Ctrl-b / Ctrl-fcursor left / rightCtrl-a / Ctrl-estart / end of lineCtrl-h / Ctrl-xdelete left of / under cursorCtrl-kkill to end of lineTABcycle word completionsCtrl-don an empty line: bye

History is kept in ~/.gforth-history across sessions and is never trimmed — prune it yourself. history-file type prints its name.

Syntax & the Text Interpreter

Source is a stream of words separated by whitespace — nothing else. There is no grammar, no precedence, no reserved punctuation: +, :, 2dup and my-word! are all just names. Names are case-insensitive.

1 2 + . \ prints 3: square ( n -- n' ) dup * ;5 square . \ prints 25

What the interpreter does with each word

Word is…Interpret stateCompile state
a defined nameexecute itcompile a call to it
an immediate nameexecute itexecute it
a number literalpush itcompile it as a literal
unrecognisederror: name undefined (-13 throw)

: switches to compile state, ; back to interpret. state holds the flag; [ and ] switch it explicitly.

Comments

( stack effect -- like this )to the next ) — needs a space after (\ to end of lineneeds a space after \\G documentation commentlike \, tagged for doc extraction

The parse area

source( -- addr u ) current input buffer>in( -- addr ) offset into itparse-name( -- c-addr u ) next blank-delimited wordchar parse( char "ccc<char>" -- c-addr u )refill( -- flag ) read the next lines" ..." evaluateinterpret a string

Words such as :, ', s" and char consume text from the input stream at parse time. In stack comments this is written "name" and it is not on the stack.

Stacks & Stack Notation

Gforth keeps four stacks: the data stack (cells, chars, addresses, doubles), the FP stack (floats), the return stack (return addresses and your temporaries), and the locals stack.

( before -- after )top of stack is rightmost, as you type it( n1 n2 -- n3 )e.g. + consumes two, leaves one

Type letters used in stack effects

LetterMeansLetterMeans
nsigned integerrfloat (FP stack)
uunsigned integerfBoolean flag
wcell: integer or addressccharacter
d / uddouble-cell signed / unsignedxtexecution token
a-addrcell-aligned addressntname token
c-addrchar-aligned addresswidword list id
f-addrfloat-aligned addressiorI/O result (0 = ok)
i*x j*xany number of items"name"parsed from input, not on stack

Data stack

dup ?dup drop nipw -- w w · dup if nonzero · discard · drop 2ndswap over tuck rot -rotthe classicspick rollu -- w · indexed access (0 pick = dup)2dup 2drop 2swap 2over 2nip 2tuck 2rotcell pairsdepth .s clearstackcount · print non-destructively · empty

Return stack — borrow, always give back

>r r@ r> rdroppush · copy · pop · discard2>r 2r@ 2r> 2rdroppairs

Rule: whatever you >r inside a definition must come back before ; or exit, and must not straddle a loop boundary — counted loops keep their control parameters there too. Use unloop before exiting a loop.

Numbers & Bases

You typeInterpreter makes
42  -17single-cell integer (in current base)
3465.  3.465  34.65double-cell integer — all three are the same number; the . only marks it as double
1e  1.e0  +12.E-4float — the exponent letter is mandatory

Base prefixes (override base for one number)

$41 0x41hexadecimal → 65%1001101binary → 77#905 &905decimal → 905'A -'a'character code → 65, -97
decimal hexset base — but see belowbase @ dec.how to actually read the base['] foo 16 base-executerun foo in hex, restore base after

Traps. base @ . always prints 10 — use dec.. Prefer base-execute over hex/decimal so the caller’s base survives. Above base 14, 123E4 is ambiguous (Gforth reads it as an integer) — write 123E+4. Number conversion never checks overflow. The word bin is a file access mode, not a base.

Prefix conventions in word names

(none)signed singledsigned double
uunsigned singleud duunsigned double
ccharacterm ummixed single/double
2two cells (not necessarily a double)ffloating point

Forth does no type checking at all, at compile time or run time. -1 u. happily prints 18446744073709551615.

Arithmetic, Logic & Comparison

+ - * / mod /mod2 1 - is infix 2-1: operands keep their order1+ 1- 2* 2/ negate abs min max2/ is an arithmetic shift, not a divide*/ */modn1*n2/n3 with a double intermediate — use it for scalingm* um* m*/ m+mixed single→doublefm/mod sm/rem um/modd n -- rem quots>d d>s d+ d- dnegate dabs dmin dmaxdoubles

Division. / and /mod have undefined rounding for negative operands. With possibly-negative values use fm/mod (floored, remainder takes the divisor’s sign) or sm/rem (symmetric, remainder takes the dividend’s sign). Nothing is overflow-checked; division by zero may or may not be caught.

Bitwise

and or xor invertbitwise, on cellslshift rshiftrshift is logical (zero fill)

Flags & comparison

A canonical true is a cell with all bits set (−1), false is all bits clear. Any non-zero cell is treated as true by if. Because true is −1, and or xor invert double as logical operators on canonical flags.

= <> < > <= >=signedu< u> u<= u>=unsigned — use these for addresses0= 0<> 0< 0> 0<= 0>=compare against zero (faster)within( u1 u2 u3 -- f ) u1 in [u2,u3)d= d< du< …prefix d / du / f / f0 all combine with the six tests
true falseconstantsaddr on addr offstore true / false into a variable

0= is also the idiomatic logical NOT for canonical flags; invert is bit-flipping and only agrees with 0= for canonical flags.

Colon Definitions & Factoring

: name ( stack -- effect ) body ;: name body ; immediate \ runs in compile state too:noname body ; \ ( -- xt ) unnamed: name ... recursive ... name ; \ self-reference: name ... recurse ... ; \ or call self anonymously

A word is visible to later definitions only. Redefining a name does not change earlier callers — they keep calling the old one. That makes it safe to shadow a word, and it is why recursive/recurse exist.

exitearly return — clean up the return stack first;sthe primitive exit compilescompile-onlymark the last definition as not interpretablemarker foolater: foo forgets itself and everything after it

Factoring — the central discipline

Forth style is many small words, each doing one nameable thing, each tested interactively as you write it. A definition longer than a few lines is a smell. Factor when a phrase repeats, when a definition needs a comment in the middle, or when you cannot describe the word in one line.

: star [char] * emit ;: stars ( n -- ) 0 ?do star loop ;: margin cr 30 spaces ;: blip margin star ;: bar margin 5 stars ;: f bar blip bar blip blip cr ;

Designing the stack effect

  • Keep it small — one or two in, one out. If you are writing rot and -rot to reach an argument, factor differently or use locals.
  • Put the value that changes most often on top.
  • An iteration of a loop should leave the stack depth and types unchanged.
  • Write the stack comment first; it is the specification.

Test in the interpreter as you go: define a word, exercise it with .s, then move on. That interactive edit–test loop is most of Forth’s productivity claim.

Defining Words: variables, constants, CREATE…DOES>

variable vv gives an address; use @ and !2variable dv fvariable fvdouble · float5 constant fivefive pushes 55 value v5v5 pushes 5; 7 to v5 changes it7 to v5 2 +to v5assign · incrementcreate namename pushes the address of its bodydefer hook' impl is hook / action-of hook' new-xt old-xt defer!retarget a deferred word' foo alias barsecond name for the same code

CREATE … DOES> — making your own defining words

create builds the header and reserves data; does> supplies the run-time behaviour that every child will share. At child-execution time the address of the child’s body arrives on the stack.

: constant ( w "name" -- ) \ CONSTANT itself create , does> ( -- w ) @ ;: array ( n "name" -- ) \ indexed cell array create cells allot does> ( i -- addr ) swap cells + ;20 array data 3 data @ . 5 3 data !

code1 does> code2 splits into two times: code1 runs when the child is defined, code2 when the child is used. Children are compact — a data field plus a pointer to the shared does> code.

' child >bodyaddress of a DOES> child's data fieldlatestxtxt of the word just definednextname( c-addr u -- ) name the next definition from a stringnonamemake the next definition anonymous

Wrapping an existing defining word

: my: : latestxt postpone literal ['] stats compile, ;my: foo + - ; \ every foo-like word now calls stats

Quotations — inline anonymous code

[: ... ;]( -- xt ) at run time; body compiled inline[: dup . ;] swap executepass behaviour as a value

Conditionals & CASE

flag IF true-part THENflag IF true-part ELSE false-part THENflag ?dup-IF ... THEN \ better than "?dup if"AHEAD ... THEN \ unconditional forward jump

endif is a Gforth synonym for then. These words are compile-time only; they consume a control-flow item (orig/dest) on the control-flow stack, which is why they must nest properly.

: max ( n1 n2 -- n ) 2dup < if swap then drop ;: ?neg ( n -- n' ) dup 0< if negate then ;

CASE

CASE val1 OF code1 ENDOF val2 OF code2 ENDOF default-codeENDCASE \ drops the selector

of compares the selector against the value; on a match it drops both and runs the clause, otherwise it leaves the selector for the next test. endcase drops the selector at the end — so the default clause sees it on the stack.

Gforth’s extended CASE

WordDoes
?ofTakes a flag, not a value — arbitrary tests in a case
next-caseEnds the case by jumping back to case — turns it into a loop. Does not drop a cell
contofLike endof, but restarts the enclosing case instead of leaving it
: sgn ( n -- -1|0|1 ) case dup 0 < ?of drop -1 endof dup 0 > ?of drop 1 endof 0 endcase ; \ 0 for endcase to drop: gcd ( n1 n2 -- n ) case 2dup > ?of tuck - contof 2dup < ?of over - contof endcase ;

Portable implementations of the extensions live in compat/caseext.fs.

Loops

Indefinite

BEGIN code flag UNTIL \ loop while flag falseBEGIN code1 flag WHILE code2 REPEATBEGIN code AGAIN \ endless

Counted

limit start ?DO body LOOP \ start .. limit-1limit start ?DO body n +LOOP \ step by nlimit start +DO body LOOP \ skip if start > limitlimit start U+DO body LOOP \ unsigned versionlimit start -DO body u -LOOP \ count downu FOR body NEXT \ u times, index counts down
i j kindex of innermost / next / third loopLEAVE ?LEAVEexit the innermost loop nowUNLOOPdiscard loop parameters before EXIT
10 0 ?do i . loop0 1 2 3 4 5 6 7 8 94 0 +do i . 2 +loop0 2: t 10 0 ?do i dup . 3 = if unloop exit then loop ;0 1 2 3

Which one to use

  • Never DO. It always enters the loop; if the parameters ever become equal you get 264 iterations. Use ?DO at minimum.
  • ?DO still runs (wrapping around) when start > limit. +DO / U+DO skip the loop instead — usually what you meant.
  • Avoid n +LOOP with negative n; its termination rule is genuinely surprising. Use -DO-LOOP.
  • Loop control lives on the return stack: don’t read >r values pushed outside the loop from inside it, and balance anything you push inside before loop or before touching i.

+DO U+DO -DO U-DO -LOOP are Gforth extensions; standard implementations are in compat/loops.fs. Arbitrary control flow can be assembled by hand with CS-PICK / CS-ROLL on the control-flow stack.

Locals

: max { n1 n2 -- n3 } n1 n2 > if n1 else n2 endif ;

Everything after -- inside the braces is a comment. The declaration looks deliberately like a stack comment and usually replaces it. Locals are initialised from the stack, deepest first.

Type specifiers

Value flavourAddress flavourType
W: (default)W^cell
D:D^double
F:F^float
C:C^character

A value-flavoured local produces its value and can be changed with to. A variable-flavoured local (^) produces its address — which becomes invalid the moment its scope ends.

: cx* { F: Ar F: Ai F: Br F: Bi -- Cr Ci } \ complex * Ar Br f* Ai Bi f* f- Ar Bi f* Ai Br f* f+ ;: emit { C^ char* -- } char* 1 type ;

Gforth lets you declare locals anywhere in a colon definition, not just at the start. A local is visible from its declaration to the end of the definition, or to the end of the enclosing control structure if it was declared inside one. scopeendscope restrict visibility explicitly.

Locals are not visible in words you call — pass values on the stack. They cost a little speed, and heavy use of them tends to mean you should factor instead. The standard locals| syntax also exists but the manual recommends against it. Don’t mix brace-locals and brace-free stack comments in the same program — confusing them causes bugs that are hard to see.

Memory & the Dictionary

@ ! +!fetch · store · add-to, one cellc@ c!char2@ 2!two cells (double)f@ f! sf@ sf! df@ df!float · IEEE single · IEEE doublesw@ uw@ w! sl@ ul@ l!16- and 32-bit access

Address arithmetic — never assume a cell is 8 bytes

cell cells cell+size · n cells · add one cellchar+ chars float floats float+same for chars, floatsaligned faligned maxalignedround an address upalign falign maxalignround HERE up

Dictionary (data space)

here( -- addr ) next free locationallot( n -- ) reserve n address units (negative releases), c, f, 2,compile a cell / char / float / double and advanceunused( -- u ) space left
create buf 100 cells allot \ 100-cell buffercreate tbl 1 , 2 , 3 , 4 , \ initialised table: th ( n addr -- addr' ) swap cells + ;

Heap

allocate( u -- a-addr ior )resize( a-addr u -- a-addr' ior )free( a-addr -- ior )

Block operations

move( from to u -- ) handles overlapcmove cmove>low→high · high→low, char at a timefill erase blankfill with c · with 0 · with spacesbounds( addr u -- addr+u addr ) — feed a ?do looppad( -- c-addr ) scratch area, ≥84 chars, volatile
: sum ( addr u -- n ) 0 -rot cells bounds ?do i @ + 1 cells +loop ;

Structures & Records

Gforth structures

struct cell% field list-next cell% field list-valueend-struct list%

A field word turns the address of a record into the address of that field. A type descriptor such as list% leaves ( -- align size ) on the stack.

cell% char% float% dfloat% sfloat% double%built-in typesfoo% n *array of n foo%list% %allotreserve in the dictionary → addrlist% %alloc / %allocateon the heap (%allocate gives an ior)list% %size %alignmentquery the descriptoraddr n nalignedalign an address to n
: list-length ( list -- n ) 0 begin ( list1 n1 ) over while 1+ swap list-next @ swap repeat nip ;

Extending a structure — start from an existing one instead of struct, and you get an extended record (inheritance of layout):

list% cell% field intlist-intend-struct intlist%

If a structure contains floats, use foo% %allot constant name rather than create name foo% %allot drop — a created body is guaranteed cell-aligned but not float-aligned. Always write the first-field word even though its offset is zero: the package compiles nothing for it, and the code reads better.

Forth-200x structures

begin-structure point% field: p-x \ cell field: p-y cfield: p-flags \ char ffield: p-scale \ floatend-structure

Naming convention: prefix every field with the structure’s name (list-next, not next) — field names are global and generic ones collide.

Strings & Characters

The modern representation is address + count on the stack, ( c-addr u ). The old counted string keeps its length in the first byte and is passed as a single address.

s" text"( -- c-addr u ) works interpreting and compilings\" a\nb\t"with C-style escapes: \a \b \e \f \n \r \t \v \" \\ \x41c" text"( -- c-addr ) counted string." text"compile: print it at run time.( text)print it now, even while compilingcount( c-addr1 -- c-addr2 u ) counted → addr/counttype typewhiteprint u chars · print that many spaces
char A [char] A( -- c ) interpreting · compilingbl emit space spaces cr pagethe basicstoupper( c1 -- c2 )

Operating on strings

compare( a1 u1 a2 u2 -- n ) -1 / 0 / 1str= str< string-prefix?flag-returning comparisonssearch( a1 u1 a2 u2 -- a3 u3 f ) find substring/string( a u n -- a' u' ) drop n chars from the front-trailingstrip trailing blankssliteralcompile a string into the current definition

Dynamic strings ($ words, gforth)

A $-variable holds a pointer to a heap-allocated, length-prefixed buffer that grows as needed.

$variable sdeclareaddr u s $!store (frees the old buffer)s $@ s $@lenfetch contents · lengthaddr u s $+!append · c$+! appends one chars $free s $initrelease · reset to emptyaddr u char $split-- a1 u1 a2 u2s $. print

Conversion

s>number?( a u -- d f ) string → double, flag = success>number( ud a u -- ud' a' u' ) digit-at-a-time accumulation>float( a u -- r true | false )accept( c-addr n -- n' ) read a line from the user

Unicode: xchar words (xc@+, xchar+, x-width, xemit, xkey) handle multi-byte characters over the same byte buffers.

Number Output & Pictured Numerics

. u. d. ud.free format, trailing space.r u.r d.r ud.r( n width -- ) right-aligned in a fielddec. hex.force decimal · hex with a $ prefix?( a-addr -- ) print the cell at an addressf. fe. fs. fp.plain · engineering · scientific · SI prefixprecision set-precisionsignificant digits for f. fe. fs.

Pictured numeric output — build a string right to left

<#start; the number must be an unsigned double#( ud1 -- ud2 ) convert one digit#s( ud -- 0 0 ) convert all remaining digits (at least one)hold( char -- ) insert a literal charactersign( n -- ) insert '-' if n is negative#>( xd -- addr u ) finish, giving the string<<# #>>gforth: nestable hold area
: .$ ( n -- ) \ cents as dollars: 12345 -> $123.45 dup abs s>d <# # # [char] . hold #s rot sign [char] $ hold #> type space ;: .date ( d m y -- ) <# # # # # [char] - hold drop 0 # # [char] - hold drop 0 # # #> type ;

Digits come out least-significant first, which is why the picture is written in reverse. # divides by base, so the same code prints in any base. Convert single numbers with s>d (signed) or 0 (unsigned) first — and take abs before s>d if you plan to use sign.

Exceptions

throw( n -- ) nothing happens if n is 0catch( i*x xt -- j*x 0 | i*x n ) like execute, but catchesabort-1 throwflag abort" msg"-2 throw, printing msgexception( addr u -- n ) allocate a new throw code (-4095..-256)nothrowafter a catch you don't rethrow, to reset backtrace state

catch restores both stack depths to what they were on entry (the contents above that point are undefined), then pushes the throw code. throw unwinds however many call levels it needs to reach the nearest dynamically enclosing catch.

3 0 ' / catch .s \ leaves the /0 throw code: foo 100 throw ;: bar ['] foo catch ; \ bar . prints 100

Restoring state safely: try … restore … endtry

: safely save-x try word-changing-x 0 restore restore-x endtry throw ;

The catch-and-restore idiom (save ['] w catch restore throw) has a window between the catch and the restore where a Ctrl-C loses the cleanup. try/restore/endtry closes it: on an exception the stack depths are restored, the throw code is pushed, and execution resumes right after restore. Related: iferror (exception-only handler), endtry-iferror.

CodeMeaningCodeMeaning
−1abort−17Pictured output overflow
−2abort"−19Word name too long
−3Stack overflow−22Control structure mismatch
−4Stack underflow−23Address alignment
−5 / −6Return stack over/underflow−24Invalid numeric argument
−8Dictionary overflow−28User interrupt (Ctrl-C)
−9Invalid memory address−32Invalid name argument
−10Divide by zero−35Invalid block number
−13Undefined word−54FP underflow
−14Compile-only word interpreted−55FP unidentified fault
−4095…−256 are yours via exception; positive codes are free for applications; iors are throwable directly.

Compile Time: IMMEDIATE, POSTPONE, tokens

Every word has two behaviours: interpretation semantics (what happens when the text interpreter meets it in interpret state) and compilation semantics (what happens in compile state). For an ordinary word the latter is “compile a call”. immediate makes them the same: execute now, even while compiling.

immediateapplied to the word just definedstate( -- a-addr ) non-zero while compiling[ ]leave / enter compile state inside a definitionliteral( n -- ) compile a value computed at compile time2literal fliteral sliteraldouble · float · string]Lgforth shorthand for ] literal
: seconds-per-year [ 365 24 * 60 * 60 * ] literal ;

POSTPONE — “compile the compilation semantics of”

Inside a definition, postpone x arranges that your word, when it runs, does what x would have done at compile time. For an ordinary x that means compiling a call to x; for an immediate x it means executing x. It is the one tool that works for both.

: my-if postpone if ; immediate: endif postpone then ; immediate]] dup + ; [[gforth: postpone a whole sequence]]L ]]2L ]]FL ]]SLpostponed literals

Tokens for words

TokenGet it withUse it with
xt execution token' name (interpreting), ['] name (compiling), :noname, latestxt, [: ;]execute, compile,, catch, is, >body
nt name tokenfind-name, latest, >namename>interpret, name>compile, name>string, id.
w xt compilation tokencomp' name, [comp'] namepostpone,
' foo executesame as running foo' foo compile,lay down a call to foo now' foo defer!/ ' foo is hook

Conditional compilation

flag [IF] ... [ELSE] ... [THEN][IFDEF] name ... [THEN] [IFUNDEF] name ... [THEN]

Word Lists & the Search Order

Names live in word lists. Two things are separately settable: the search order (a stack of word lists consulted when a name is looked up, first entry searched first) and the compilation word list (where new definitions go).

wordlist( -- wid ) create a new empty word listget-order( -- widn .. wid1 n ) wid1 is searched firstset-order( widn .. wid1 n -- ) n=-1 → minimum orderget-current set-currentthe compilation word list>order previouspush / drop the top of the search orderalso definitionsduplicate top · compile into the top word listonly Forth Rootminimum order · replace top with forth-wordlist · rootorder words vocsshow order · list top word list · list vocabulariessealdrop everything but the top word list
vocabulary editor \ a named word listalso editor definitions : delete ... ; \ lands in EDITOR, hides globalprevious definitions
find-name( c-addr u -- nt | 0 ) search the current orderfind-name-in( c-addr u wid -- nt | 0 )search-wordlist( c-addr u wid -- 0 | xt +-1 )forth-wordlist( -- wid ) the standard words

Why bother

  • Keep an application’s internal words out of the global namespace.
  • Reuse a natural name (delete, list, next) that is already taken.
  • Build symbol tables: table and cs-wordlist make case-sensitive word lists you can look up at run time.

Gforth is case-insensitive by default. table / cs-vocabulary give you case-sensitive lookup without warnings, which is what you want for data rather than code.

Files, Blocks & Loading Source

Loading Forth source

include file.fsinterpret it now, every timerequire file.fsonly if not already loaded — use thisneeds file.fsalias for requireincluded( c-addr u -- ) string versionrequired( c-addr u -- ) string versionsourcefilename sourceline#where am I?fpath path+ path= .paththe Forth source search path

Files

r/o w/o r/w binaccess modes; bin modifies the othersopen-file( c-addr u fam -- fid ior )create-file( c-addr u fam -- fid ior ) truncatesclose-file flush-file( fid -- ior )read-file( c-addr u1 fid -- u2 ior )read-line( c-addr u1 fid -- u2 flag ior ) flag=0 at EOFwrite-file write-line( c-addr u fid -- ior )file-position reposition-fileud — seekfile-size resize-fileuddelete-file rename-file file-statushousekeepingslurp-file( c-addr1 u1 -- c-addr2 u2 ) whole file by namestdin stdout stderr( -- fid )
: cat ( c-addr u -- ) r/o open-file throw >r begin pad 200 r@ read-line throw while pad swap type cr repeat drop r> close-file throw ;

Every file word returns an ior: 0 on success, otherwise a code you can throw directly. read-line’s flag is false at end of file — check it, not just the ior.

Other I/O

open-pipe close-pipe( c-addr u fam -- fid ior )outfile-execute infile-execute( xt fid -- ) redirectopen-dir read-dir close-dir get-dir set-dirdirectoriess" ls -l" system sh ls -lrun a shell command; $? has the statusgetenv( c-addr u -- c-addr u )next-arg arg argc shift-argscommand-line arguments

Blocks (the classic screen-based store)

use file.fb / open-blocksselect the blocks fileu block( u -- a-addr ) 1024-byte bufferu list / scrshow block as 16×64 · last listedu load n1 n2 thruinterpret block(s)update save-buffers empty-buffers flushwrite-back control

Floating Point

Gforth keeps floats on a separate stack. A stack comment shows both stacks in one notation, so ( n -- r ) means “takes a cell from the data stack, leaves a float on the FP stack”. Literals need an exponent: 1e, 3.14e0, 1.6e-19.

fdup fdrop fswap fover frot fnip ftuck fpickFP stack shufflingfdepth f.sdepth · non-destructive prints>f d>f f>s f>dconversions to and from integers
f+ f- f* f/ fnegate fabs f** fsqrtarithmeticf2* f2/ 1/ftimes 2 · halve · reciprocalfmin fmax floor froundfloor rounds toward −∞fexp fln flog falog fexpm1 flnp1exp/log; the m1/p1 forms are accurate near 0fsin fcos ftan fsincos fatan2trig; angles in radiansfasin facos fatan fsinh … fatanhinverse & hyperbolicpi( -- r )

Comparison — don’t use f=

f~abs( r1 r2 r3 -- f ) |r1-r2| < r3f~rel( r1 r2 r3 -- f ) |r1-r2| < r3*|r1+r2|f~standard medley: r3>0 → abs, r3=0 → bitwise, r3<0 → relativef< f> f<= f>= f0< f0=ordered comparisons are fine

Memory & output

f@ f! float floats float+ faligned falignnative formatsf@ sf! df@ df!IEEE single / double — for external dataf. fe. fs. fp.plain · engineering · scientific · SI prefixesset-precision( u -- ) significant digits>float( c-addr u -- r true | false ) parsef.rdp f>str-rdpfixed-width formatted output
: dot ( addr1 addr2 u -- r ) \ dot product 0e -rot 0 ?do over f@ over f@ f* f+ [ 1 floats ] literal under+ swap [ 1 floats ] literal + swap loop 2drop ;

Classic Forth style prefers scaled integers and */ to floats: exact, fast, and enough for most measurement work. Reach for floats when the dynamic range genuinely demands it.

Debugging & Inspection

.sprint the data stack without consuming it — your main toolf.ssame for the FP stackdepth fdepth clearstackscounts · empty both stacksa-addr ?print the cell at an addressaddr u dumphex dumpsee namedecompile a definitionxt-see simple-see see-codevariants, down to native codewords vocs orderwhat is defined · where

Tracing

~~drop it anywhere: prints file:line and the stack~~bt ~~1btalso print a backtrace · only once+ltrace -ltraceline tracing on / offdbg namesingle-step through a definitionbreak: break" msg"breakpoints inside a definition??? WTF??open a debugging shell · with backtrace and stack dump~~Variable v ~~Value xreport every access

Assertions

assert( depth 2 >= )same as assert1(assert0( ... )always onassert1( ... )on by defaultassert2( ... ) assert3( ... )debugging · expensive checksassert-level( -- a-addr ) everything above this is compiled out

Undoing

marker marklater: mark removes itself and all later definitions' foo ' bar replace-wordmake bar run foo (both colon definitions)

Reading an error message

./xxx.fs:4: Invalid memory address>>>bar<<<Backtrace:$400E664C @$400E6664 foo

File and line, then the offending word between >>> and <<<, then a return-stack dump read as a backtrace — innermost first, so here bar called foo called @. Names are guesses (return-stack entries are not tagged), so occasional false or missing entries are normal.

The backtrace shown is the one for the first throw after the last catch — put nothrow after a catch that does not rethrow, or you will get a backtrace for the wrong error. Use gforth (not gforth-fast) while developing: it reports stack underflows and division errors the fast engine may miss entirely, and can produce backtraces from primitives.

Object-Oriented Forth

Three packages ship with Gforth. objects.fs is the recommended one: classes, single inheritance, interfaces, late binding through selectors.

objects.fs

object class \ parent on the stack selector draw ( x y graphical -- )end-class graphicalgraphical class cell% field circle-radius :noname ( x y circle -- ) circle-radius @ draw-circle ; overrides draw :noname ( n circle -- ) circle-radius ! ; overrides constructend-class circle50 circle heap-new constant my-circle100 100 my-circle draw
WordRole
classend-classStart / finish a class, given its parent
selectorDeclare a late-bound method name (receiver on TOS)
overridesBind an xt to a selector in this class
field, inst-var,
inst-value
Instance data
heap-new / dict-newAllocate + construct an object
construct / init-objectInitialise fields / a raw memory chunk
m:;m, :mMethod definition; binds this
this, exitmReceiver · early return from a method
bind, [bind],
[parent], [current]
Early (static) binding to a named class’s method
interface
end-interface, implementation
Multiple interfaces without multiple inheritance
protected / publicHide / expose during class definition

You may only invoke a selector on an object of the class where it was declared, or a descendant. Immediately before end-class the search order must match what it was after class.

mini-oof.fs — the whole model in a few lines

object class method draw cell var radius end-class circle:noname ( circle -- ) ... ; circle defines drawcircle new constant c

oof.fs

A third model with classhow:class;, new, init, dispose, super, self, bind, withendwith, instance pointers (ptr, asptr) and early binding (early). Closer to conventional OO languages; see the manual’s comparison table before choosing.

Idioms & Gotchas

Idioms worth memorising

addr u bounds ?do ... loopwalk a byte buffer0 addr u cells bounds ?do i @ + 1 cells +loopsum an arraydup 0< if negate thenabs, by hand2dup < if swap then dropmax, by hand?dup-if ... thenact only on a non-zero value, keeping it>r ... r>park one value out of the way (never across a loop)['] word catch ?dup if ... thenguarded call[ 60 60 * ] literalcompute at compile time: th cells + ;index into a cell arraybegin ... key? untilpoll for a keystrokes" file" r/o open-file throwior straight into throw

Gotchas

  • Space after everything. ( x -- y) is fine but (x -- y) is an undefined word (x. Same for ."text" and \comment.
  • ." is compile-time only in a strict reading; use .( to print while interpreting.
  • s" buffers are transient. Forth-2012 guarantees only two 80-character buffers — assume a string lives until the next-but-one s". Copy it if it must survive. (Gforth actually allocates, which leaks if you evaluate strings containing s" in a loop.)
  • pad moves. It sits above here, so any allot or new definition relocates it.
  • Cells are not bytes. Always write cells, cell+, chars — never 8 *.
  • Compare addresses with u<, not <: addresses above half the address space look negative.
  • base is global state. Change it and every subsequent number in the file reads differently. Use base-execute.
  • Redefinition does not retro-patch. Words already compiled keep calling the old definition — recompile the callers, or use defer for a hook you intend to change.
  • Return-stack discipline is not checked. An unbalanced >r returns to the wrong place, usually with a spectacular crash far from the cause.
  • Locals are not visible in called words, and ^-flavoured local addresses die at the end of scope.
  • DO vs ?DO: equal parameters make DO loop essentially forever.

When something goes wrong, the first three things to try are .s, see the word, and drop a ~~ in the middle.

Starting FORTH → Gforth

Leo Brodie’s Starting FORTH teaches a FORTH-79 / polyFORTH system. Almost everything transfers, but a few things differ in a modern Standard Forth such as Gforth.

Book usesIn Gforth
Screens, LIST, LOAD, the line editorText files: require file.fs, your own editor. Blocks still work (use, list, load, thru) if you want them
NOT0= for logical negation, invert for bitwise. NOT is not standard
ENDIFTHEN (Gforth accepts endif too)
DOLOOPUse ?DO or +DO — see the Loops card
FORGET wordmarker — define a mark, execute it to roll back
VARIABLE with an initial value (0 VARIABLE X)variable x takes no value; use 0 value x or variable x 0 x !
ASCII cchar c interpreting, [char] c compiling
<BUILDS … DOES>create … does>
." text" while interpreting.( text)
-TRAILING, COUNT, <# # #S #>, */, >R R>Unchanged — these are all still standard
QUIT, ABORT", PAGE, SPACES, KEY, EXPECTAll present; expect is obsolescent, use accept
16-bit cells, 2 * for cell size64-bit cells — always use cells and cell+

Still exactly right in the book: postfix thinking, factoring into tiny words, keeping the stack shallow, the fixed-point/scaling philosophy (*/ and rational approximation instead of floats), pictured numeric output, CREATE…DOES> as the way to extend the compiler, and the habit of testing every word at the terminal as you write it.

A non-destructive stack print (the book’s Handy Hint) is built in here as .s.

Word Index

Every word documented in the Gforth Manual, grouped by what it does

Stack

47

Data stack

dropw --
nipw1 w2 -- w2
dupw -- w w
overw1 w2 -- w1 w2 w1
tuckw1 w2 -- w2 w1 w2
swapw1 w2 -- w2 w1
pickS:... u -- S:... w
rotw1 w2 w3 -- w2 w3 w1
-rotw1 w2 w3 -- w3 w1 w2
?dupw -- S:... w
rollx0 x1 .. xn n -- x1 .. xn x0
2dropw1 w2 --
2nipw1 w2 w3 w4 -- w3 w4
2dupw1 w2 -- w1 w2 w1 w2
2overw1 w2 w3 w4 -- w1 w2 w3 w4 w1 w2
2tuckw1 w2 w3 w4 -- w3 w4 w1 w2 w3 w4
2swapw1 w2 w3 w4 -- w3 w4 w1 w2
2rotw1 w2 w3 w4 w5 w6 -- w3 w4 w5 w6 w1 w2

Return stack

>rw -- R:w
r>R:w -- w
r@-- w ; R: w -- w
rdropR:w --
2>rw1 w2 -- R:w1 R:w2
2r>R:w1 R:w2 -- w1 w2
2r@R:w1 R:w2 -- R:w1 R:w2 w1 w2
2rdropR:w1 R:w2 --

FP stack

floating-stack-- n
fdropr --
fnipr1 r2 -- r2
fdupr -- r r
foverr1 r2 -- r1 r2 r1
ftuckr1 r2 -- r2 r1 r2
fswapr1 r2 -- r2 r1
fpickf:... u -- f:... r
frotr1 r2 r3 -- r2 r3 r1

Stack pointers

sp0-- a-addr
sp@S:... -- a-addr
sp!a-addr -- S:...
fp0-- a-addr
fp@f:... -- f-addr
fp!f-addr -- f:...
rp0-- a-addr
rp@-- a-addr
rp!a-addr --
lp0-- a-addr
lp@-- addr
lp!c-addr --

Arithmetic

41

Single precision

+n1 n2 -- n
1+n1 -- n2
under+n1 n2 n3 -- n n2
-n1 n2 -- n
1-n1 -- n2
*n1 n2 -- n
/n1 n2 -- n
modn1 n2 -- n
/modn1 n2 -- n3 n4
negaten1 -- n2
absn -- u
minn1 n2 -- n
maxn1 n2 -- n
FLOORED-- f

Bitwise

andw1 w2 -- w
orw1 w2 -- w
xorw1 w2 -- w
invertw1 -- w2
lshiftu1 n -- u2
rshiftu1 n -- u2
2*n1 -- n2
d2*d1 -- d2
2/n1 -- n2
d2/d1 -- d2

Double precision

s>dn -- d
d>sd -- n
d+d1 d2 -- d
d-d1 d2 -- d
dnegated1 -- d2
dabsd -- ud
dmind1 d2 -- d
dmaxd1 d2 -- d

Mixed precision

m+d1 n -- d2
*/n1 n2 n3 -- n4
*/modn1 n2 n3 -- n4 n5
m*n1 n2 -- d
um*u1 u2 -- ud
m*/d1 n2 u3 -- dquot
um/modud u1 -- u2 u3
fm/modd1 n1 -- n2 n3
sm/remd1 n1 -- n2 n3

Comparison & Flags

37

Boolean flags

true-- f
false-- f
ona-addr --
offa-addr --

Numeric comparison

<n1 n2 -- f
<=n1 n2 -- f
<>n1 n2 -- f
=n1 n2 -- f
>n1 n2 -- f
>=n1 n2 -- f
0<n -- f
0<=n -- f
0<>n -- f
0=n -- f
0>n -- f
0>=n -- f
u<u1 u2 -- f
u<=u1 u2 -- f
u>u1 u2 -- f
u>=u1 u2 -- f
withinu1 u2 u3 -- f
d<d1 d2 -- f
d<=d1 d2 -- f
d<>d1 d2 -- f
d=d1 d2 -- f
d>d1 d2 -- f
d>=d1 d2 -- f
d0<d -- f
d0<=d -- f
d0<>d -- f
d0=d -- f
d0>d -- f
d0>=d -- f
du<ud1 ud2 -- f
du<=ud1 ud2 -- f
du>ud1 ud2 -- f
du>=ud1 ud2 -- f

Floating Point

55

Floating point

s>fn -- r
d>fd -- r
f>sr -- n
f>dr -- d
f+r1 r2 -- r3
f-r1 r2 -- r3
f*r1 r2 -- r3
f/r1 r2 -- r3
fnegater1 -- r2
fabsr1 -- r2
fmaxr1 r2 -- r3
fminr1 r2 -- r3
floorr1 -- r2
froundr1 -- r2
f**r1 r2 -- r3
fsqrtr1 -- r2
fexpr1 -- r2
fexpm1r1 -- r2
flnr1 -- r2
flnp1r1 -- r2
flogr1 -- r2
falogr1 -- r2
f2*r1 -- r2
f2/r1 -- r2
1/fr1 -- r2
fsinr1 -- r2
fcosr1 -- r2
fsincosr1 -- r2 r3
ftanr1 -- r2
fasinr1 -- r2
facosr1 -- r2
fatanr1 -- r2
fatan2r1 r2 -- r3
fsinhr1 -- r2
fcoshr1 -- r2
ftanhr1 -- r2
fasinhr1 -- r2
facoshr1 -- r2
fatanhr1 -- r2
pi-- r
f~relr1 r2 r3 -- flag
f~absr1 r2 r3 -- flag
f~r1 r2 r3 -- flag
f=r1 r2 -- f
f<>r1 r2 -- f
f<r1 r2 -- f
f<=r1 r2 -- f
f>r1 r2 -- f
f>=r1 r2 -- f
f0<r -- f
f0<=r -- f
f0<>r -- f
f0=r -- f
f0>r -- f
f0>=r -- f

Memory

71

Access

@a-addr -- w
!w a-addr --
+!n a-addr --
c@c-addr -- c
c!c c-addr --
2@a-addr -- w1 w2
2!w1 w2 a-addr --
f@f-addr -- r
f!r f-addr --
sf@sf-addr -- r
sf!r sf-addr --
df@df-addr -- r
df!r df-addr --
sw@c-addr -- n
uw@c-addr -- u
w!w c-addr --
sl@c-addr -- n
ul@c-addr -- u
l!w c-addr --

Address arithmetic

charsn1 -- n2
char+c-addr1 -- c-addr2
cellsn1 -- n2
cell+a-addr1 -- a-addr2
cell-- u
alignedc-addr -- a-addr
floatsn1 -- n2
float+f-addr1 -- f-addr2
float-- u
falignedc-addr -- f-addr
sfloatsn1 -- n2
sfloat+sf-addr1 -- sf-addr2
sfalignedc-addr -- sf-addr
dfloatsn1 -- n2
dfloat+df-addr1 -- df-addr2
dfalignedc-addr -- df-addr
maxalignedaddr1 -- addr2
cfalignedaddr1 -- addr2
ADDRESS-UNIT-BITS-- n
/w-- u
/l-- u

Dictionary allocation

here-- addr
unused-- u
allotn --
c,c --
f,f --
,w --
2,w1 w2 --
align--
falign--
sfalign--
dfalign--
maxalign--
cfalign--

Heap

allocateu -- a-addr wior
freea-addr -- wior
resizea-addr1 u -- a-addr2 wior

Blocks of memory

movec-from c-to ucount --
eraseaddr u --
cmovec-from c-to u --
cmove>c-from c-to u --
fillc-addr u c --
blankc-addr u --
comparec-addr1 u1 c-addr2 u2 -- n
str=c-addr1 u1 c-addr2 u2 -- f
str<c-addr1 u1 c-addr2 u2 -- f
string-prefix?c-addr1 u1 c-addr2 u2 -- f
searchc-addr1 u1 c-addr2 u2 -- c-addr3 u3 flag
-trailingc_addr u1 -- c_addr u2
/stringc-addr1 u1 n -- c-addr2 u2
boundsaddr u -- addr+u addr
pad-- c-addr

Defining Words

33

Colon & CREATE

:"name" -- colon-sys
;compilation colon-sys -- ; run-time nest-sys
Create"name" --
DOES>compilation colon-sys1 -- colon-sys2
>bodyxt -- a_addr
const-does>run-time: w*uw r*ur uw ur "name" --

Variables & constants

Variable"name" --
2Variable"name" --
fvariable"name" --
User"name" --
Constantw "name" --
2Constantw1 w2 "name" --
fconstantr "name" --
Valuew "name" --
TOvalue "name" --
+TOvalue "name" --
addr"name" -- addr

Deferred & anonymous

Defer"name" --
defer!xt xt-deferred --
ISvalue "name" --
defer@xt-deferred -- xt
action-ofinterpretation "name" -- xt; compilation "name" -- ; run-time -- xt
deferscompilation "name" -- ; run-time ... -- ...
:noname-- xt colon-sys
noname--
latestxt-- xt
Aliasxt "name" --
nextnamec-addr u --

Quotations

[:compile-time: -- quotation-sys flag colon-sys
;]compile-time: quotation-sys -- ; run-time: -- xt

Comments

(compilation ’ccc<close-paren>’ -- ; run-time --
\compilation ’ccc<newline>’ -- ; run-time --
\Gcompilation ’ccc<newline>’ -- ; run-time --

Control Structures

56

Conditionals, loops, CASE

IFcompilation -- orig ; run-time f --
AHEADcompilation -- orig ; run-time --
THENcompilation orig -- ; run-time --
BEGINcompilation -- dest ; run-time --
UNTILcompilation dest -- ; run-time f --
AGAINcompilation dest -- ; run-time --
CS-PICK... u -- ... destu
CS-ROLLdestu/origu .. dest0/orig0 u -- .. dest0/orig0 destu/origu
ELSEcompilation orig1 -- orig2 ; run-time --
WHILEcompilation dest -- orig dest ; run-time f --
REPEATcompilation orig dest -- ; run-time --
ENDIFcompilation orig -- ; run-time --
?dup-IFcompilation -- orig ; run-time n -- n|
?DUP-0=-IFcompilation -- orig ; run-time n -- n|
?DOcompilation -- do-sys ; run-time w1 w2 -- | loop-sys
+DOcompilation -- do-sys ; run-time n1 n2 -- | loop-sys
U+DOcompilation -- do-sys ; run-time u1 u2 -- | loop-sys
-DOcompilation -- do-sys ; run-time n1 n2 -- | loop-sys
U-DOcompilation -- do-sys ; run-time u1 u2 -- | loop-sys
DOcompilation -- do-sys ; run-time w1 w2 -- loop-sys
FORcompilation -- do-sys ; run-time u -- loop-sys
LOOPcompilation do-sys -- ; run-time loop-sys1 -- | loop-sys2
+LOOPcompilation do-sys -- ; run-time loop-sys1 n -- | loop-sys2
-LOOPcompilation do-sys -- ; run-time loop-sys1 u -- | loop-sys2
NEXTcompilation do-sys -- ; run-time loop-sys1 -- | loop-sys2
LEAVEcompilation -- ; run-time loop-sys --
?LEAVEcompilation -- ; run-time f | f loop-sys --
unloopR:w1 R:w2 --
DONEcompilation orig -- ; run-time --
casecompilation -- case-sys ; run-time --
endcasecompilation case-sys -- ; run-time x --
next-casecompilation case-sys -- ; run-time --
ofcompilation -- of-sys ; run-time x1 x2 -- |x1
?ofcompilation -- of-sys ; run-time f --
endofcompilation case-sys1 of-sys -- case-sys2 ; run-time --
contofcompilation case-sys1 of-sys -- case-sys2 ; run-time --

Loop indices

iR:n -- R:n n
jR:w R:w1 R:w2 -- w R:w R:w1 R:w2
kR:w R:w1 R:w2 R:w3 R:w4 -- w R:w R:w1 R:w2 R:w3 R:w4

Calls & returns

recursivecompilation -- ; run-time --
recurse
EXITcompilation -- ; run-time nest-sys --
;sR:w --

Exceptions

throwy1 .. ym nerror -- y1 .. ym / z1 .. zn error
exceptionaddr u -- n
catch... xt -- ... n
nothrow--
trycompilation -- orig ; run-time -- R:sys1
endtrycompilation -- ; run-time R:sys1 --
iferrorcompilation orig1 -- orig2 ; run-time --
restorecompilation orig1 -- ; run-time --
endtry-iferrorcompilation orig1 -- orig2 ; run-time R:sys1 --
ABORT"compilation ’ccc"’ -- ; run-time f --
abort?? -- ??
WARNING"compilation ’ccc"’ -- ; run-time f --
warnings-- addr

Locals

19

Scope

scopecompilation -- scope ; run-time --
endscopecompilation scope -- ; run-time --
UNREACHABLE--
ASSUME-LIVEorig -- orig
(local)addr u --

Implementation

@local##noffset -- w
f@local##noffset -- r
laddr##noffset -- c-addr
lp+!##noffset --
lp!c-addr --
>lw --
f>lr --
compile-lp+!n --
lp+!#
?branch-lp+!#
lp+!#
common-listlist1 list2 -- list3
sub-list?list1 list2 -- f
list-sizelist -- u

Compiling & Semantics

53

Immediacy

immediate--
compile-only--
restrict--

Literals

[--
]--
Literalcompilation n -- ; run-time -- n
]Lcompilation: n -- ; run-time: -- n
2Literalcompilation w1 w2 -- ; run-time -- w1 w2
FLiteralcompilation r -- ; run-time -- r
SLiteralCompilation c-addr1 u ; run-time -- c-addr2 u

Macros & POSTPONE

postpone"name" --
]]--
[[--
]]Lpostponing: x -- ; compiling: -- x
]]2Lpostponing: x1 x2 -- ; compiling: -- x1 x2
]]FLpostponing: r -- ; compiling: -- r
]]SLpostponing: addr1 u -- ; compiling: -- addr2 u
compile,xt --

Combined words

interpret/compile:interp-xt comp-xt "name" --

Execution token

'"name" -- xt
[']compilation. "name" -- ; run-time. -- xt
executext --
performa-addr --

Compilation token

[COMP']compilation "name" -- ; run-time -- w xt
COMP'"name" -- w xt
postpone,w xt --

Name token

find-namec-addr u -- nt | 0
find-name-inc-addr u wid -- nt | 0
latest-- nt
>namext -- nt|0
name>interpretnt -- xt|0
name>compilent -- w xt
name>intnt -- xt
name?intnt -- xt
name>compnt -- w xt
name>stringnt -- addr count
id.nt --
.nament --
.idnt --

Threading internals

threading-method-- n
>code-addressxt -- c_addr
code-address!c_addr xt --
>does-codext -- a_addr
does-code!a-addr xt --
/does-handler-- n
docol:-- addr
docon:-- addr
dovar:-- addr
douser:-- addr
dodefer:-- addr
dofield:-- addr
>definerxt -- definer
definer!definer xt --

Text Interpreter & Input

52

Interpreter state

>in-- addr
source-- addr u
tib-- addr
#tib-- addr

Input sources

source-id-- 0 | -1 | fileid
blk-- addr
save-input-- x1 .. xn n
restore-inputx1 .. xn n -- flag
evaluate... addr u -- ...
query--

Parsing the stream

parsechar "ccc<char>" -- c-addr u
parse-name"name" -- c-addr u
parse-word-- c-addr u
name-- c-addr u
wordchar "<chars>ccc<char>-- c-addr
refill-- flag
execute-parsing... addr u xt -- ...
execute-parsing-filei*x fileid xt -- j*x

Number conversion

dpl-- a-addr
base-executei*x xt u -- j*x
base-- a-addr
hex--
decimal--

Line input

acceptc-addr +n1 -- +n2
edit-linec-addr n1 n2 -- n3
s>number?addr u -- d f
s>unumber?c-addr u -- ud flag
>numberud1 c-addr1 u1 -- ud2 c-addr2 u2
>floatc-addr u -- f:... flag
>float1c-addr u c -- f:... flag
convertud1 c-addr1 -- ud2 c-addr2
expectc-addr +n --
span-- c-addr

Conditional compilation

[IF]flag -- / parser
[ELSE]--
[THEN]--
[ENDIF]--
[IFDEF]"<spaces>name" --
[IFUNDEF]"<spaces>name" --
[?DO]n-limit n-index --
[DO]n-limit n-index --
[FOR]n --
[LOOP]--
[+LOOP]n --
[NEXT]n --
[BEGIN]--
[UNTIL]flag --
[AGAIN]--
[WHILE]flag --
[REPEAT]--

Recognizers

get-recognizers-- xt1 .. xtn n
set-recognizersxt1 .. xtn n

Output

47

Numeric output

.n --
dec.n --
hex.u --
u.u --
.rn1 n2 --
u.ru n --
d.d --
ud.ud --
d.rd n --
ud.rud n --
f.r --
fe.r --
fs.r --
fp.r --
precision-- u
set-precisionu --
f.rdprf +nr +nd +np --

Pictured numeric output

<#--
<<#--
#ud1 -- ud2
#sud -- 0 0
holdchar --
signn --
#>xd -- addr u
#>>--
representr c-addr u -- n f1 f2
f>str-rdprf +nr +nd +np -- c-addr nr
f>buf-rdprf c-addr +nr +nd +np --

Characters & strings

bl-- c-char
space--
spacesu --
emitc --
toupperc1 -- c2
."compilation ’ccc"’ -- ; run-time --
.(compilation&interpretation "ccc<paren>" --
.\"compilation ’ccc"’ -- ; run-time --
typec-addr u --
typewhiteaddr n --
cr--
S"compilation ’ccc"’ -- ; run-time -- c-addr u
s\"compilation ’ccc"’ -- ; run-time -- c-addr u
C"compilation "ccc<quote>" -- ; run-time -- c-addr
char’<spaces>ccc’ -- c
[Char]compilation ’<spaces>ccc’ -- ; run-time -- c

Terminal

at-xyx y --
form
page--

Strings

53

Counted strings

countc-addr1 -- c-addr2 u

Dynamic $-strings

deletebuffer size u --
insertstring length buffer size --
$!addr1 u $addr --
$@$addr -- addr2 u
$@len$addr -- u
$!lenu $addr --
$+!lenu $addr -- addr
$deladdr off u --
$insaddr1 u $addr off --
$+!addr1 u $addr --
c$+!char $addr --
$free$addr --
$init$addr --
$splitaddr u char -- addr1 u1 addr2 u2
$iter.. $addr char xt -- ..
$overaddr u $addr off --
$execxt addr --
$tmpxt -- addr u
$.addr --
$slurpfid addr --
$slurp-fileaddr1 u1 addr2 --
$[]u $[]addr -- addr’
$[]!addr u n $[]addr --
$[]+!addr u n $[]addr --
$[]@n $[]addr -- addr u
$[]#addr -- len
$[]mapaddr xt --
$[]slurpfid addr --
$[]slurp-fileaddr u $addr --
$[].addr --
$[]freeaddr --
$save$addr --
$[]saveaddr --
$boot$addr --
$[]bootaddr --
$savedaddr --
$[]savedaddr --
$Variable--
$[]Variable--

Xchars / Unicode

xc-sizexc -- u
x-sizexc-addr u1 -- u2
xc@+xc-addr1 -- xc-addr2 xc
xc!+?xc xc-addr1 u1 -- xc-addr2 u2 f
xchar+xc-addr1 -- xc-addr2
xchar-xc-addr1 -- xc-addr2
+x/stringxc-addr1 u1 -- xc-addr2 u2
x\string-xc-addr1 u1 -- xc-addr1 u2
-trailing-garbagexc-addr u1 -- addr u2
x-widthxc-addr u -- n
xkey-- xc
xemitxc --
xchar-encoding-- addr u

Structures

25

Gforth structures

%alignalign size --
%alignmentalign size -- align
%allocalign size -- addr
%allocatealign size -- addr ior
%allotalign size -- addr
cell%-- align size
char%-- align size
dfloat%-- align size
double%-- align size
end-structalign size "name" --
fieldalign1 offset1 align size "name" -- align2 offset2
float%-- align size
nalignedaddr1 n -- addr2
sfloat%-- align size
%sizealign size -- size
struct-- align size

Forth-200x structures

begin-structure"name" -- struct-sys 0
end-structurestruct-sys +n --
+fieldunknown
cfield:u1 "name" -- u2
field:u1 "name" -- u2
2field:u1 "name" -- u2
ffield:u1 "name" -- u2
sffield:u1 "name" -- u2
dffield:u1 "name" -- u2

Word Lists & Search Order

30

Word lists

forth-wordlist-- wid
definitions--
get-current-- wid
set-currentwid --
get-order-- widn .. wid1 n
set-orderwidn .. wid1 n --
wordlist-- wid
table-- wid
cs-wordlist-- wid
cs-vocabulary"name" --
>orderwid --
previous--
also--
Forth--
Only--
order--
findc-addr -- xt +-1 | c-addr 0
search-wordlistc-addr count wid -- 0 | xt +-1
words--
vlist--
Root--
Vocabulary"name" --
seal--
vocs--
current-- addr
context-- addr

Environment queries

environment?c-addr u -- false / ... true
environment-wordlist-- wid
gforth-- c-addr u
os-class-- c-addr u

Files & Blocks

78

General files

r/o-- fam
r/w-- fam
w/o-- fam
binfam1 -- fam2
open-filec-addr u wfam -- wfileid wior
create-filec-addr u wfam -- wfileid wior
close-filewfileid -- wior
delete-filec-addr u -- wior
rename-filec-addr1 u1 c-addr2 u2 -- wior
read-filec-addr u1 wfileid -- u2 wior
read-linec_addr u1 wfileid -- u2 flag wior
key-filefd -- key
key?-filewfileid -- f
write-filec-addr u1 wfileid -- wior
write-linec-addr u wfileid -- ior
emit-filec wfileid -- wior
flush-filewfileid -- wior
file-statusc-addr u -- wfam wior
file-positionwfileid -- ud wior
reposition-fileud wfileid -- wior
file-sizewfileid -- ud wior
resize-fileud wfileid -- wior
slurp-filec-addr1 u1 -- c-addr2 u2
slurp-fidfid -- addr u
stdin-- wfileid
stdout-- wfileid
stderr-- wfileid

Source files

include-filei*x wfileid -- j*x
includedi*x c-addr u -- j*x
included?c-addr u -- f
include... "file" -- ...
requiredi*x addr u -- i*x
require... "file" -- ...
needs... "name" -- ...
sourcefilename-- c-addr u
sourceline#-- u

Directories

open-dirc-addr u -- wdirid wior
read-dirc-addr u1 wdirid -- u2 flag wior
close-dirwdirid -- wior
filename-matchc-addr1 u1 c-addr2 u2 -- flag
get-dirc-addr1 u1 -- c-addr2 u2
set-dirc-addr u -- wior
=mkdirc-addr u wmode -- wior
mkdir-parentsc-addr u mode -- ior

Search paths

open-path-fileaddr1 u1 path-addr -- wfileid addr2 u2 0 | ior
clear-pathpath-addr --
also-pathc-addr len path-addr --
.pathpath-addr --
path+path-addr "dir" --
path=path-addr "dir1|dir2|dir3"
fpath-- path-addr

Redirection & pipes

outfile-execute... xt file-id -- ...
infile-execute... xt file-id -- ...
open-pipec-addr u wfam -- wfileid wior
close-pipewfileid -- wretval wior
broken-pipe-error-- n

Blocks

open-blocksc-addr u --
use"file" --
block-offset-- addr
get-block-fid-- wfileid
block-positionu --
listu --
scr-- a-addr
blocku -- a-addr
bufferu -- a-addr
empty-buffers--
empty-bufferbuffer --
update--
updated?n -- f
save-buffers--
save-bufferbuffer --
flush--
loadi*x u -- j*x
thrui*x n1 n2 -- j*x
+loadi*x n -- j*x
+thrui*x n1 n2 -- j*x
-->--
block-includeda-addr u --

Keyboard Input

33

Single-key input

key-- char
key?-- flag
ekey-- u
ekey>charu -- u false | c true
ekey>fkeyu1 -- u2 f
ekey?-- flag
k-left-- u
k-right-- u
k-up-- u
k-down-- u
k-home-- u
k-end-- u
k-prior-- u
k-next-- u
k-insert-- u
k-delete-- u
k-f1-- u
k-f2-- u
k-f3-- u
k-f4-- u
k-f5-- u
k-f6-- u
k-f7-- u
k-f8-- u
k-f9-- u
k-f10-- u
k-f11-- u
k-f12-- u
k-shift-mask-- u
k-ctrl-mask-- u
k-alt-mask-- u
fkey.u --
simple-fkey-stringu1 -- c-addr u

Debugging Tools

44

Examining

.s--
f.s--
maxdepth-.s-- addr
depth-- +n
fdepth-- +n
clearstack... --
clearstacks... --
?a-addr --
dumpaddr u --
see"<spaces>name" --
xt-seext --
simple-see"name" --
simple-see-rangeaddr1 addr2 --
see-code"name" --
see-code-rangeaddr1 addr2 --

Debugging

~~--
printdebugdata--
.debuglinenfile nline --
debug-fid-- file-id
once--
~~bt--
~~1bt--
???--
WTF??--
!!FIXME!!--
replace-wordxt1 xt2 --
~~Variable"name" --
~~Valuen "name" --
+ltrace--
-ltraceunknown
locate"name" --
edit"name" --
#locnline nchar "file" --

Assertions

assert0(--
assert1(--
assert2(--
assert3(--
assert(--
)--
assert-level-- a-addr

Single-step

dbg"name" --
break:--
break"’ccc"’ --

Forgetting

marker"<spaces> name" --

Objects (objects.fs)

44

objects.fs glossary

bind... "class" "selector" -- ...
<bind>class selector-xt -- xt
bind'"class" "selector" -- xt
[bind]compile-time: "class" "selector" -- ; run-time: ... object -- ...
classparent-class -- align offset
class->mapclass -- map
class-inst-sizeclass -- addr
class-override!xt sel-xt class-map --
class-previousclass --
class>orderclass --
construct... object --
current'"selector" -- xt
[current]compile-time: "selector" -- ; run-time: ... object -- ...
current-interface-- addr
dict-new... class -- object
end-classalign offset "name" --
end-class-nonamealign offset -- class
end-interface"name" --
end-interface-noname-- interface
end-methods--
exitm--
heap-new... class -- object
implementationinterface --
init-object... class object --
inst-valuealign1 offset1 "name" -- align2 offset2
inst-varalign1 offset1 align size "name" -- align2 offset2
interface--
m:-- xt colon-sys; run-time: object --
:m"name" -- xt; run-time: object --
;mcolon-sys --; run-time: --
methodxt "name" --
methodsclass --
object-- class
overridesxt "selector" --
[parent]compile-time: "selector" -- ; run-time: ... object -- ...
printobject --
protected--
public--
selector"name" --
this-- object
<to-inst>w xt --
[to-inst]compile-time: "name" -- ; run-time: w --
to-thisobject --
xt-new... class xt -- object

Objects (oof / mini-oof)

39

oof.fs base class

class"name" --
definitions--
class?o -- flag
init... --
dispose--
new-- o
new[]n -- o
:"name" --
ptr"name" --
asptro "name" --
[]n "name" --
::"name" --
super"name" --
self-- o
bindo "name" --
boundclass addr "name" --
link"name" -- class addr
isxt "name" --
'"name" -- xt
postpone"name" --
witho --
endwith--

oof.fs class declaration

varsize --
ptr--
asptrclass --
defer--
early--
method--
static--
how:--
class;--

mini-oof.fs

object-- a-addr
methodm v "name" -- m’ v
varm v size "name" -- m v’
classclass -- class selectors vars
end-classclass selectors vars "name" --
definesxt class "name" --
newclass -- o
::class "name" --

Multitasking

38

Tasks, events, locks

newtaskstacksize -- task
taskstacksize "name" --
execute-taskxt -- task
stacksize-- n
newtask4dsize rsize fsize lsize -- task
stacksize4-- dsize fsize rsize lsize
activatetask --
passx1 .. xn n task --
initiatext task --
pause--
restarttask --
halttask --
stop--
stop-nstimeout --
UValue"name" --
UDefer"name" --
user'’user’ -- n
semaphore"name" --
locksemaphore --
unlocksemaphore --
critical-sectionxt semaphore --
!@u1 a-addr -- u2
+!@u1 a-addr -- u2
?!@unew uold a-addr -- uprev
barrier--
<event--
event>task --
event:"name" --
?events--
event-loop--
elit,x --
e$,addr u --
eflit,x --
cond"name" --
pthread_cond_signalunknown
pthread_cond_broadcastunknown
pthread_cond_waitunknown
pthread_cond_timedwaitunknown

C Interface

15

Declaring C functions

\c"rest-of-line" --
c-function"forth-name" "c-name" "{type}" "—" "type" --
c-value"forth-name" "c-name" "—" "type" --
c-variable"forth-name" "c-name" --

Libraries

c-library-namec-addr u --
c-library"name" --
end-c-library--
clear-libs--
add-libc-addr u --

Pointers & callbacks

c-funptr"forth-name" <{>"c-typecast"<}> "{type}" "—" "type" --
c-callback"forth-name" "{type}" "—" "type" --

Low level

open-libc-addr1 u1 -- u2
lib-symc-addr1 u1 u2 -- u3
lib-error-- c-addr u
call-c... w -- ...

System & OS

19

Command line

next-arg-- addr u
argu -- addr count
shift-args--
argc-- addr
argv-- addr

Shell

sh"..." --
systemc-addr u --
$?-- n
getenvc-addr1 u1 -- c-addr2 u2

Time

msn --
time&date-- nsec nmin nhour nday nmonth nyear
utime-- dtime
cputime-- duser dsystem

Images & startup

savesystem"name" --
#!--
'cold--
bootmessage--

Miscellaneous

quit?? -- ??
bye--

Assembler

8

Code definitions

assembler--
init-asm--
abi-code"name" -- colon-sys
end-codecolon-sys --
code"name" -- colon-sys
;codecompilation. colon-sys1 -- colon-sys2
flush-icachec-addr u --

Disassembler

discodeaddr u --