
    <<CommandPack.Mesa>>
        <<Copyright  1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, December 10, 1982 10:47 am>>
        <<Maxwell, August 29, 1983 8:11 am>>
        <<Paul Rovner, December 2, 1983 5:35 pm>>
        <<Russ Atkinson (RRA) March 7, 1985 4:26:00 am PST>>
    <<>>
    DIRECTORY
        Ascii USING [Lower],
        CommandUtil USING [PairList, Switches],
        IO USING [GetChar, GetIndex, PutChar, PutRope, SetIndex, STREAM, EndOfStream],
        Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, Length, ROPE, Substr];

    CommandPack: PROGRAM
        IMPORTS Ascii, IO, Rope
        EXPORTS CommandUtil = {

        PairList: TYPE = CommandUtil.PairList;
        ROPE: TYPE = Rope.ROPE;

        TokenClass: TYPE = CHAR;
            id: TokenClass = 'I;
            eom: TokenClass = '\003;    -- 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;


            <<external parsing routine>>

            Parse: PUBLIC PROC [s: IO.STREAM] RETURNS [operator: ROPE, argList, resultList: PairList, switches: ROPE] = {

                token: TokenClass;
                idString: ROPE;
                tIndex: CARDINAL;
                c: CHAR _ ' ;

                NextToken: PROC = INLINE {
                    WHILE c = '  OR c = '\n
                        DO c _ IO.GetChar[s ! IO.EndOfStream => {c _ eom; CONTINUE}] ENDLOOP;
                    tIndex _ IO.GetIndex[s] - 1;
                    SELECT c FROM
                        IN ['a..'z], IN ['A..'Z], IN ['0..'9], '<, '., '+, '-, '~, '!, '$ => {
                            idString _ NIL;
                            DO
                                SELECT c FROM
                                    IN ['a..'z], IN ['A..'Z], IN ['0..'9],
                                    '<, '>, '., '+, '-, '~, '!, '$ => {
                                        idString _ Rope.Concat[idString, Rope.FromChar[c]]; 
                                        c _ IO.GetChar[s ! IO.EndOfStream => {c _ eom; CONTINUE}]};
                                    ENDCASE => EXIT;
                                ENDLOOP;
                            token _ id};
                        '_, '[, '], ':, '/ =>
                            {token _ c; c _ IO.GetChar[s ! IO.EndOfStream => {c _ eom; CONTINUE}]};
                        ',, ';, eom => token _ c;
                        ENDCASE => BadCommand[]};

                pair: ARRAY [0..1] OF ROPE;

                PushArg: PROC = INLINE {argList _ CONS[[pair[0], pair[1]], argList]};
                PushResult: PROC = INLINE {resultList _ CONS[[pair[0], pair[1]], resultList]};
                RestoreToken: PROC = {IO.SetIndex[s, tIndex]};
                BadCommand: PROC = {ERROR Failed};

                state: State _ 0;
                operator _ switches _ NIL;  argList _ resultList _ NIL;

                UNTIL state = 17 DO
                    NextToken[];
                    SELECT token FROM
                        id =>
                            SELECT state FROM
                                0 =>    {operator _ idString; state _ 1};
                                2, 8 => {operator _ idString; state _ 9};
                                3 =>    {pair[0]  _ idString; state _ 4};
                                5 =>    {pair[1]  _ idString; PushResult[]; state _ 6};
                                10 =>   {pair[0]  _ idString; state _ 11};
                                12 =>   {pair[1]  _ idString; PushArg[]; state _ 13};
                                15 =>   {switches _ switches.Concat[idString]; 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 _ IO.GetChar[s ! IO.EndOfStream => {c _ eom; CONTINUE}]};
                                13 => {state _ 10; c _ IO.GetChar[s ! IO.EndOfStream => {c _ eom; CONTINUE}]};
                                1, 9, 14, 15, 16 => state _ 17;
                                ENDCASE => BadCommand[];
                        '/ =>
                            SELECT state FROM
                                0, 1, 9, 14, 16 => 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: IO.STREAM,
                operator: ROPE, argList, resultList: PairList, switches: ROPE] = {

                PutList: PROC [list: PairList] = {
                    d.PutChar['[];
                    FOR p: PairList _ list, p.rest UNTIL p = NIL DO
                        IF p.first.key # NIL THEN {IO.PutRope[d, p.first.key];  IO.PutRope[d, ": "]};
                        IO.PutRope[d, p.first.val];
                        IF p.rest # NIL THEN IO.PutRope[d, ", "];
                        ENDLOOP;
                    d.PutChar[']]};

                IF resultList # NIL THEN {
                    IF resultList.rest = NIL AND resultList.first.key = NIL 
                        THEN IO.PutRope[d, resultList.first.val]
                        ELSE PutList[resultList];
                    IO.PutRope[d, " _ "]};
                IO.PutRope[d, operator];
                IF argList # NIL THEN PutList[argList];
                IF switches # NIL AND switches.Length[] > 0 THEN {
                    d.PutChar['/]; IO.PutRope[d, switches]}};

            GetSwitches: PUBLIC PROC [switches: ROPE, default: CommandUtil.Switches] 
                RETURNS[CommandUtil.Switches] = {
                sense: BOOL _ TRUE;
                length: INT _ switches.Length[];
                FOR i: INT IN [0..length) DO
                    c: CHAR = switches.Fetch[i];
                    SELECT Ascii.Lower[c] FROM
                        '-, '~ => sense _ ~sense;
                        IN ['a..'z] => {default[c] _ sense; sense _ TRUE};
                        ENDCASE;
                    ENDLOOP;
                RETURN[default]};

        <<PairList utilities>>

            GetNthPair: PUBLIC PROC [list: PairList, n: CARDINAL, delete: BOOL_FALSE]
                RETURNS [key, value: ROPE_NIL] = {
                i: CARDINAL _ 0;
                FOR p: PairList _ list, p.rest UNTIL p = NIL DO
                    IF i = n THEN {key _ p.first.key; value _ p.first.val; 
                        IF delete THEN p.first.key _ p.first.val _ NIL; EXIT};
                    i _ i+1;
                    ENDLOOP;
                RETURN};

            ListLength: PUBLIC PROC [list: PairList] RETURNS [n: CARDINAL_0] = {
                FOR p: PairList _ list, p.rest UNTIL p = NIL DO n _ n+1 ENDLOOP;
                RETURN};

            KeyValue: PUBLIC PROC [key: ROPE, list: PairList, delete: BOOL_FALSE]
                RETURNS [s: ROPE_NIL] = {
                FOR p: PairList _ list, p.rest UNTIL p = NIL DO
                    IF Rope.Equal[p.first.key, key, FALSE] THEN {
                        s _ p.first.val; IF delete THEN p.first.val _ NIL; EXIT};
                    ENDLOOP;
                RETURN};

        <<file naming utilities>>

            SetExtension: PUBLIC PROC [root, defaultExt: ROPE] RETURNS [name: ROPE] = {
                IF Rope.Find[root, "."] < 0 THEN root _ Rope.Cat[root, ".", defaultExt];
                RETURN[root]};

            GetRootName: PUBLIC PROC [name: ROPE] RETURNS [root: ROPE] = {
                dotIndex: INT _ Rope.Find[name, "."];
                RETURN[IF dotIndex < 0 THEN name ELSE Rope.Substr[name, 0, dotIndex]]};


            }.

