-- file CommandPack.Mesa -- last modified by Satterthwaite, September 5, 1982 2:50 pm -- last modified by Russ Atkinson, 24-Nov-80 14:40:48 DIRECTORY Ascii: TYPE USING [CR, SP], CharIO: TYPE USING [PutChar, PutString], CommandUtil: TYPE USING [CommandObject, CommandPtr], Heap: TYPE USING [systemZone], Stream: TYPE USING [Handle], Strings: TYPE USING [ String, SubString, SubStringDescriptor, AppendChar, AppendString, EquivalentSubStrings]; CommandPack: PROGRAM IMPORTS CharIO, Heap, Strings EXPORTS CommandUtil = { OPEN CommandUtil; String: TYPE = Strings.String; Pair: TYPE = RECORD [key, val: String, next: PairList]; PairList: PUBLIC TYPE = LONG POINTER TO Pair; TokenClass: TYPE = CHAR; id: TokenClass = 'I; eom: TokenClass = 03c; -- ControlC State: TYPE = [0..17]; -- 0: -- 1: id -- 2: id ← -- 3: [ ?{id : id}, -- 4: [ ?{id : id}, id -- 5: [ ?{id : id}, id : -- 6: [ ?{id : id}, id : id -- 7: [ ?{id : id}, ] -- 8: [ ?{id : id}, ] ← -- 9: LHS ← id -- 10: ?(LHS ←) id [ ?{id : id}, -- 11: ?(LHS ←) id [ ?{id : id}, id -- 12: ?(LHS ←) id [ ?{id : id}, id : -- 13: ?(LHS ←) id [ ?{id : id}, id : id -- 14: ?(LHS ←) id [ ?{id : id}, ] -- 15: ?(LHS) id ?([ ?{id : id}, ])) / -- 16: ?(?(LHS) id ?([ ?{id : id}, ])) / id -- 17: ?(?(LHS) id ?([ ?{id : id}, ])) ?(/ ?id) (;|eom) | eom -- where LHS = id | [ ?{id : id}, ] Failed: PUBLIC ERROR = CODE; -- stream-like utilities GetChar: PROC [p: CommandPtr] RETURNS [c: CHAR] = { IF p.pos >= p.len THEN RETURN [eom]; c ← p.data[p.pos]; p.pos ← p.pos + 1}; GetIndex: PROC [p: CommandPtr, delta: INTEGER←0] RETURNS [CARDINAL] = { pos: CARDINAL ← p.pos + delta; IF delta < 0 AND pos > p.pos THEN pos ← 0; RETURN [pos]}; SetIndex: PROC [p: CommandPtr, index: CARDINAL] = {p.pos ← index}; -- external parsing routine Parse: PUBLIC PROC [s: CommandPtr, opX, argX, resultX: CARDINAL] RETURNS [operator: String, argList, resultList: PairList, switches: String] = { token: TokenClass; state: State; idString: STRING ← [100]; tIndex: CARDINAL; c: CHAR; NextToken: PROC = INLINE { WHILE c = Ascii.SP OR c = Ascii.CR DO c ← GetChar[s] ENDLOOP; tIndex ← GetIndex[s] - 1; SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '<, '., '+, '-, '~, '!, '$ => { idString.length ← 0; DO SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '<, '>, '., '+, '-, '~, '!, '$ => { Strings.AppendChar[idString, c]; c ← GetChar[s]}; ENDCASE => EXIT; ENDLOOP; token ← id}; '←, '[, '], ':, '/ => {token ← c; c ← GetChar[s]}; ',, ';, eom => token ← c; ENDCASE => BadCommand[]}; CopyId: PROC [extra: CARDINAL] RETURNS [String] = INLINE { RETURN [CopyString[idString, extra]]}; pair: ARRAY [0..1] OF String; PushArg: PROC = INLINE {argList ← AddPair[argList, pair[0], pair[1]]}; PushResult: PROC = INLINE {resultList ← AddPair[resultList, pair[0], pair[1]]}; RestoreToken: PROC = {SetIndex[s, tIndex]}; BadCommand: PROC = { operator ← FreeString[operator]; resultList ← FreePairList[resultList]; argList ← FreePairList[argList]; switches ← FreeString[switches]; ERROR Failed}; c ← ' ; state ← 0; operator ← switches ← NIL; argList ← resultList ← NIL; UNTIL state = 17 DO NextToken[]; SELECT token FROM id => SELECT state FROM 0 => {operator ← CopyId[MAX[resultX, opX]]; state ← 1}; 2, 8 => {operator ← CopyId[opX]; state ← 9}; 3 => {pair[0] ← CopyId[0]; state ← 4}; 5 => {pair[1] ← CopyId[resultX]; PushResult[]; state ← 6}; 10 => {pair[0] ← CopyId[0]; state ← 11}; 12 => {pair[1] ← CopyId[argX]; PushArg[]; state ← 13}; 15 => {switches ← CopyId[0]; state ← 16}; 1, 9, 14, 16 => {RestoreToken[]; state ← 17}; ENDCASE => BadCommand[]; '← => SELECT state FROM 1 => { pair[0] ← NIL; pair[1] ← operator; operator ← NIL; PushResult[]; state ← 2}; 7 => state ← 8; ENDCASE => BadCommand[]; '[ => SELECT state FROM 0 => state ← 3; 1, 9 => state ← 10; 14 => {RestoreToken[]; state ← 17}; ENDCASE => BadCommand[]; '] => SELECT state FROM 3, 6 => state ← 7; 10, 13 => state ← 14; ENDCASE => BadCommand[]; ': => SELECT state FROM 4 => state ← 5; 11 => state ← 12; ENDCASE => BadCommand[]; ', => SELECT state FROM 6 => {state ← 3; c ← GetChar[s]}; 13 => {state ← 10; c ← GetChar[s]}; 1, 9, 14, 15, 16 => state ← 17; ENDCASE => BadCommand[]; '/ => SELECT state FROM 0, 1, 9, 14 => state ← 15; ENDCASE => BadCommand[]; '; => SELECT state FROM 1, 9, 14, 15, 16 => state ← 17; ENDCASE => BadCommand[]; eom => SELECT state FROM 0, 1, 9, 14, 15, 16 => state ← 17; ENDCASE => BadCommand[]; ENDCASE; ENDLOOP; RETURN}; Echo: PUBLIC PROC [ d: Stream.Handle, operator: String, argList, resultList: PairList, switches: String] = { OPEN CharIO; EchoList: PROC [list: PairList] = { PutChar[d, '[]; FOR p: PairList ← list, p.next UNTIL p = NIL DO IF p.key # NIL THEN {PutString[d, p.key]; PutString[d, ": "L]}; PutString[d, p.val]; IF p.next # NIL THEN PutString[d, ", "L]; ENDLOOP; PutChar[d, ']]}; IF resultList # NIL THEN { IF resultList.next = NIL AND resultList.key = NIL THEN PutString[d, resultList.val] ELSE EchoList[resultList]; PutString[d, " ← "L]}; PutString[d, operator]; IF argList # NIL THEN EchoList[argList]; IF switches # NIL AND switches.length > 0 THEN { PutString[d, "/"L]; PutString[d, switches]}}; -- string utilities CopyString: PUBLIC PROC [s: String, extra: CARDINAL←0] RETURNS [String] = { copy: String = (Heap.systemZone).NEW[StringBody[(IF s=NIL THEN 0 ELSE s.length) + extra]]; IF s # NIL THEN Strings.AppendString[copy, s]; RETURN [copy]}; FreeString: PUBLIC PROC [s: String] RETURNS [String] = { IF s # NIL THEN (Heap.systemZone).FREE[@s]; RETURN [NIL]}; -- PairList utilities AddPair: PUBLIC PROC [list: PairList, key,val: String] RETURNS [PairList] = { new: PairList ← (Heap.systemZone).NEW[Pair]; new↑ ← [key: key, val: val, next: list]; RETURN [new]}; GetNthPair: PUBLIC PROC [list: PairList, n: CARDINAL, delete: BOOL←FALSE] RETURNS [key, value: String←NIL] = { i: CARDINAL ← 0; FOR p: PairList ← list, p.next UNTIL p = NIL DO IF i = n THEN {key ← p.key; value ← p.val; IF delete THEN p.key ← p.val ← NIL; EXIT}; i ← i+1; ENDLOOP; RETURN}; ListLength: PUBLIC PROC [list: PairList] RETURNS [n: CARDINAL←0] = { FOR p: PairList ← list, p.next UNTIL p = NIL DO n ← n+1 ENDLOOP; RETURN}; KeyValue: PUBLIC PROC [key: Strings.SubString, list: PairList, delete: BOOL←FALSE] RETURNS [s: String←NIL] = { FOR p: PairList ← list, p.next UNTIL p = NIL DO ss: Strings.SubStringDescriptor ← [base: p.key, offset: 0, length: p.key.length]; IF Strings.EquivalentSubStrings[@ss, key] THEN { s ← p.val; IF delete THEN p.val ← NIL; EXIT}; ENDLOOP; RETURN}; FreePairList: PUBLIC PROC [list: PairList] RETURNS [PairList] = { next: PairList; p: PairList ← list; UNTIL p = NIL DO next ← p.next; [] ← FreeString[p.key]; [] ← FreeString[p.val]; (Heap.systemZone).FREE[@p]; p ← next; ENDLOOP; RETURN [NIL]}; -- file naming utilities SetExtension: PUBLIC PROC [root, defaultExt: String] RETURNS [name: String] = { SELECT TRUE FROM ~Dotted[root] => { name ← AdjustString[root, defaultExt.length + 2]; Strings.AppendChar[name, '.]; Strings.AppendString[name, defaultExt]}; ENDCASE => name ← root; RETURN}; Dotted: PROC [s: String] RETURNS [BOOL] = INLINE { FOR i: CARDINAL IN [0..s.length) DO IF s[i] = '. THEN RETURN [TRUE] ENDLOOP; RETURN [FALSE]}; -- storage allocation AdjustString: PROC [old: String, extra: CARDINAL] RETURNS [new: String] = { IF old.length + extra <= old.maxlength THEN new ← old ELSE { new ← (Heap.systemZone).NEW[StringBody[old.length + extra]]; Strings.AppendString[new, old]; (Heap.systemZone).FREE[@old]}; RETURN}; }.