Let a script run a script, four deep

A build script calling a setup script is the first thing anybody tries.

What is saved when one script starts another is A POSITION AND NOT A
BUFFER: the name, which block comes next, how many are left, and where in
the block it had got to. Seventy bytes, and they sit next to each other in
the data segment on purpose so that saving them is one copy. The block
itself is read again on the way back, which costs one disk read per return
and saves 257 bytes a level - the inner script reads its own block into the
single buffer there is, so coming back means fetching the outer one's block
again and landing on the byte it left.

The slot is reached by stepping rather than by multiplying, because this
machine has no multiply and the depth is never more than three steps.

Four levels. Deep enough for a script calling a script that calls a helper,
shallow enough that a script running itself says so rather than filling
memory. A line that fails now stops every level and not just the innermost,
because a build whose helper failed should not carry on in its caller.

The caller's place is saved BEFORE the new file is looked at, and put back
on every way out that is not success. Opening writes the name into the live
state in order to ask the disk about it, so by the time "there is no such
file" is known, the caller's place has already been overwritten - a failed
'do' inside a script would otherwise leave the script that ran it reading
from a name it never chose.

The test resumes in the outer script's SECOND block, which is the case the
whole design turns on and the one an ordinary nesting test would miss.
Breaking the re-read, the save, or the limit each fails it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-30 16:22:09 -04:00
co-authored by Claude Opus 5
parent a12d61fb80
commit c28826df77
10 changed files with 234 additions and 14 deletions
+16 -4
View File
@@ -140,7 +140,7 @@ prompt:
; comes back to. A build whose first step failed and whose second step ran anyway produces
; something wrong and says it succeeded, which is the failure this whole flag exists to
; prevent.
SETD.1 ScriptRunning
SETD.1 ScriptDepth
LDA.1
BRA promptWhere
SETD.1 LineFailed
@@ -149,7 +149,9 @@ prompt:
SETD.0 ScriptStopped
CALL printString
CALL newLine
CALL scriptClose
; Every level, not just this one. A build whose helper script failed should not carry on
; in the script that called the helper either.
CALL scriptAbandon
promptWhere:
; Where you are, but only when that is not obvious. At the root the prompt is the one it
@@ -320,11 +322,12 @@ promptSay:
; is nobody there and the shell should stop; a script ending means go back to whoever asked
; for it. So the end of a script falls through to the console rather than to the door.
shellReadLine:
SETD.1 ScriptRunning
SETD.1 ScriptDepth
LDA.1
BRA shellReadTyped
CALL scriptLine
BNQ shellReadTyped
BNQ shellReadLine ; That one ended. Ask again: something under it may still be
; running, and only depth reaching nought means the console.
; Echoed, so that a script working can be watched and a script failing says where. It is
; printed after the prompt, so it reads exactly like somebody typing it.
@@ -1624,6 +1627,13 @@ doScript:
CCF
SUB
BRQ scriptNoFile
INIB 0x02
CCF
SUB
BRQ scriptNotOne
SETD.0 ScriptTooDeep
BRI fileComplain
scriptNotOne:
SETD.0 ScriptNotOne
BRI fileComplain
scriptNoFile:
@@ -3628,6 +3638,8 @@ ScriptNoFile:
"do: cannot find it"
ScriptNotOne:
"do: that is not a script - it wants #! on the first line"
ScriptTooDeep:
"do: scripts are only four deep"
ScriptStopped:
"stopped: that line did not work"
Unknown: