Loops, and the scripting language is a language
while and for. Both only mean anything in a script, because a loop goes back to the line that opened it and a prompt has no line to go back to - and both say so rather than doing something surprising. THE SCRIPT READER KEEPS THE POSITION OF EVERY LINE before reading it, which is what makes any of this possible: by the time a line has been read the reader is past it, and a line is not a fixed size to subtract. Three words per line, and the block is read again on the way back so the pointer into it means what it meant - the same thing nesting one script inside another already did, for a different reason. THE TWO LOOPS END DIFFERENTLY, and that is the design rather than an accident. A while is taken away at its end and its own line asks the question again, so nothing has to be remembered. A for is not: how many words it has used is kept in the block, and its line reads itself again and counts one more off the front. That is a byte in a block instead of a copy of the word list in every one of them. Blocks grew from a byte to a record of sixteen - state, kind, words used, and where the line that opened it was - and sixteen because A and B are a shift register, so four rotations turn a block number into its offset. The history and the variables are addressed the same way for the same reason. Nested loops, an if inside a loop, a loop inside a branch nobody takes, and a for with no words: the last two run no times rather than once, which is the case worth having a test for. Three things found by running it: textSame asks whether two WHOLE strings are the same, so "in red green blue" is not "in". The word has to be split off before it is compared. A for typed at a prompt complained about while, because both arrive at the same place. One message that names neither is better than one that names the wrong one. And docs.sh caught a naming convention nobody had written down: it recognises a packed name by its label ending in "Name", so ForName2 was silently not counted. It failed the right way round - saying the run was shorter than the count claims rather than passing - but the convention now lives where the names are and not only in the checker. 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
16f8232a35
commit
2abc8281df
@@ -138,6 +138,32 @@ The shell already had the other half. `LineFailed` exists because a script stops
|
||||
line that did not work, so every command in the system was already saying whether it had
|
||||
worked, for a different reason.
|
||||
|
||||
**And two loops, which only mean anything in a script**, because a loop goes back to the line
|
||||
that opened it and a prompt has no line to go back to.
|
||||
|
||||
```text
|
||||
#! script
|
||||
for colour in red green blue
|
||||
echo it is $colour
|
||||
end
|
||||
|
||||
set n go
|
||||
while same $n go
|
||||
echo round once
|
||||
set n stop
|
||||
end
|
||||
```
|
||||
|
||||
The script reader keeps the position of **every** line before reading it, which is what makes
|
||||
that possible: by the time a line has been read the reader is past it, and a line is not a
|
||||
fixed size to subtract.
|
||||
|
||||
A `while` is taken away at its `end` and its line asks the question again. A `for` is not:
|
||||
how many words it has used is kept in the block, and its line reads itself again and counts
|
||||
one more off the front - a byte in a block rather than a copy of the list in every one of
|
||||
them. A `for` with no words runs no times, and a loop inside a branch nobody is taking runs
|
||||
no times either.
|
||||
|
||||
Blocks nest eight deep. A branch that is not being taken is **not even looked at**: the
|
||||
skipping happens before names are filled in, so `$whatever` inside a branch nobody is running
|
||||
is not a mistake, and a line nobody is running cannot fail.
|
||||
@@ -305,6 +331,8 @@ CosmOS currently provides these built-in commands:
|
||||
| `if <command>` | Run the lines after it only if that command worked. See Lines Run Sometimes. |
|
||||
| `else` | Run them only if it did not. |
|
||||
| `end` | Close the block. |
|
||||
| `while <command>` | Run the lines after it for as long as that command keeps working. |
|
||||
| `for <name> in <words>` | Run them once for each word, with the name set to it. |
|
||||
| `same <a> <b>` | Fails when the two are different, which is how `if` asks about a value. |
|
||||
| `monitor` | Enter monitor mode, in which the prompt becomes `*` and the commands below are also available. |
|
||||
| `help` | Show the built-in command summary. |
|
||||
|
||||
@@ -208,6 +208,15 @@ promptIfTaken:
|
||||
promptIfPush:
|
||||
CALL blockPush
|
||||
BNQ ifDeep
|
||||
|
||||
; A loop has to be able to come back to the line that asked, which if does not.
|
||||
SETD.1 LoopKind
|
||||
LDA.1
|
||||
BRA promptIfPlain
|
||||
CALL blockTop
|
||||
BNQ promptIfPlain
|
||||
CALL blockKeepWhere
|
||||
promptIfPlain:
|
||||
RSTA
|
||||
SETD.1 LineFailed
|
||||
STA.1 ; And the question is answered rather than unanswered.
|
||||
@@ -388,6 +397,16 @@ promptDispatch:
|
||||
CALL textSame
|
||||
BRQ doSame
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 WhileName
|
||||
CALL textSame
|
||||
BRQ doWhile
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 ForName
|
||||
CALL textSame
|
||||
BRQ doFor
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 HelpName
|
||||
CALL textSame
|
||||
@@ -2790,22 +2809,52 @@ varExpandNoSuch:
|
||||
; and the shell already had the other half - LineFailed, which every command sets and which
|
||||
; stop-on-failure was built on.
|
||||
;
|
||||
; A block is one byte on a stack eight deep:
|
||||
; A block is a record of sixteen bytes, eight of them, and sixteen because A and B are a
|
||||
; shift register: four rotations turn a block number into its offset. The history and the
|
||||
; variables are addressed the same way and for the same reason.
|
||||
;
|
||||
; 0 this branch is running
|
||||
; 1 this branch is not, but an else would turn it on
|
||||
; 2 this branch is not, and an else would not either
|
||||
; +0 state: 0 running, 1 not but an else would, 2 not and an else would not
|
||||
; +1 kind: 0 if, 1 while, 2 for
|
||||
; +2 how many words a for has used
|
||||
; +4 where the line that opened it was, as a script position - see blockKeepWhere
|
||||
;
|
||||
; The two kinds of not-running are what make nesting work without looking down the stack: an
|
||||
; "if" met while something above it is being skipped pushes a 2, so the top of the stack
|
||||
; always says everything, and a line runs when the stack is empty or its top is nought.
|
||||
blockSkipping:
|
||||
|
||||
; DP3 to block A.
|
||||
blockAt:
|
||||
SETD.3 BlockStack
|
||||
RSTB
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
DPUW.3
|
||||
RET
|
||||
|
||||
; DP3 on the block on top. Q is one if there is not one.
|
||||
blockTop:
|
||||
SETD.1 BlockDepth
|
||||
LDA.1
|
||||
BRA blockRunning ; Nothing open, so nothing is being skipped.
|
||||
BRA blockNone
|
||||
DECA
|
||||
SETD.3 BlockStack
|
||||
DPUA.3
|
||||
CALL blockAt
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
blockNone:
|
||||
INIA 0x01
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
RET
|
||||
|
||||
blockSkipping:
|
||||
CALL blockTop
|
||||
BNQ blockRunning ; Nothing open, so nothing is being skipped.
|
||||
LDA.3
|
||||
BRA blockRunning
|
||||
INIA 0x01
|
||||
@@ -2820,7 +2869,8 @@ blockRunning:
|
||||
ADD ; Q is zero: running.
|
||||
RET
|
||||
|
||||
; A holds what to push. Q is one if there is no room for another.
|
||||
; A holds the state to push and LoopKind what kind of block it is. Q is one if there is no
|
||||
; room for another.
|
||||
blockPush:
|
||||
SETD.1 BlockHold
|
||||
STA.1
|
||||
@@ -2830,12 +2880,20 @@ blockPush:
|
||||
CCF
|
||||
SUB
|
||||
BNC blockFull
|
||||
|
||||
LDA.1
|
||||
SETD.3 BlockStack
|
||||
DPUA.3
|
||||
CALL blockAt
|
||||
SETD.1 BlockHold
|
||||
LDA.1
|
||||
STA.3
|
||||
INCD.3
|
||||
SETD.1 LoopKind
|
||||
LDA.1
|
||||
STA.3
|
||||
INCD.3
|
||||
RSTA
|
||||
STA.3 ; No words used yet, whatever kind it is.
|
||||
|
||||
SETD.1 BlockDepth
|
||||
LDA.1
|
||||
INCA
|
||||
@@ -2852,24 +2910,58 @@ blockFull:
|
||||
ADD
|
||||
RET
|
||||
|
||||
; DP3 on the block on top. Q is one if there is not one.
|
||||
blockTop:
|
||||
blockPop:
|
||||
SETD.1 BlockDepth
|
||||
LDA.1
|
||||
BRA blockNone
|
||||
DECA
|
||||
SETD.3 BlockStack
|
||||
DPUA.3
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
STA.1
|
||||
RET
|
||||
blockNone:
|
||||
INIA 0x01
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
|
||||
; ---- Going back to the line that opened a block ----
|
||||
;
|
||||
; A loop is a block that, when it ends, puts the reader back where it started. The position
|
||||
; was kept before the line was read - see ScriptLineIndex in script.asm, because by the time
|
||||
; a line has been read the reader is past it and a line is not a fixed size to subtract. The
|
||||
; block it names is read again on the way back, which is what makes the pointer into it mean
|
||||
; what it meant. Exactly what nesting one script inside another already does, for a different
|
||||
; reason.
|
||||
;
|
||||
; DP3 names the block.
|
||||
blockKeepWhere:
|
||||
SETD.0 ScriptLineIndex
|
||||
PSHD.3
|
||||
POPD.1
|
||||
DPUP.1 0d4
|
||||
CALL sbfsCopyWord
|
||||
SETD.0 ScriptLineAt
|
||||
PSHD.3
|
||||
POPD.1
|
||||
DPUP.1 0d6
|
||||
CALL sbfsCopyWord
|
||||
SETD.0 ScriptLineBlocks
|
||||
PSHD.3
|
||||
POPD.1
|
||||
DPUP.1 0d8
|
||||
CALL sbfsCopyWord
|
||||
RET
|
||||
|
||||
blockGoWhere:
|
||||
PSHD.3
|
||||
POPD.0
|
||||
DPUP.0 0d4
|
||||
SETD.1 ScriptIndex
|
||||
CALL sbfsCopyWord
|
||||
PSHD.3
|
||||
POPD.0
|
||||
DPUP.0 0d6
|
||||
SETD.1 ScriptAt
|
||||
CALL sbfsCopyWord
|
||||
PSHD.3
|
||||
POPD.0
|
||||
DPUP.0 0d8
|
||||
SETD.1 ScriptBlocks
|
||||
CALL sbfsCopyWord
|
||||
CALL scriptReread
|
||||
RET
|
||||
|
||||
; ---- if ----
|
||||
@@ -2879,13 +2971,17 @@ blockNone:
|
||||
; somebody's answer. The note is read at the top of the loop, which is where every command
|
||||
; comes back to and the one place that sees a result before the next line disturbs it.
|
||||
doIf:
|
||||
RSTA
|
||||
SETD.1 LoopKind
|
||||
STA.1
|
||||
doIfKind:
|
||||
CALL blockSkipping
|
||||
BNQ ifSkipped
|
||||
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA ifWhat ; "if" with nothing to decide by.
|
||||
BRA ifWhat ; Nothing to decide by.
|
||||
|
||||
; The rest of the line, moved to the front of it. Forwards, and the source is ahead of the
|
||||
; destination, so one walk does it without anything being overwritten before it is read.
|
||||
@@ -2914,15 +3010,29 @@ ifSkipped:
|
||||
|
||||
ifWhat:
|
||||
SETD.0 IfUsage
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
|
||||
BRI blockComplain
|
||||
ifDeep:
|
||||
SETD.0 IfTooDeep
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
BRI blockComplain
|
||||
|
||||
; ---- while ----
|
||||
;
|
||||
; The same shape as if, and then one thing more: the block remembers where the while line
|
||||
; was, and end goes back to it - so the condition is ASKED AGAIN rather than remembered.
|
||||
doWhile:
|
||||
SETD.1 ScriptDepth
|
||||
LDA.1
|
||||
BRA loopTyped ; There is nothing to go back to at a prompt.
|
||||
INIA 0d1
|
||||
SETD.1 LoopKind
|
||||
STA.1
|
||||
BRI doIfKind
|
||||
|
||||
; Neither loop means anything typed at a prompt: there is no line to go back to. Said once,
|
||||
; because both of them arrive here and a message naming the wrong one is worse than none.
|
||||
loopTyped:
|
||||
SETD.0 LoopTyped
|
||||
BRI blockComplain
|
||||
|
||||
; ---- else ----
|
||||
doElse:
|
||||
@@ -2943,23 +3053,174 @@ elseStays:
|
||||
BRI prompt
|
||||
elseLonely:
|
||||
SETD.0 ElseLonely
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
BRI blockComplain
|
||||
|
||||
; ---- end ----
|
||||
;
|
||||
; What it does depends on what it closes. An if is simply taken away. A loop that was running
|
||||
; goes back to the line that opened it - a while so that its condition is asked again, and a
|
||||
; for so that its next word is taken - and a loop that was not running has finished, so it
|
||||
; goes away like anything else.
|
||||
doEnd:
|
||||
SETD.1 BlockDepth
|
||||
LDA.1
|
||||
BRA endLonely
|
||||
DECA
|
||||
CALL blockTop
|
||||
BNQ endLonely
|
||||
|
||||
LDA.3
|
||||
BNA endAway ; Not running, so whatever it was, it is over.
|
||||
|
||||
INCD.3
|
||||
LDA.3
|
||||
BRA endAway ; An if that ran needs nothing doing.
|
||||
|
||||
INIB 0d2
|
||||
XOR
|
||||
BRQ endForAgain
|
||||
|
||||
; A while: taken away, and the reader put back on its line. The line pushes a fresh block
|
||||
; when it asks its question again.
|
||||
CALL blockTop
|
||||
CALL blockGoWhere
|
||||
CALL blockPop
|
||||
BRI prompt
|
||||
|
||||
endForAgain:
|
||||
; A for: the block stays, because what it has used is in it, and the line it opened with
|
||||
; reads itself again and counts off one more word.
|
||||
INIA 0x01
|
||||
SETD.1 LoopResume
|
||||
STA.1
|
||||
CALL blockTop
|
||||
CALL blockGoWhere
|
||||
BRI prompt
|
||||
|
||||
endAway:
|
||||
CALL blockPop
|
||||
BRI prompt
|
||||
endLonely:
|
||||
SETD.0 EndLonely
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
BRI blockComplain
|
||||
|
||||
; ---- for ----
|
||||
;
|
||||
; "for x in a b c" runs what follows once for each word, with x set to it in turn. THE LINE IS
|
||||
; READ AGAIN ON EVERY TURN and the words counted off from the front, so the only thing a block
|
||||
; has to remember is how many have been used - one byte, rather than a copy of the list in
|
||||
; every block.
|
||||
doFor:
|
||||
SETD.1 ScriptDepth
|
||||
LDA.1
|
||||
BRA loopTyped
|
||||
|
||||
SETD.1 LoopResume
|
||||
LDA.1
|
||||
BNA forAgain
|
||||
|
||||
CALL blockSkipping
|
||||
BNQ ifSkipped
|
||||
|
||||
; A new one. It is pushed running, and set to not-running below if there are no words.
|
||||
INIA 0d2
|
||||
SETD.1 LoopKind
|
||||
STA.1
|
||||
RSTA
|
||||
CALL blockPush
|
||||
BNQ ifDeep
|
||||
CALL blockTop
|
||||
BNQ forLonely
|
||||
CALL blockKeepWhere
|
||||
BRI forWord
|
||||
|
||||
forAgain:
|
||||
; Coming round again: the block that is open already says how many words have gone.
|
||||
RSTA
|
||||
SETD.1 LoopResume
|
||||
STA.1
|
||||
CALL blockTop
|
||||
BNQ forLonely
|
||||
INIA 0d2
|
||||
DPUA.3
|
||||
LDA.3
|
||||
INCA
|
||||
STA.3
|
||||
|
||||
forWord:
|
||||
CALL blockTop
|
||||
BNQ forLonely
|
||||
INIA 0d2
|
||||
DPUA.3
|
||||
LDA.3
|
||||
SETD.1 ForUsed
|
||||
STA.1
|
||||
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
SETD.1 ForVarAt
|
||||
STD.0.1
|
||||
CALL textSplit
|
||||
|
||||
; The word "in" between the name and the words. Split off first and then compared, because
|
||||
; textSame asks whether two whole strings are the same and "in red green blue" is not "in".
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA forWhat
|
||||
CALL textSplit
|
||||
SETD.1 InName
|
||||
CALL textSame
|
||||
BNQ forWhat
|
||||
|
||||
forSkipWords:
|
||||
SETD.1 ForUsed
|
||||
LDA.1
|
||||
BRA forTakeWord
|
||||
DECA
|
||||
STA.1
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA forNoMore
|
||||
CALL textSplit
|
||||
BRI forSkipWords
|
||||
|
||||
forTakeWord:
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA forNoMore
|
||||
SETD.1 ForWordAt
|
||||
STD.0.1
|
||||
CALL textSplit
|
||||
|
||||
SETD.0 ForVarAt
|
||||
LDD.0.0
|
||||
SETD.1 ForWordAt
|
||||
LDD.1.1
|
||||
CALL varSet
|
||||
BNQ forFull
|
||||
|
||||
CALL blockTop
|
||||
BNQ forLonely
|
||||
RSTA
|
||||
STA.3 ; Running, and the body follows.
|
||||
BRI prompt
|
||||
|
||||
forNoMore:
|
||||
; The words have run out, so the body is passed over and the end takes the block away.
|
||||
CALL blockTop
|
||||
BNQ forLonely
|
||||
INIA 0d1
|
||||
STA.3
|
||||
BRI prompt
|
||||
|
||||
forWhat:
|
||||
SETD.0 ForUsage
|
||||
BRI blockComplain
|
||||
forFull:
|
||||
SETD.0 SetVarNoRoom
|
||||
BRI blockComplain
|
||||
forLonely:
|
||||
SETD.0 EndLonely
|
||||
BRI blockComplain
|
||||
|
||||
; ---- same ----
|
||||
;
|
||||
@@ -2982,6 +3243,13 @@ doSame:
|
||||
sameNot:
|
||||
BRI commandFailed
|
||||
|
||||
; DP0 names what went wrong. Says it and marks the line, which is what every one of these
|
||||
; does and is worth writing once.
|
||||
blockComplain:
|
||||
CALL printString
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
|
||||
; Takes the spaces off the front of the line, by moving what is after them to the front. In
|
||||
; place and forwards, with the source ahead of the destination, so one walk does it.
|
||||
lineTrim:
|
||||
@@ -3059,6 +3327,16 @@ blockPassOver:
|
||||
CALL lineFirstIs
|
||||
BRQ ifSkipped
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 WhileName
|
||||
CALL lineFirstIs
|
||||
BRQ ifSkipped
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 ForName
|
||||
CALL lineFirstIs
|
||||
BRQ ifSkipped
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 ElseName
|
||||
CALL lineFirstIs
|
||||
@@ -7274,6 +7552,11 @@ NeedsValue:
|
||||
; NOTHING MAY BE PUT BETWEEN THEM. Tests/docs.sh checks that this run holds exactly the names
|
||||
; the dispatch tests and that the count below agrees, so a command added without a name here
|
||||
; is caught rather than silently left out of what Tab knows about.
|
||||
;
|
||||
; AND EACH LABEL ENDS IN "Name", which is how that check recognises one. A label called
|
||||
; ForName2 was not counted, and the check said the run was one shorter than the count claimed
|
||||
; rather than saying why - which is the right way round for it to fail, but the convention is
|
||||
; worth writing down where the names are rather than only in the checker.
|
||||
ShellNames:
|
||||
DirName:
|
||||
"dir"
|
||||
@@ -7315,10 +7598,14 @@ EndName:
|
||||
"end"
|
||||
SameName:
|
||||
"same"
|
||||
WhileName:
|
||||
"while"
|
||||
ForName:
|
||||
"for"
|
||||
|
||||
; How many of them, since a run of strings does not say where it stops.
|
||||
ShellNameCount:
|
||||
0d20
|
||||
0d22
|
||||
|
||||
SbxText:
|
||||
".sbx"
|
||||
@@ -7342,6 +7629,12 @@ ElseLonely:
|
||||
"else with no if above it"
|
||||
EndLonely:
|
||||
"end with no if above it"
|
||||
LoopTyped:
|
||||
"a loop wants a script to go back through"
|
||||
ForUsage:
|
||||
"for wants a name, then in, then the words"
|
||||
InName:
|
||||
"in"
|
||||
|
||||
; Where the machine is, written out for the prompt, and where in the buffer it begins.
|
||||
; Built from the end backwards, so it starts somewhere in the middle.
|
||||
@@ -7627,11 +7920,24 @@ ShellStack:
|
||||
|
||||
; ---- Which blocks are open, and whether their lines are being run ----
|
||||
BlockStack:
|
||||
#Reserve 0d8
|
||||
#Reserve 0d128
|
||||
BlockDepth:
|
||||
0x00
|
||||
BlockHold:
|
||||
0x00
|
||||
; What kind of block is about to be pushed, since blockPush takes the state in A and there is
|
||||
; nowhere else for a second answer to go.
|
||||
LoopKind:
|
||||
0x00
|
||||
; Set by an end that is sending a for round again, and read by the for line it lands on.
|
||||
LoopResume:
|
||||
0x00
|
||||
ForUsed:
|
||||
0x00
|
||||
ForVarAt:
|
||||
0x00 0x00
|
||||
ForWordAt:
|
||||
0x00 0x00
|
||||
; Set while the line being run is an "if" condition rather than somebody's own command.
|
||||
IfPending:
|
||||
0x00
|
||||
|
||||
@@ -243,6 +243,21 @@ scriptLine:
|
||||
STD.0.1
|
||||
|
||||
scriptLineAgain:
|
||||
; ---- Where this line begins ----
|
||||
;
|
||||
; Kept before it is read, because a loop has to be able to go back to the line that opened
|
||||
; it and by the time that line has been read the reader is past it. Three words, and the
|
||||
; block itself is read again on the way back, which is what scriptReread is for.
|
||||
SETD.0 ScriptIndex
|
||||
SETD.1 ScriptLineIndex
|
||||
CALL sbfsCopyWord
|
||||
SETD.0 ScriptAt
|
||||
SETD.1 ScriptLineAt
|
||||
CALL sbfsCopyWord
|
||||
SETD.0 ScriptBlocks
|
||||
SETD.1 ScriptLineBlocks
|
||||
CALL sbfsCopyWord
|
||||
|
||||
SETD.1 ScriptLength
|
||||
RSTA
|
||||
STA.1
|
||||
@@ -539,6 +554,19 @@ ScriptIndex:
|
||||
0x00 0x00
|
||||
ScriptAt:
|
||||
0x00 0x00
|
||||
|
||||
; ---- And where the line being read began ----
|
||||
;
|
||||
; A loop goes back to the line that opened it, and by the time that line has been read the
|
||||
; reader is past it. So the position is kept before every line rather than worked out
|
||||
; afterwards, which cannot be done: a line is not a fixed size and there is nothing to
|
||||
; subtract.
|
||||
ScriptLineIndex:
|
||||
0x00 0x00
|
||||
ScriptLineAt:
|
||||
0x00 0x00
|
||||
ScriptLineBlocks:
|
||||
0x00 0x00
|
||||
; Saved with the rest, so that a quiet script calling a loud one gets its quiet back when
|
||||
; the loud one finishes. A new script INHERITS it rather than resetting, because a build
|
||||
; that asked for quiet meant its helpers too; only the first script started from the prompt
|
||||
|
||||
Reference in New Issue
Block a user