Gforth 1.0 upgrade guide: new help, string literals, Cilk threading and SwiftForth-class performance
Most Forth notes out there simply name-drop features and move on, which leaves you guessing what to type next. This guide reverses that: every capability introduced along the route from 0.7.x to 1.0 is tied to a concrete task you can run in the interpreter today, plus the compatibility caveats that matter before you push the new binary to a shared machine.
How much Gforth 1.0 changes day-to-day Forth work
For anyone whose workflow is a REPL, a source buffer and an editor jump list, the release lands in three places: the text interpreter now recognizes more literal forms, a full IDE word set ships inside the system itself, and the documented performance profile moves to a SwiftForth-class level without any user action. Parallel work also becomes first-class, because Cilk and task primitives are part of the core rather than an add-on. What does not change is the need to check word stability before you depend on it - and the need to know which words stopped being documented.
Before you upgrade a shared build, here is the short version:
- Search your codebase for the deprecated names listed under "Retired names and replacements"; they still resolve today but are flagged for removal.
- Re-run the suite that exercises memory access, since 16-bit, 32-bit and 64-bit unit words (w@, l@, x@, xd@ and their stores) are now documented as the portable way to reach bytes, halfwords, cells and double cells.
- Decide where parallelism is worth it: Cilk is now available for splitting work across tasks, and IP-update optimization is what raises throughput on loop-dominated code.
Stability labels: a support contract you can query
The documentation now separates words that are intended to be permanently supported from those that are not, as described in Stability Goals. That distinction is what lets you tell auditors or a new teammate which parts of a codebase are safe to build on.
Tripwire: confirm a word's status before shipping it
- Start the interpreter with the Web documentation available, so word references resolve.
- Type help followed by the word name to read its documentation entry, or select the word in the Web version of this document.
- If the word already existed in 0.7.x but had no entry, treat it as newly documented rather than newly added: several words in this release fall in that group.
- Open the word index and check whether the entry is marked permanent or non-permanent, and record that in your own compatibility notes.
- Repeat for any word you plan to leave in production code, because undocumented status is the signal that removal may follow.
Headline text-interpreter changes
What the interpreter recognizes has grown, and each addition removes a class of hand-written helpers:
- Scaled floating-point notation, such as 6k5 and 23%, so you no longer need a conversion step for common literals.
- String literals written directly, as in "string".
- Execution tokens as `word, name tokens as ``word, and body addresses as <word> and <word+8>. See Literals in source code.
- Vocabulary qualification on a word, including several vocabularies at once, as in forth:and versus assembler:and.
- New nonparsing syntax for value-flavoured and defer-flavoured words: ->v means to v, +>v means +to v, 'v means addr v, @>d means action-of d, and =>d means is d. See rec-to.
- Environment variable contents written as ${LANG}.
- More recognizers that can extend the text interpreter: recs rec-name rec-local rec-scope rec-number .-is-dcell? rec-float rec-complex rec-string rec-to rec-dtick rec-tick rec-body rec-env rec-meta rec-forth rec-sequence: get-recs set-recs rec-none translate-name translate-cell translate-dcell translate-float translate-complex translate-string scan-translate-string translate-env translate-to rec-filter rec-forth-nt? translate: interpreting compiling postponing ?rec-found
Editing and debugging: the IDE words inside the system
Instead of an external indexer, Gforth 1.0 carries source-aware navigation:
- Show a word's source code with locate and browse.
- Show a word's uses with where, nw, bw, and ww.
- Get backtraces with source locations and browse them with nt, bt, and tt; hand them to an editor through edit, g, gg, and whereg.
- Other IDE words are fancy-after-l xt-locate l n b before-locate after-locate ll short-where expand-where prepend-where unused-words.
- A smarter .s infers what stack items mean and displays them appropriately, while .s itself remains dumb.
- A status line at the bottom of the terminal can show BASE, stacks, and search order; toggle it with -status and +status.
Concurrency, Cilk, message queues and the C interface
Three separate mechanisms now cover "spread this work out", and picking the right one is the main design decision:
- Multi-tasking on multiple cores or hardware threads: newtask task newtask4 stacksize stacksize4 initiate execute-task kill-task kill stop stop-ns stop-dns thread-deadline halt sleep wake restart pause auser uallot up@ user' 's mutex lock unlock critical-section atomic!@ atomic+!@ atomic?!@ barrier
- Message queues, similar to the actor model, provide a way to combine communication with synchronization of tasks: send-event ?events event-loop
- Cilk is useful for distributing work across tasks: cores cilk-init spawn spawn1 spawn2 cilk-sync cilk-bye
- C interface enhancements, including callbacks, are available: c-value c-variable c-funptr c++-library-name c++-library add-libpath add-framework add-incdir add-cflags add-ldflags c-callback c-callback-thread callback#
Dictionary layout, closures and reshaping existing words
For library authors and tooling work:
- The dictionary can now have more than one section, each with its own dictionary pointer, which allows allocation without affecting other sections and is used, for example, for nested definitions. See Sections. Words: next-section previous-section extra-section .sections
- Multiple words can be postponed with ]] ... [[ .
- Value-flavoured and defer-flavoured fields: value: cvalue: wvalue: lvalue: scvalue: swvalue: slvalue: 2value: fvalue: sfvalue: dfvalue: zvalue: $value: value[]: $value[]: defer:
- Per-task values and defers: uvalue udefer
- Closures provide execution tokens for code with data: free-closure [n:l [d:l [f:l [n:d [d:d [f:d [n:h [d:h [f:h [n:h1 [d:h1 [f:h1 [{: :}l :}d :}h :}h1 :}xt >addr <{: ;>
And the header layout change that lets you rewrite words in place:
- Word behaviour can be modified with set-does> set-compsem set->int set->comp set-name>string set-name>link ;
- A code address can be used instead of an xt with set-execute ; new code addresses for this are dovalue: dodoes: doabicode:
- User-defined to etc. are supported: to-table: n/a to-class: >uvalue set-to
- The implementation of words can be modified with set-optimizer
- For fans of non-postfix syntax: compsem: intsem: opt:
- Words can be created from a template: create-from reveal reveal! noname-from
- set-does> and related operations can be applied to a word defined earlier: make-latest
- Introspection is available: .hm immediate?
Numbers, memory widths and byte order
The expanded core vocabulary in one place, since these are the words you reach for when porting code across targets:
- The return stack can be used interpretively, except for passing data into or out of INCLUDED, EVALUATE etc.
- Information words: help shows the documentation of words or documentation nodes, plus authors, license, and .
- Boolean flags: select
- Single-precision arithmetic, umin umax; explicitly floored, symmetric, or unsigned division words, /s /f u/ mods modf umod /mods /modf u/mod du/mod */s */f u*/ */mods */modf u*/mod ud/mod; and two-stage division words, staged/-size /f-stage1m /f-stage2m modf-stage2m /modf-stage2m u/-stage1m u/-stage2m umod-stage2m u/mod-stage2m staged/-divisor
- Bitwise operations: mux arshift dlshift drshift darshift >pow2 log2 pow2? ctz wrol wror lrol lror rol ror drol dror
- Pseudo-random numbers: rnd random seed!
- Floating-point words: f*+ s>f f>s fcopysign ftrunc v* faxpy infinity inf -infinity -inf nan
- Stack manipulation: data stack, third fourth; FP stack, fthird ffourth f-rot; return stack, r'@ rpick n>r nr> f>r fr> fr@
- Dictionary allocation: ->here w, l, x, xd, a, mem, save-mem-dict
- Heap allocation: save-mem extend-mem free-mem-var .
- Growable buffers: buffer% init-buffer adjust-buffer .
- Generic memory access: *aligned *align; cells: !@ +!@ cell- cell/ th th@ th!
- Memory can be accessed in 16-bit (w), 32-bit (l), and 64-bit (x, xd) units: w@ w! l@ l! x@ x! xd@ xd!
- Words for dealing with different byte orders are wbe wle lbe lle xbe xle xdbe.
0.7.x versus 1.0: what to expect during migration
| Area | 0.7.x | 1.0 | Migration note |
| Stability documentation | No published permanent/non-permanent split | Stability Goals states which words are intended to be permanently supported and which are not | Re-label internal APIs before promising support downstream |
| Source literals | Basic forms | Scaled FP such as 6k5 and 23%, strings, xts, nts, body addresses | Existing code keeps parsing; new literals reduce helper words |
| Vocabulary and to syntax | Parsing forms only | Vocabulary prefixes plus nonparsing ->v, +>v, 'v, @>d, =>d | Pick one style; mixed syntax complicates review |
| Environment access | Programmatic lookup | ${LANG} written inline | Replace hand-rolled lookup helpers |
| IDE navigation | Manual grep | locate, browse, where, nw, bw, ww, nt, bt, tt, editor hooks | Adopt before writing custom tooling |
| Parallelism | Not a documented core area | Multi-tasking, message queues, Cilk, C callbacks | Prototype Cilk only after mutex and barrier code is clean |
| Performance | Pre-optimization baseline | IP-update speeds loop-dominated programs by a factor of 2; literal stack allows constant folding; overall performance comparable to SwiftForth | Re-benchmark hot loops before adding your own hand-tuning |
| Memory widths | Fewer documented units | 16-bit (w), 32-bit (l), 64-bit (x, xd) access plus byte-order words | Porting to a different target: switch to the documented unit words |
Retired names and replacements
Many of the following words are still present in 1.0, but they may be removed in the future, so treat them as removal candidates rather than unsupported:
- .name (use id. ).
- does-handler! (implementation changed, use set-does> ).
- /does-handler (implementation changed, see Carnal words).
- @local# f@local# laddr# lp+!# (replaced by @localn f@localn lp+n lp+! ).
- clear-libs (interface changed, use c-library ... end-c-library ).
- <interpretation interpretation> <compilation compilation> create-interpret/compile (replaced by set->int set-compsem ).
- naligned (use *aligned ).
- name>comp name>int (use name>compile name>interpret ).
- path-allot (use dup , 0 , allot ).
- sl@ sw@ ul@ uw@ (use l@ l>s ; w@ w>s ; l@ ; w@ ).
Internal and therefore no longer documented: ;s block-position cfalign cfaligned compile-lp+! empty-buffer name?int s>number? s>unumber? save-buffer
Three workflows worth testing first
Read a word's contract in place
- Enter help with the word name, and read the documentation node it returns.
- For wider context, open the word index or select the word in the Web version of this document.
- Confirm whether the entry is a permanent or non-permanent word under Stability Goals.
Trace a failing call back to source
- Reproduce the fault and capture the backtrace, which now shows source locations.
- Move through the frames with nt, bt, and tt.
- When you want the file open at the offending line, hand the frame to an editor with edit, g, gg, or whereg.
- Inspect the surrounding word with locate and browse, then list its other uses with where, nw, bw, ww.
Split a hot loop across cores
- Query cores to see how many cores or hardware threads are available.
- Initialize the runtime with cilk-init.
- Break out independent sub-work with spawn, spawn1, and spawn2.
- Wait for completion with cilk-sync, then close the runtime with cilk-bye.
- Share state only through mutex, lock, unlock, critical-section, barrier, and the atomic!@ atomic+!@ atomic?!@ words.
Frequently asked questions
Does the loop speed-up apply to my program? The documented effect is on loop-dominated programs, which gain a factor of 2 from IP-update optimization; code that spends its time elsewhere will not see the same ratio.
Are the deprecated words gone? No. Many are still present in 1.0, but they may be removed in the future, which is why the replacement list matters now.
Do I need a separate library for parallelism? No: multi-tasking, message queues and Cilk are part of the documented word set, and the C interface additions, including callbacks, cover foreign code.
What is the performance target? The release states overall performance comparable to SwiftForth, with the literal stack enabling constant folding and other optimizations.
Common misconceptions
- Reading a newly documented word as a newly added one - several words existed earlier but were undocumented.
- Using the return stack interpretively to pass data into or out of INCLUDED, EVALUATE etc.; that case is explicitly excluded.
- Assuming .s now explains stack contents in general - the smarter display is a separate word, while .s itself remains dumb.
- Treating undocumented words as already removed; they are present but flagged for possible future removal.
- Keeping sl@, sw@, ul@, uw@ or naligned instead of the documented replacements.
Practical next steps
Upgrade a scratch build first and run the word-through-stability checklist above; sweep for the deprecated names; re-benchmark the loops that dominate your timings, since the factor of 2 improvement is concentrated there; and only then experiment with Cilk for work that genuinely parallelizes. The literal additions such as 6k5, 23% and "string" are the lowest-risk wins, so convert those call sites first and leave the dictionary-section and closure APIs for library-level code.
If you maintain Forth tooling for a team, this is the release where the interpreter stops needing external tooling for navigation, and where documented stability gives you a defensible base for long-lived code.