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}, ]
external parsing routine
Parse: 
PUBLIC 
PROC [s: 
IO.
STREAM]
RETURNS [operator: 
ROPE, argList, resultList: PairList, switches: 
ROPE] = {
token: TokenClass ¬ 0C;
idString: ROPE ¬ NIL;
tIndex: CARDINAL ¬ 0;
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: MimCommandUtil.Switches] 
RETURNS[MimCommandUtil.Switches] = {
sense: BOOL ¬ TRUE;
length: INT ¬ switches.Length[];
FOR i: 
INT 
IN [0..length) 
DO
c: CHAR = switches.Fetch[i];
tc: CHAR = Ascii.Lower[c]; -- workaround for Mimosa bug.
SELECT tc 
FROM
'-, '~ => sense ¬ ~sense;
IN ['a..'z] => {default[c] ¬ sense; sense ¬ TRUE};
ENDCASE;
 
ENDLOOP;
 
RETURN[default]};
 
 
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};
 
 
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]]};
 
}.