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:
co-authored by
Claude Opus 5
parent
a12d61fb80
commit
c28826df77
@@ -13,7 +13,10 @@ bad.script 45
|
||||
plain.script 24
|
||||
cross.script 280
|
||||
nonl.script 38
|
||||
13 files
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
16 files
|
||||
> load what?
|
||||
> no such file
|
||||
> not a program
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
CosmOS
|
||||
> > echo outer in block one
|
||||
outer in block one
|
||||
> do inner.script
|
||||
> echo inner one
|
||||
inner one
|
||||
> echo inner two
|
||||
inner two
|
||||
> echo outer resumed in block one
|
||||
outer resumed in block one
|
||||
> > echo self
|
||||
self
|
||||
> do loop.script
|
||||
> echo self
|
||||
self
|
||||
> do loop.script
|
||||
> echo self
|
||||
self
|
||||
> do loop.script
|
||||
> echo self
|
||||
self
|
||||
> do loop.script
|
||||
do: scripts are only four deep
|
||||
stopped: that line did not work
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -12,7 +12,10 @@ bad.script 45
|
||||
plain.script 24
|
||||
cross.script 280
|
||||
nonl.script 38
|
||||
13 files
|
||||
outer.script 376
|
||||
inner.script 44
|
||||
loop.script 35
|
||||
16 files
|
||||
> loaded, starting at 4000
|
||||
> it says: the disk took its time
|
||||
finished
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
do outer.script
|
||||
do loop.script
|
||||
exit
|
||||
@@ -135,6 +135,26 @@ open('cross.script','w').write(head + pad + 'Say across the block boundary\n')
|
||||
# The last line has no newline after it and still has to run.
|
||||
printf '#! script\nSay with no newline after me' > nonl.script
|
||||
"$TOOL" put "$DISKS/cosmos.img" nonl.script >/dev/null
|
||||
# ---- One script inside another ----
|
||||
#
|
||||
# outer.script resumes in its SECOND block, which is the case the whole design turns on: the
|
||||
# inner script reads its own block into the one buffer there is, so coming back means reading
|
||||
# the outer one's block again and landing on the byte it left off at. Generated, because
|
||||
# where the do line falls is the entire point and no editor should be able to move it.
|
||||
python3 -c "
|
||||
head = '#! script\n'
|
||||
pad = ''
|
||||
while len(head) + len(pad) < 300:
|
||||
pad += '; pad\n'
|
||||
open('outer.script','w').write(head + pad +
|
||||
'echo outer in block one\ndo inner.script\necho outer resumed in block one\n')
|
||||
"
|
||||
"$TOOL" put "$DISKS/cosmos.img" outer.script >/dev/null
|
||||
printf '#! script\necho inner one\necho inner two\n' > inner.script
|
||||
"$TOOL" put "$DISKS/cosmos.img" inner.script >/dev/null
|
||||
# A script that runs itself, which is what the depth limit is for.
|
||||
printf '#! script\necho self\ndo loop.script\n' > loop.script
|
||||
"$TOOL" put "$DISKS/cosmos.img" loop.script >/dev/null
|
||||
|
||||
# A disk of its own for the writing test, with one file already on it so that what it
|
||||
# writes has to be placed somewhere that does not tread on what is there.
|
||||
|
||||
@@ -775,6 +775,12 @@ cosmosScriptBad | CosmOS/Source/cosmos.asm | run | cosmosScr
|
||||
# the boundary between two blocks, and a last line with no newline after it. Both of these
|
||||
# were faults before they were checks.
|
||||
cosmosScriptEdges | CosmOS/Source/cosmos.asm | run | cosmosScriptEdges.in | 200000000 | disks/cosmos.img
|
||||
# One script inside another, and the limit on how far that goes. The outer script resumes in
|
||||
# its second block, which is the case the design turns on - the inner one reads its own block
|
||||
# into the single buffer, so coming back means reading the outer one's again and landing on
|
||||
# the byte it left. Then a script that runs itself, which stops at four deep and takes every
|
||||
# level with it, because a build whose helper failed should not carry on in its caller.
|
||||
cosmosScriptNest | CosmOS/Source/cosmos.asm | run | cosmosScriptNest.in | 200000000 | disks/cosmos.img
|
||||
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
|
||||
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
|
||||
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
|
||||
|
||||
Reference in New Issue
Block a user