-- Commander.mesa; edited by Johnsson, 9-Sep-80 20:35:49
-- edited by Sweet, 20-Apr-81 14:44:22
-- edited by Bruce, 10-Jan-81 15:32:29
-- edited by Andrew Birrell August 9, 1982 1:13 pm (for Cedar 3.3!)
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
Ascii USING [CR, DEL, ESC, NUL, SP],
CommanderDefs USING [CommandBlock, CommandBlockHandle, CommandParam, ParamType],
Exec,
Frame USING [MyLocalFrame],
Inline USING [BITAND, BITOR],
PrincOps USING [StateVector],
Runtime USING [CallDebugger],
Storage USING [Node, String, CopyString, Free],
String USING [AppendChar, AppendString, EquivalentString, InvalidNumber,StringBoundsFault],
Time USING [AppendCurrent],
TTY,
UserTerminal;
Commander: PROGRAM
IMPORTS Exec, Frame, Inline, Runtime, Storage, String, Time, TTY, UserTerminal
EXPORTS CommanderDefs =
BEGIN OPEN String, CommanderDefs, Ascii;
CommandItem: TYPE = RECORD [
cb: CommandBlockHandle, link: POINTER TO CommandItem];
StringItem: TYPE = RECORD [link: POINTER TO StringItem, string: STRING];
commandHead: POINTER TO CommandItem ← NIL;
stringHead: POINTER TO StringItem ← NIL;
SyntaxError: ERROR = CODE;
Help: SIGNAL = CODE;
BadName: ERROR = CODE;
BadParam: ERROR [type: ParamType] = CODE;
GetDebugger: PROCEDURE = BEGIN Runtime.CallDebugger[NIL]; END;
ExtensionIs: PROCEDURE [name, ext: STRING] RETURNS [BOOLEAN] =
BEGIN
t: STRING ← [40];
i: CARDINAL;
IF name.length <= ext.length THEN RETURN[FALSE];
FOR i IN [name.length - ext.length..name.length) DO
String.AppendChar[t, name[i]]; ENDLOOP;
RETURN[String.EquivalentString[t, ext]]
END;
CheckForExtension: PROCEDURE [name, ext: STRING] =
BEGIN
i: CARDINAL;
FOR i IN [0..name.length) DO IF name[i] = '. THEN RETURN; ENDLOOP;
String.AppendString[name, ext];
RETURN
END;
AddCommand: PUBLIC PROCEDURE [name: STRING, proc: PROCEDURE, numargs: CARDINAL]
RETURNS [CommandBlockHandle] =
BEGIN OPEN Storage;
c: POINTER TO CommandItem ← Node[SIZE[CommandItem]];
cb: CommandBlockHandle ← Node[
SIZE[CommandBlock] + numargs*SIZE[CommandParam]];
c↑ ← CommandItem[cb: cb, link: commandHead];
commandHead ← c;
cb.name ← name;
cb.proc ← proc;
cb.nparams ← numargs;
RETURN[cb]
END;
NewString: PROCEDURE [s: STRING] RETURNS [ns: STRING] =
BEGIN OPEN Storage;
si: POINTER TO StringItem ← Node[SIZE[StringItem]];
si↑ ← StringItem[link: stringHead, string: ns ← CopyString[s]];
stringHead ← si;
RETURN
END;
FreeStrings: PROCEDURE =
BEGIN OPEN Storage;
next: POINTER TO StringItem;
WHILE stringHead # NIL DO
next ← stringHead.link;
Free[stringHead.string];
Free[stringHead];
stringHead ← next;
ENDLOOP;
RETURN
END;
WriteEOL: PROCEDURE = {IF ~TTY.NewLine[Exec.w] THEN TTY.PutCR[Exec.w]};
firstCommand: BOOLEAN ← TRUE;
interactive: BOOLEAN ← FALSE;
SetupCom: PROCEDURE [c: POINTER TO Exec.CommandLine ← @Exec.commandLine] RETURNS [BOOLEAN] =
{RETURN[~(c.s = NIL OR c.i >= c.s.length)]};
SkipToken: PROCEDURE [c: POINTER TO Exec.CommandLine ← @Exec.commandLine] =
BEGIN
foundToken: BOOLEAN ← FALSE;
ch: CHARACTER;
DO
IF c.i >= c.s.length THEN EXIT;
ch ← c.s[c.i]; c.i ← c.i + 1;
SELECT ch FROM
Ascii.SP, Ascii.CR => IF foundToken THEN EXIT;
ENDCASE => foundToken ← TRUE;
ENDLOOP;
RETURN
END;
-- code to get a line from the user, handling ESC and ?; stuffs it in line
line: STRING ← NIL;
lineTerminator: CHARACTER;
Lindex: CARDINAL;
AppendStringToLine: PROCEDURE [s: STRING] =
BEGIN
UNTIL (s.length + line.length) <= line.maxlength DO AddToLine[]; ENDLOOP;
AppendString[line, s];
RETURN
END;
AppendCharToLine: PROCEDURE [c: CHARACTER] =
BEGIN
IF line.length = line.maxlength THEN AddToLine[];
AppendChar[line, c];
RETURN
END;
ReadUserLine: PROCEDURE [newstring: BOOLEAN] =
BEGIN -- read line from user; also handles <ESC> and '? for input from user
IF line = NIL THEN line ← Storage.String[80];
[] ← TTY.GetEditedString[Exec.w, line, LineMonitor, newstring !
resume => BEGIN newstring ← FALSE; RETRY END];
Lindex ← 0;
RETURN
END;
resume: SIGNAL = CODE;
LineMonitor: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
BEGIN
SELECT c FROM
CR => RETURN[TRUE];
'? =>
BEGIN
WriteChar['?];
IF line.length = 0 THEN SIGNAL Help;
PromptCompletions[];
SIGNAL resume;
ERROR
END;
ESC => BEGIN ExtendLine[]; SIGNAL resume; ERROR END;
ENDCASE => RETURN[FALSE]
END;
PromptCompletions: PROCEDURE =
BEGIN
id: STRING = [40];
atLeastOne: BOOLEAN ← FALSE;
p: POINTER TO CommandItem;
IF GetLastID[id] THEN
FOR p ← commandHead, p.link UNTIL p = NIL DO
IF PrefixString[prefix: id, of: p.cb.name] THEN
BEGIN
IF ~atLeastOne THEN WriteEOL[];
WriteChar[SP];
WriteChar[SP];
WriteString[p.cb.name];
atLeastOne ← TRUE;
END;
ENDLOOP;
IF atLeastOne THEN ReTypeLine[] ELSE UserTerminal.BlinkDisplay[];
RETURN
END;
ExtendLine: PROCEDURE =
BEGIN
i: CARDINAL;
id: STRING = [40];
match: STRING = [40];
moreThanOne, atLeastOne: BOOLEAN ← FALSE;
p: POINTER TO CommandItem;
IF GetLastID[id] THEN
BEGIN
FOR p ← commandHead, p.link UNTIL p = NIL DO
IF PrefixString[prefix: id, of: p.cb.name] THEN
IF ~atLeastOne THEN
BEGIN AppendString[match, p.cb.name]; atLeastOne ← TRUE; END
ELSE BEGIN AndString[match, p.cb.name]; moreThanOne ← TRUE; END;
ENDLOOP;
END;
IF atLeastOne AND id.length # match.length THEN
BEGIN
FOR i IN [id.length..match.length) DO
AppendCharToLine[match[i]]; WriteChar[match[i]]; ENDLOOP;
IF moreThanOne THEN UserTerminal.BlinkDisplay[];
END
ELSE UserTerminal.BlinkDisplay[];
RETURN
END;
PrefixString: PROCEDURE [prefix, of: STRING] RETURNS [BOOLEAN] =
BEGIN
i: CARDINAL;
IF prefix.length > of.length THEN RETURN[FALSE];
FOR i IN [0..prefix.length) DO
IF ~EquivalentChar[prefix[i], of[i]] THEN RETURN[FALSE]; ENDLOOP;
RETURN[TRUE]
END;
AndString: PROCEDURE [accum, s: STRING] =
BEGIN
i: CARDINAL;
FOR i IN [0..s.length) DO
IF ~EquivalentChar[accum[i], s[i]] THEN BEGIN accum.length ← i; RETURN END;
ENDLOOP;
accum.length ← s.length;
RETURN;
END;
GetLastID: PROCEDURE [id: STRING] RETURNS [BOOLEAN] =
BEGIN
i, start: CARDINAL;
c: CHARACTER;
IF line.length = 0 THEN RETURN[FALSE];
start ← line.length;
FOR i DECREASING IN [0..line.length) DO
IF AlphaNumeric[c ← line[i]] THEN start ← i
ELSE IF c = '] OR c = SP THEN EXIT ELSE RETURN[FALSE];
ENDLOOP;
FOR i IN [start..line.length) DO id[i - start] ← line[i] ENDLOOP;
id.length ← line.length - start;
RETURN[id.length # 0]
END;
AlphaNumeric: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
{RETURN[Alphabetic[c] OR Digit[c]]};
Alphabetic: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
BEGIN RETURN[Inline.BITAND[c, 337B] IN [100B..132B]] END;
Digit: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
BEGIN RETURN[c IN ['0..'9]] END;
EquivalentChar: PROCEDURE [c, d: CHARACTER] RETURNS [BOOLEAN] =
BEGIN OPEN Inline; RETURN[BITOR[c, 40B] = BITOR[d, 40B]] END;
AddToLine: PROCEDURE = INLINE {line ← Storage.CopyString[line, 80]};
ReTypeLine: PROCEDURE = BEGIN WriteEOL[]; WriteString[line]; RETURN END;
-- code to handle characters
command: STRING = [100];
executing: BOOLEAN ← FALSE;
Cindex: CARDINAL;
currentChar: CHARACTER;
EndOfString: SIGNAL = CODE;
GetChar: PROCEDURE RETURNS [CHARACTER] ← GetCommandChar;
PutBackChar: PROCEDURE ← PutBackCommandChar;
GetCommandChar: PROCEDURE RETURNS [CHARACTER] =
BEGIN
IF Cindex >= command.length THEN currentChar ← NUL
ELSE BEGIN currentChar ← command[Cindex]; Cindex ← Cindex + 1; END;
RETURN[currentChar]
END;
PutBackCommandChar: PROCEDURE =
BEGIN
IF currentChar = NUL THEN RETURN;
IF Cindex = 0 THEN ERROR;
Cindex ← Cindex - 1;
RETURN
END;
CommandOverFlow: SIGNAL = CODE;
SetUpCommand: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
BEGIN
ENABLE StringBoundsFault => SIGNAL CommandOverFlow;
RETURN[IF interactive THEN CopyFromLine[] ELSE CopyFromExecCommand[]];
END
END;
CopyFromLine: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
c: CHARACTER ← NUL;
DO
IF Lindex >= line.length THEN RETURN[FALSE];
c ← line[Lindex];
Lindex ← Lindex + 1;
IF c # SP AND c # CR THEN EXIT;
ENDLOOP;
command.length ← 0;
DO
AppendChar[command, c];
IF c = '] OR Lindex >= line.length THEN EXIT;
c ← line[Lindex];
Lindex ← Lindex + 1;
ENDLOOP;
Cindex ← 0;
RETURN[TRUE]
END;
SkipExecBlanks: PROCEDURE [c: POINTER TO Exec.CommandLine ← @Exec.commandLine] RETURNS [ch: CHARACTER] =
BEGIN
UNTIL c.i >= c.s.length DO
ch ← c.s[c.i]; c.i ← c.i + 1; IF ch # SP AND ch # CR THEN EXIT;
ENDLOOP;
END;
WriteChar: PROC [ch: CHARACTER] = {TTY.PutChar[Exec.w, ch]};
WriteString: PROC [s: STRING] = {TTY.PutString[Exec.w, s]};
WriteLine: PROC [s: STRING] = {TTY.PutLine[Exec.w, s]};
EndOf: PROC [c: POINTER TO Exec.CommandLine ← @Exec.commandLine] RETURNS [BOOLEAN] =
{RETURN[c.s = NIL OR c.i >= c.s.length]};
CopyFromExecCommand: PROCEDURE [c: POINTER TO Exec.CommandLine ← @Exec.commandLine] RETURNS [BOOLEAN] =
BEGIN
ch: CHARACTER;
IF ~SetupCom[c] THEN BEGIN interactive ← TRUE; RETURN[FALSE] END;
ch ← SkipExecBlanks[c];
IF EndOf[c] THEN
IF firstCommand THEN BEGIN interactive ← TRUE; RETURN[FALSE] END
ELSE SIGNAL CommandExit;
command.length ← 0;
WriteEOL[];
WriteChar['<];
WriteChar['>];
DO
AppendChar[command, ch];
WriteChar[ch];
IF ch = '] OR EndOf[c] THEN EXIT;
ch ← c.s[c.i]; c.i ← c.i + 1;
ENDLOOP;
WriteEOL[];
Cindex ← 0;
RETURN[TRUE]
END;
GetName: PROCEDURE [n: STRING] =
BEGIN
n.length ← 0;
DO
IF AlphaNumeric[GetChar[]] THEN AppendChar[n, currentChar] ELSE EXIT;
ENDLOOP;
PutBackChar[];
SkipBlanks[];
IF GetChar[] # '[ THEN SE[];
RETURN
END;
SkipBlanks: PROCEDURE =
BEGIN DO IF GetChar[] # SP THEN BEGIN PutBackChar[]; RETURN END; ENDLOOP END;
-- code to parse user command
ParseCommand: PROCEDURE [state: POINTER TO PrincOps.StateVector] =
BEGIN
proc: STRING = [40];
cb: CommandBlockHandle;
i: CARDINAL;
GetName[proc];
cb ← FindProc[proc].cb;
FOR i IN [0..cb.nparams) DO
state.stk[i] ← GetArg[cb, cb.params[i].type];
IF GetChar[] # (IF i = cb.nparams - 1 THEN '] ELSE ',) THEN SE[];
ENDLOOP;
state.dest ← LOOPHOLE[cb.proc];
state.stkptr ← cb.nparams;
RETURN
END;
FindProc: PROCEDURE [name: STRING] RETURNS [p: POINTER TO CommandItem] =
BEGIN
FOR p ← commandHead, p.link UNTIL p = NIL DO
IF EquivalentString[name, p.cb.name] THEN RETURN; ENDLOOP;
ERROR BadName;
END;
GetArg: PROCEDURE [cb: CommandBlockHandle, t: ParamType]
RETURNS [a: UNSPECIFIED] =
BEGIN
s: STRING = [100];
SkipBlanks[];
SELECT GetChar[] FROM
'" =>
BEGIN
IF t # string THEN ERROR BadParam[t];
DO
IF GetChar[] = '" AND GetChar[] # '" THEN {PutBackChar[]; EXIT};
IF executing THEN AppendChar[s, currentChar];
ENDLOOP;
IF executing THEN a ← NewString[s];
END;
'' => {IF t # character THEN ERROR BadParam[t]; a ← GetChar[]};
IN ['0..'9], '(, '- =>
BEGIN
IF t # numeric THEN ERROR BadParam[t];
PutBackChar[];
a ← ExpressionToNumber[];
END;
IN ['a..'z], IN ['A..'Z] => {
SELECT t FROM
boolean =>
IF currentChar = 'T THEN a ← GetTRUE[t]
ELSE IF currentChar = 'F THEN a ← GetFALSE[t];
string => {
DO
SELECT currentChar FROM
',, '], CR, NUL => {PutBackChar[]; EXIT};
ENDCASE => IF executing THEN AppendChar[s, currentChar];
[] ← GetChar[];
ENDLOOP;
IF executing THEN a ← NewString[s]};
ENDCASE => ERROR BadParam[t]};
ENDCASE => ERROR BadParam[t];
SkipBlanks[];
RETURN
END;
GetTRUE: PROCEDURE [t: ParamType] RETURNS [BOOLEAN] =
BEGIN
IF GetChar[] # 'R THEN ERROR BadParam[t];
IF GetChar[] # 'U THEN ERROR BadParam[t];
IF GetChar[] # 'E THEN ERROR BadParam[t];
RETURN[TRUE];
END;
GetFALSE: PROCEDURE [t: ParamType] RETURNS [BOOLEAN] =
BEGIN
IF GetChar[] # 'A THEN ERROR BadParam[t];
IF GetChar[] # 'L THEN ERROR BadParam[t];
IF GetChar[] # 'S THEN ERROR BadParam[t];
IF GetChar[] # 'E THEN ERROR BadParam[t];
RETURN[FALSE];
END;
-- code to parse user commands in interactive mode
ParsePromptedCommand: PROCEDURE =
BEGIN
proc: STRING = [40];
cb: CommandBlockHandle;
IF GetLastID[proc] THEN
BEGIN cb ← FindProc[proc].cb; GetPromptedArgs[cb]; Confirm[]; RETURN END;
lineTerminator ← CR;
RETURN
END;
CRFound: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] = BEGIN RETURN[c = CR] END;
GetPromptedArgs: PROCEDURE [cb: CommandBlockHandle] =
BEGIN
i: CARDINAL;
cindex: CARDINAL;
cstring: STRING = [100];
GetArgChar: PROCEDURE RETURNS [c: CHARACTER] =
BEGIN
IF cindex >= cstring.length THEN currentChar ← NUL
ELSE BEGIN currentChar ← cstring[cindex]; cindex ← cindex + 1; END;
RETURN[currentChar]
END;
PutBackArgChar: PROCEDURE =
BEGIN
IF currentChar = NUL THEN RETURN;
IF cindex = 0 THEN ERROR;
cindex ← cindex - 1;
RETURN
END;
GetChar ← GetArgChar;
PutBackChar ← PutBackArgChar;
AppendCharToLine['[];
FOR i IN [0..cb.nparams) DO
WriteString["
"];
WriteString[cb.params[i].prompt];
WriteChar[':];
WriteChar[' ];
[] ← TTY.GetEditedString[Exec.w, cstring, CRFound, TRUE];
cindex ← 0;
[] ← GetArg[cb, cb.params[i].type];
AppendStringToLine[cstring];
AppendCharToLine[',];
ENDLOOP;
IF cb.nparams # 0 THEN line[line.length - 1] ← '] ELSE AppendCharToLine[']];
GetChar ← GetCommandChar;
PutBackChar ← PutBackCommandChar;
RETURN
END;
Confirm: PROCEDURE =
BEGIN
char: CHARACTER;
WriteString[" [confirm]"];
DO
char ← TTY.GetChar[Exec.w];
SELECT char FROM
DEL => SIGNAL TTY.Rubout;
CR => BEGIN WriteEOL[]; EXIT END;
SP => BEGIN WriteString["
<>"]; EXIT END;
ENDCASE => WriteChar['?];
ENDLOOP;
lineTerminator ← char;
RETURN
END;
-- parsing arithmetic expressions
symbol: Symbol;
Symbol: TYPE = RECORD [
body: SELECT tag: * FROM
num => [val: INTEGER], delim => [char: CHARACTER], ENDCASE];
Num: TYPE = num Symbol;
SE: PROCEDURE = BEGIN ERROR SyntaxError END;
Scan: PROCEDURE =
BEGIN
v8, v10, radix, number: CARDINAL;
digits: ARRAY CHARACTER ['0..'9] OF CARDINAL = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
firstchar: BOOLEAN ← TRUE;
v8 ← v10 ← 0;
SkipBlanks[];
DO
SELECT GetChar[
] FROM
IN ['0..'9] =>
BEGIN
v8 ← v8*8 + digits[currentChar];
v10 ← v10*10 + digits[currentChar];
END;
'M =>
BEGIN
IF ~firstchar THEN SE[];
IF ~(GetChar[] = 'O AND GetChar[] = 'D) THEN SE[];
IF ~Alphabetic[GetChar[]] THEN PutBackChar[] ELSE SE[];
symbol ← [delim['!]];
RETURN
END;
'b, 'B => BEGIN number ← v8; radix ← 8; GOTO exponent END;
'd, 'D => BEGIN number ← v10; radix ← 10; GOTO exponent END;
SP => GOTO done;
NUL => IF ~firstchar THEN GOTO done ELSE BEGIN symbol ← nul; RETURN END;
'(, '/, '*, '+, '-, '), '], ', =>
IF firstchar THEN BEGIN symbol ← [delim[currentChar]]; RETURN END
ELSE BEGIN PutBackChar[]; GOTO done END;
ENDCASE => SIGNAL InvalidNumber;
firstchar ← FALSE;
REPEAT
done => BEGIN symbol ← [num[v10]]; RETURN END;
exponent =>
BEGIN
IF firstchar THEN SE[];
v10 ← 0;
WHILE Digit[GetChar[]] DO
v10 ← v10*10 + digits[currentChar];
REPEAT FINISHED => PutBackChar[]; -- took one too many
ENDLOOP;
THROUGH [1..v10] DO number ← number*radix ENDLOOP;
symbol ← [num[number]];
RETURN
END;
ENDLOOP;
END;
nul: Symbol = [delim[NUL]];
Primary: PROCEDURE RETURNS [n: Num] =
BEGIN
WITH s: symbol SELECT FROM
delim =>
BEGIN
IF s.char # '( THEN SE[];
Scan[];
n ← Exp[];
WITH symbol SELECT FROM
delim => IF char = ') THEN BEGIN Scan[]; RETURN END;
ENDCASE;
SE[];
END;
num => BEGIN n ← s; Scan[]; RETURN END;
ENDCASE
END;
Factor: PROCEDURE RETURNS [n: Num] =
BEGIN
WITH symbol SELECT FROM
delim =>
IF char = '- THEN BEGIN Scan[]; n ← Primary[]; n.val ← -n.val; RETURN END;
ENDCASE;
RETURN[Primary[]]
END;
Product: PROCEDURE RETURNS [n: Num] =
BEGIN
x: Num;
n ← Factor[];
DO
WITH symbol SELECT FROM
delim =>
SELECT char FROM
'* => BEGIN Scan[]; n.val ← Factor[].val*n.val; END;
'/ => BEGIN Scan[]; x ← Factor[]; n.val ← n.val/x.val; END;
'! => BEGIN Scan[]; x ← Factor[]; n.val ← n.val MOD x.val; END;
ENDCASE => EXIT;
ENDCASE => EXIT;
ENDLOOP;
RETURN
END;
Exp: PROCEDURE RETURNS [n: Num] =
BEGIN
n ← Product[];
DO
WITH symbol SELECT FROM
delim =>
SELECT char FROM
'+ => BEGIN Scan[]; n.val ← Product[].val + n.val; END;
'- => BEGIN Scan[]; n.val ← n.val - Product[].val; END;
'], ', => BEGIN PutBackChar[]; EXIT END;
NUL, ') => EXIT;
ENDCASE => SE[];
ENDCASE => EXIT;
ENDLOOP;
RETURN
END;
ExpressionToNumber: PROCEDURE RETURNS [INTEGER] =
BEGIN Scan[]; RETURN[Exp[].val] END;
ShowSE: PROCEDURE =
BEGIN
IF ~executing THEN BEGIN WriteChar['?]; RETURN END;
WriteEOL[];
IF interactive THEN WriteString[command];
WriteEOL[];
THROUGH [1..(Cindex + (IF interactive THEN 0 ELSE 2))) DO
WriteChar['.]; ENDLOOP;
WriteChar['↑];
RETURN
END;
Driver: PROCEDURE =
BEGIN
state: PrincOps.StateVector;
newline: BOOLEAN;
ci: POINTER TO CommandItem;
i: CARDINAL;
BEGIN
ENABLE
BEGIN
SyntaxError, InvalidNumber, StringBoundsFault =>
BEGIN ShowSE[]; GO TO abort END;
CommandOverFlow =>
BEGIN WriteEOL[]; WriteString["Command too long!"]; GO TO abort END;
BadName => BEGIN ShowSE[]; WriteString[" not found!"]; GO TO abort END;
BadParam =>
BEGIN
ShowSE[];
WriteString[" expected "];
SELECT type FROM
string => WriteString["string"];
character => WriteString["character"];
numeric => WriteString["numerical"];
ENDCASE;
WriteString[" parameter"];
GO TO abort
END;
TTY.Rubout => BEGIN WriteString[" XXX"]; GO TO abort END;
Help =>
BEGIN
WriteEOL[];
FOR ci ← commandHead, ci.link UNTIL ci = NIL DO
WriteString[ci.cb.name];
WriteChar['[];
FOR i IN [0..ci.cb.nparams) DO
IF i # 0 THEN WriteChar[',];
SELECT ci.cb.params[
i].type FROM
string => WriteChar['"];
character => WriteChar[''];
ENDCASE;
WriteString[ci.cb.params[i].prompt];
SELECT ci.cb.params[i].type FROM string => WriteChar['"]; ENDCASE;
ENDLOOP;
WriteChar[']];
IF ci.link # NIL THEN BEGIN WriteChar[',]; WriteChar[' ]; END;
ENDLOOP;
GO TO abort
END;
UNWIND => FreeStrings[]
END;
newline ← TRUE;
executing ← FALSE;
IF interactive THEN
BEGIN
WriteEOL[];
WriteChar['<];
WriteChar['>];
DO
ENABLE StringBoundsFault => BEGIN AddToLine[]; RESUME [line] END;
ReadUserLine[newline];
newline ← FALSE;
ParsePromptedCommand[];
IF lineTerminator = CR THEN EXIT;
ENDLOOP;
END;
GetChar ← GetCommandChar;
PutBackChar ← PutBackCommandChar;
executing ← TRUE;
WHILE SetUpCommand[] DO
ParseCommand[@state];
state.instbyte ← 0;
state.source ← LOOPHOLE[Frame.MyLocalFrame[]];
firstCommand ← FALSE;
TRANSFER WITH state;
state ← STATE;
ENDLOOP;
executing ← FALSE;
EXITS abort => NULL;
END;
FreeStrings[];
RETURN
END;
Quit: PROCEDURE = BEGIN SIGNAL CommandExit; END;
WriteHerald: PROCEDURE =
BEGIN
time: STRING ← [22];
WriteEOL[];
IF h # NIL THEN WriteLine[h];
Time.AppendCurrent[time];
time.length ← time.length - 3;
WriteLine[time];
END;
h: STRING ← NIL;
InitCommander: PUBLIC PROCEDURE [herald: STRING] =
BEGIN
h ← herald;
[] ← AddCommand["Quit", Quit, 0];
[] ← AddCommand["Debug", GetDebugger, 0];
END;
CommandExit: SIGNAL = CODE;
WaitCommands: PUBLIC PROCEDURE =
BEGIN
IF h = NIL THEN InitCommander[NIL];
executing ← interactive ← FALSE;
firstCommand ← TRUE;
WriteHerald[];
DO Driver[ ! CommandExit => EXIT]; ENDLOOP;
END;
END.
-- Here is the grammar for the command line
CommandLine ::= PromptedCommandList <CR> | NonPromptedCommandList ;
NonPromptedCommandList <EOF>
PromptedCommandList ::= PromptedCommand | Command | CommandList <SP> Command
| CommandList <SP> PromptedCommand
NonPromptedCommandList ::= Command | CommandList <SP> Command
Command ::= ID [ ParamList ]
PromptedCommand ::= ID <CR> PromptedParamList
ParamList ::= Param | ParamList , Param
PromptedParamList ::= Param | PromptedParamList <CR> Param
Param ::= " STRING " | ' CHARACTER | Expression | <empty>
Expression ::= Product | Expression + Product | Expression - Product
Product ::= Factor | Product * Factor | Product / Factor | Product MOD Factor
Factor ::= - Primary | Primary
Primary ::= NUM | ( Expression )