Skip to content

Recursion in a map of our own - #37

Merged
jeany55 merged 2 commits into
trigscript-4from
recursion
Oct 20, 2026
Merged

jeany55 merged 2 commits into
trigscript-4from
recursion

Conversation

@jeany55

@jeany55 jeany55 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Functions that call themselves, in a map of our own (no eudplib, no Python). The meaning is the Python side's: a
call that may come back into the function it is in keeps that function's variables and where it goes back to around
the call; past the depth the script's settings allow, the program says so in red and stops for good.

How (compiler/backend/emit.ts, layout.ts, graph.ts; plan section "Recursion on our own backend"):

  • A stack of cell triggers, the routed copies' (72 bytes apart, overlapped). Keeping a frame is one trigger, a Set
    Deaths a variable, its destination a place of the stack's sp; a cell comes back through one trigger and a jump.
    eudplib's build reads a cell back a bit at a time (about 35 triggers).
  • A stack for each function that calls itself: one for all of them gave sp a place at every call of every such
    function, and each call wrote them all twice. The depth that runs out is still one count for all.
  • Only what is read again is kept: liveAfterCalls works out, backwards over the function's statements, what is
    read after each call before it is written. saves lists every variable of the function, on purpose; fib keeps two
    cells a call instead of three.
  • Where a function goes back to is written by each call into all its places (a label), not copied before each
    store. A frame keeps the calling function's, as the Python side's does (two functions calling each other differ).

probes/own-recursion.ts, the frame of fib(18) (8361 calls) and down(1000), lifted game, Remastered's weights:

ms
One stack 32.05
A stack for each function 17.78
Only what is read again 13.06
Where it goes back to written by each call 11.64
Through eudplib 26.06

Going past the depth: 0.25 ms against eudplib's 0.51.

Checked: random programs with functions that call themselves (a depth that goes down, one or two calls of itself
with the front end's saves, inside loops and ifs) as written, as graphs and from the laid-out payload's bytes:
1500 in the suite, 14,000 once — the test found two mistakes of the liveness on the way, both fixed. The probe's lines
the same at the same frames built both ways, every one ok, the overflow in red with nothing after it; game-check: 3605
accesses, none ending the game; the whole suite; the calls and own-loader probes still all ok. Played in
Remastered:
trigscript-own-recursion.scx printed all nine lines as the lifted game does, the overflow line red and
nothing after it, no stutter seen on the frame of about 9,400 calls.

Not yet: a growing array declared in a function that calls itself, units and texts on the stack (refused, and said).

🤖 Generated with Claude Code

https://claude.ai/code/session_01T1CM9pLuTuAn3mgTEkjgVg

jeany55 and others added 2 commits October 19, 2026 18:51
…tion, keeping only what is read again

A call that may come back into the function it is in (the front end's `saves`) keeps that
function's variables and where it goes back to on a stack of its own, and takes them back after;
past the depth the script's settings allow, the program says so in red and stops for good, as the
Python side's build does.

- The stack's cells are the routed copies' cell triggers. Keeping a frame is one trigger, a Set
  Deaths a variable, its destination a place of the stack's `sp`; a cell comes back through one
  trigger (its next pointer a place of `sp`) into a scratch cell, and from there into every place
  of the variable. eudplib's build reads a cell back a bit at a time.
- Where a function that calls itself goes back to is a variable whose places are the next pointer
  of its last trigger and each store that keeps it; every call sets them all to its label. A frame
  keeps the calling function's (two functions calling each other differ).
- A stack for each function: one for them all gave `sp` a place at every call of every such
  function, and each call wrote them all twice. The depth that runs out is one count for all.
- Only what is read again: `liveAfterCalls` works out, backwards over the function's statements,
  what some way on from each call reads before writing; the rest of `saves` stays off the stack.

probes/own-recursion.ts, the frame of fib(18) and down(1000): one stack 32.05 ms, a stack for each
function 17.78, only what is read again 13.06, labels written by the calls 11.64; eudplib 26.06.

Checked: random programs with functions that call themselves (a depth that goes down, one or two
calls of itself with the front end's saves, in loops and ifs) as written, as graphs and from the
laid-out bytes; the probe's lines the same at the same frames built both ways, the overflow in red
with nothing after it; game-check: 3605 accesses, none ending the game; the whole suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1CM9pLuTuAn3mgTEkjgVg
…m programs' long look

The recursion test's evaluator now has a budget for a whole program (a hundred thousand
statements, its calls' bodies included): a program whose functions call themselves from inside
each other's loops multiplies into hundreds of millions of calls, which the graph interpreter took
minutes over. Such a program is set aside, and the test fails past a few in a hundred. With
EMITTER_SEEDS / EMITTER_FROM the test runs a longer look in pieces (and a longer time limit).
14,000 of them pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1CM9pLuTuAn3mgTEkjgVg
@jeany55
jeany55 merged commit e6dfa9c into trigscript-4 Oct 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant