Let the shell run a file of commands
'do <file>' runs the lines in a file as though they had been typed. The
only thing a script changes is where the next line comes from: everything
below shellReadLine - splitting the line, matching it, loading a program -
cannot tell the difference and does not have to.
What makes a file a script is '#!' on the front of it, not its name and not
a flag in its entry. The rule this filesystem keeps is that an entry holds
only what the content cannot say about itself, and a script can say what it
is; the loader already refuses anything that is not SBEX, so the two kinds
of runnable file turn each other away without either knowing about the
other. It is also the deferred half of the file-typing design, which said
to wait for a second kind of runnable thing before building any of it. This
is that second kind.
'#' is a directive and ';' is a comment, as in SplitBit assembly - one rule
across the machine rather than two dialects. Not Unix's convention: there
'#!' really is a comment that only the kernel reads, while here the shell
requires it and refuses the file without it, so calling it a comment would
be a lie about what it does.
A script stops at the first line that does not work, which is what the
LineFailed groundwork was for. Comments and blank lines are dropped by the
reader rather than by the dispatch, so they are not echoed either. A script
running out hands back to the console rather than ending the shell, because
running out of file and running out of typing are not the same thing. The
interactive assembler reads through the same path, so a script can contain
a block of assembly.
Three things this cost that were not obvious:
- RET puts A and B back, so a routine cannot answer in them. scriptByte
returning the character in A assembled, ran, and handed the caller its
own A back every time. It answers in memory now.
- A last line with no newline is still a line. Text files do not reliably
end with one and an editor eating it is a bad way to find out a command
did not run.
- Not LastStatus. See the commit before this one.
Six checks in three tests, two of which are about byte positions rather
than behaviour - a command lying across the boundary between two blocks,
and that missing newline - so their fixtures are generated rather than
committed, where an editor cannot helpfully repair them.
Nesting is not in yet: a script cannot run a script. That wants a stack of
positions rather than the one the reader keeps.
Also derives native.sh's self-hosting source list from cosmos.asm's own
#Include lines. It was a hand written list and went stale the moment
script.asm existed - the fourth time a list beside a thing has drifted from
the thing - so it now asks the thing.
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
fc56e815fc
commit
2466d79d9c
@@ -47,6 +47,7 @@
|
||||
#Include text.asm
|
||||
#Include sbfs.asm
|
||||
#Include services.asm
|
||||
#Include script.asm
|
||||
|
||||
#Program
|
||||
|
||||
@@ -133,6 +134,24 @@ bootNoDisk:
|
||||
; shell. Only saying so leaves the monitor, or a program breaking the machine badly enough
|
||||
; to need starting again.
|
||||
prompt:
|
||||
; ---- A script stops at the first line that did not work ----
|
||||
;
|
||||
; Checked here, before the next line is read, because this is the one place every command
|
||||
; 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
|
||||
LDA.1
|
||||
BRA promptWhere
|
||||
SETD.1 LineFailed
|
||||
LDA.1
|
||||
BRA promptWhere
|
||||
SETD.0 ScriptStopped
|
||||
CALL printString
|
||||
CALL newLine
|
||||
CALL scriptClose
|
||||
|
||||
promptWhere:
|
||||
; Where you are, but only when that is not obvious. At the root the prompt is the one it
|
||||
; has always been, so a machine nobody has moved about on looks exactly as it did - and
|
||||
; every recorded test that never says "cd" keeps its recorded prompt.
|
||||
@@ -160,7 +179,7 @@ promptSay:
|
||||
|
||||
SETD.0 CommandLine
|
||||
INIB 0d63
|
||||
CALL readLine
|
||||
CALL shellReadLine
|
||||
|
||||
; Running out of typing is how this ends. It is not the same as an empty line, which is
|
||||
; just somebody pressing return, and the shell should sit there when that happens.
|
||||
@@ -230,6 +249,11 @@ promptSay:
|
||||
CALL textSame
|
||||
BRQ doRename
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 DoName
|
||||
CALL textSame
|
||||
BRQ doScript
|
||||
|
||||
SETD.0 CommandLine
|
||||
SETD.1 HelpName
|
||||
CALL textSame
|
||||
@@ -282,6 +306,31 @@ promptSay:
|
||||
CALL textSame
|
||||
BRQ doAssemble
|
||||
|
||||
; ---- Where a line comes from ----
|
||||
;
|
||||
; The whole of what a script is. Everything below this - splitting the line, matching it
|
||||
; against the commands, loading a program - cannot tell the difference and does not have to.
|
||||
;
|
||||
; A SCRIPT RUNNING OUT IS NOT THE SAME AS TYPING RUNNING OUT. The console ending means there
|
||||
; 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
|
||||
LDA.1
|
||||
BRA shellReadTyped
|
||||
CALL scriptLine
|
||||
BNQ shellReadTyped
|
||||
|
||||
; 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.
|
||||
CALL printString
|
||||
CALL newLine
|
||||
RET
|
||||
|
||||
shellReadTyped:
|
||||
CALL readLine
|
||||
RET
|
||||
|
||||
; ---- A command that did not work ----
|
||||
;
|
||||
; The one place a failure is recorded, so that the thing reading lines out of a file can
|
||||
@@ -1531,6 +1580,37 @@ fileComplain:
|
||||
CALL newLine
|
||||
BRI commandFailed
|
||||
|
||||
; ---- do ----
|
||||
;
|
||||
; Runs the lines in a file as though they had been typed. What makes a file one of these is
|
||||
; the #! on the front of it, not its name and not a flag in its directory entry: the rule is
|
||||
; that the entry holds only what the content cannot say about itself, and a script can say
|
||||
; what it is. The loader already refuses anything that is not SBEX, so the two kinds of
|
||||
; runnable thing turn each other away without either of them knowing about the other.
|
||||
doScript:
|
||||
SETD.1 TextRest
|
||||
LDD.0.1
|
||||
LDA.0
|
||||
BRA scriptNoName
|
||||
|
||||
CALL scriptOpen
|
||||
BRQ prompt ; It is open, and the next line read will come from it.
|
||||
|
||||
; Which of the two went wrong. A number would be no use to anybody here.
|
||||
MVQA
|
||||
INIB 0x01
|
||||
CCF
|
||||
SUB
|
||||
BRQ scriptNoFile
|
||||
SETD.0 ScriptNotOne
|
||||
BRI fileComplain
|
||||
scriptNoFile:
|
||||
SETD.0 ScriptNoFile
|
||||
BRI fileComplain
|
||||
scriptNoName:
|
||||
SETD.0 ScriptUsage
|
||||
BRI fileComplain
|
||||
|
||||
; ---- run ----
|
||||
;
|
||||
; Hands the machine to whatever was loaded. Where the Stack is now is written down first,
|
||||
@@ -3094,7 +3174,7 @@ assembleLine:
|
||||
|
||||
SETD.0 AsmLine
|
||||
INIB 0d40
|
||||
CALL readLine
|
||||
CALL shellReadLine
|
||||
|
||||
INA 0x01
|
||||
INIB 0x02 ; ENDED, so there is nothing more to assemble.
|
||||
@@ -3517,6 +3597,14 @@ OnFallback:
|
||||
"
|
||||
NoDisk:
|
||||
"no filesystem on the disk"
|
||||
ScriptUsage:
|
||||
"do: give me the name of a script"
|
||||
ScriptNoFile:
|
||||
"do: cannot find it"
|
||||
ScriptNotOne:
|
||||
"do: that is not a script - it wants #! on the first line"
|
||||
ScriptStopped:
|
||||
"stopped: that line did not work"
|
||||
Unknown:
|
||||
"I do not know: "
|
||||
Farewell:
|
||||
@@ -3696,6 +3784,8 @@ NeedsValue:
|
||||
"that one needs a value after it"
|
||||
DirName:
|
||||
"dir"
|
||||
DoName:
|
||||
"do"
|
||||
LoadName:
|
||||
"load"
|
||||
RunName:
|
||||
|
||||
Reference in New Issue
Block a user