Skip to content

More Forth words: +LOOP/J/LEAVE, DOES>, RECURSE, PICK/ROLL/?DUP/MIN/MAX/<=/>=/<> #3

Description

@softwarewrighter

Problem

Follow-up to #2 (DO/LOOP family and friends). That issue added DO/LOOP/?DO/I/UNLOOP and CONSTANT/VARIABLE and BEGIN-style WHILE/REPEAT/AGAIN. The remaining words from #2's body are still missing — most are small one-liners once the loop and defining-word machinery is in place, but a few (DOES>, LEAVE) need additional kernel support.

Scope: both kernels (forth-in-forth and forth-on-forthish), unless otherwise noted. Preserve byte-identical output across the two where possible.

Words to add

1. DO-loop extensions (build on #2)

  • +LOOP — like LOOP but increments the index by TOS instead of 1. Handles negative increments (down-counting) and the "cross the boundary" exit condition (standard Forth semantics: exit when the boundary is crossed in the direction of increment).
    • Needs a new (+LOOP) primitive in kernel.s. IMMEDIATE +LOOP parallels LOOP's compile-time behavior.
  • J — push the outer loop's index (for nested DO/LOOP). With our RS layout [index limit caller-IP], J reads 9(r1) (index of the outer loop, below caller's IP). One new primitive.
  • LEAVE — exit the current loop early. Requires compile-time bookkeeping: ?DO-style forward branches chained through LOOP so each LEAVE can be patched to the loop exit. Meaningful refactor of the IMMEDIATE DO/LOOP compiler.

2. DOES>

  • DOES> — splice runtime behavior onto the most recent CREATE. Requires the inner interpreter to support re-entering a colon-def at a patched address. Classic approach: CREATE builds a word whose CFA is a trampoline that pushes the data-field address and jumps to DOCOL at a patched location.
  • Would let CONSTANT/VARIABLE be expressed more elegantly: : CONSTANT CREATE , DOES> @ ; instead of the current ,DOCOL + ['] LIT templating.
  • Non-trivial. Likely wants a small kernel primitive ((DOES>)) plus rework of CREATE's CFA format.

3. RECURSE

  • RECURSE — IMMEDIATE; compiles a call to the currently-compiling definition. Needed because the new entry is SMUDGE/HIDDEN during its own compilation, so FIND won't see it. With LATEST pointing at the in-progress entry, implementation is a few lines: : RECURSE LATEST @ >CFA , ; IMMEDIATE (requires >CFA or equivalent).

4. Stack / comparison ergonomics (pure Forth, no new primitives)

  • ?DUPDUP if non-zero. : ?DUP DUP IF DUP THEN ;
  • PICKn PICK copies the n-th stack element to top. One-liner using SP@ (we have it): : PICK 1 + 3 * SP@ + @ ; — cell-size aware.
  • ROLLn ROLL rotates the n-th element to top. Trickier; needs a helper loop.
  • MIN / MAX: MIN 2DUP < IF DROP ELSE NIP THEN ; etc.
  • <= / >= / <>: <= > 0= ;, : >= < 0= ;, : <> = 0= ;
  • 0<> — non-zero test. : 0<> 0= 0= ;

Expected location: core/lowlevel.fth for simple ones; core/midlevel.fth or a new core/ergo.fth tier for PICK/ROLL if they grow.

Non-goals

Motivation

With the words from #2 in place, the core language feels almost complete, but a handful of idioms — especially LEAVE, ?DUP, MIN/MAX, <=/>=/<> — still require awkward workarounds in demos. DOES> is the big one: it unlocks a whole family of defining words (arrays, enumerations, records) that are currently inexpressible.

Split from #2 so that issue can stay closed; pick any subset of these off at a time.

Testing

Each new word gets a demo in examples/ and stack-leak coverage (DEPTH before/after). Match byte-identical behavior between forth-in-forth and forth-on-forthish kernels where applicable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions