You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the bare forth.s REPL, CREATE name N , does not yield a callable variable. After the CREATE, executing name pushes nothing, so the standard CREATE name 0 , pattern for declaring a single-cell variable does not work.
This came up while wiring up Tuplet's Forth emitter (sw-vibe-coding/tuplet). Our lowering doc (docs/lowering.md) leans on the conventional pattern below to allocate tuple/scalar storage:
Generating that produces the failure pasted below: every line up to the body returns ok, then the call site emits ? because the data-area words aren't callable.
Concrete repro
Tested on sw-embed/sw-cor24-forth HEAD d349a54 with cor24-run:
The kernel comment on entry_create (forth.s ~line 1218) is explicit:
; CREATE ( -- ) : Read name, build dictionary header at HERE
; Reads next word from UART input, builds link+flags+name at HERE.
; Updates LATEST. Does NOT write CFA — caller does that.
So CREATE is intentionally a header-only primitive used internally by : (which then writes its own CFA). The public REPL CREATE therefore leaves the new word with no runtime — calling it dispatches to whatever bytes happen to land at the post-name address. There is no DOVAR-style runtime attached.
Issue #3 already tracks DOES> / CONSTANT / VARIABLE for the higher-level kernels (forth-in-forth, forth-on-forthish) and explicitly excludes forth.s. This issue is therefore filing the gap that was carved out: REPL users of forth.s have no way to declare a usable variable cell.
Why the natural prelude workaround doesn't reach
Defining VARIABLE or CONSTANT from the REPL would normally look like:
— but compiling a literal address into the body needs LIT to be reachable from compile mode (i.e. an immediate LITERAL or [']), and forth.s exposes neither. WORDS lists LIT only as the runtime primitive used by the inner interpreter; there is no compile-time way to splice a number into a colon body.
So the gap is closed: from forth.s alone you can't allocate a callable variable cell. (You can build the dictionary entry; you can't make calling it do anything useful.)
Asks (any one of these unblocks Tuplet)
In rough order of preference / smallest-change-first:
Expose LITERAL (immediate; compiles LIT n) or ['] (immediate; compiles LIT xt-of-next-word). Either lets VARIABLE / CONSTANT be written as ordinary colon definitions in user space without changing CREATE.
Add VARIABLE name and CONSTANT name primitives directly to forth.s. Same number of new entry points (one each) but no immediate-word complexity.
Make CREATE attach a default DOVAR-like CFA so name after CREATE name pushes the data-field address. This is the most ANS-faithful fix and unblocks CREATE name 0 , directly.
If forth.s is intentionally minimal and any of these is out of scope, a doc update saying so explicitly (and pointing prelude-builders at forth-in-forth / forth-on-forthish) would also unblock us by redirecting Tuplet's emitter target.
Tuplet-side workaround
Until this is resolved, the Tuplet Forth emitter (tuplet-forth-emit saga, step 002) can produce deterministic Forth text but cannot run it end-to-end. The runner script is wired up so that re-enabling the runnable demo is a one-line change once any of the above lands.
Problem
In the bare
forth.sREPL,CREATE name N ,does not yield a callable variable. After the CREATE, executingnamepushes nothing, so the standardCREATE name 0 ,pattern for declaring a single-cell variable does not work.This came up while wiring up Tuplet's Forth emitter (
sw-vibe-coding/tuplet). Our lowering doc (docs/lowering.md) leans on the conventional pattern below to allocate tuple/scalar storage:Generating that produces the failure pasted below: every line up to the body returns
ok, then the call site emits?because the data-area words aren't callable.Concrete repro
Tested on
sw-embed/sw-cor24-forthHEADd349a54withcor24-run:Observations:
CREATE foo 42 ,→ok. HERE advances by 10 bytes (3 link + 1 flags_len + 3 name + 3 comma'd cell), so the header and data cell are present.foo .S→<0>— the stack is empty.foopushed nothing.foo @ .→?(unknown-word path) followed by stray output and another?.A self-contained repro lives at
scripts/repro-cor24-forth-create-variable.shin the Tuplet repo:https://github.com/sw-vibe-coding/tuplet/blob/main/scripts/repro-cor24-forth-create-variable.sh
Root cause
The kernel comment on
entry_create(forth.s~line 1218) is explicit:So
CREATEis intentionally a header-only primitive used internally by:(which then writes its own CFA). The public REPLCREATEtherefore leaves the new word with no runtime — calling it dispatches to whatever bytes happen to land at the post-name address. There is no DOVAR-style runtime attached.Issue #3 already tracks DOES> / CONSTANT / VARIABLE for the higher-level kernels (
forth-in-forth,forth-on-forthish) and explicitly excludesforth.s. This issue is therefore filing the gap that was carved out: REPL users offorth.shave no way to declare a usable variable cell.Why the natural prelude workaround doesn't reach
Defining
VARIABLEorCONSTANTfrom the REPL would normally look like:or, dovar-free:
— but compiling a literal address into the body needs
LITto be reachable from compile mode (i.e. an immediateLITERALor[']), andforth.sexposes neither. WORDS listsLITonly as the runtime primitive used by the inner interpreter; there is no compile-time way to splice a number into a colon body.So the gap is closed: from
forth.salone you can't allocate a callable variable cell. (You can build the dictionary entry; you can't make calling it do anything useful.)Asks (any one of these unblocks Tuplet)
In rough order of preference / smallest-change-first:
LITERAL(immediate; compilesLIT n) or['](immediate; compilesLIT xt-of-next-word). Either letsVARIABLE/CONSTANTbe written as ordinary colon definitions in user space without changing CREATE.VARIABLE nameandCONSTANT nameprimitives directly toforth.s. Same number of new entry points (one each) but no immediate-word complexity.nameafterCREATE namepushes the data-field address. This is the most ANS-faithful fix and unblocksCREATE name 0 ,directly.DOES>so user code can splice runtime behavior. This is more invasive (issue More Forth words: +LOOP/J/LEAVE, DOES>, RECURSE, PICK/ROLL/?DUP/MIN/MAX/<=/>=/<> #3 lists it for the higher-level kernels) — listed last for completeness.If
forth.sis intentionally minimal and any of these is out of scope, a doc update saying so explicitly (and pointing prelude-builders atforth-in-forth/forth-on-forthish) would also unblock us by redirecting Tuplet's emitter target.Tuplet-side workaround
Until this is resolved, the Tuplet Forth emitter (
tuplet-forth-emitsaga, step 002) can produce deterministic Forth text but cannot run it end-to-end. The runner script is wired up so that re-enabling the runnable demo is a one-line change once any of the above lands.