DIRECTORY
CedarScanner,
FileIO,
IO,
Menus,
Rope,
ViewerClasses;
MenuParser: CEDAR PROGRAM
IMPORTS FileIO, IO, CedarScanner, Rope =
BEGIN OPEN CedarScanner;
global data:
t: Token;
menus: LIST OF Menus.Menu ← NIL;
globalRope: Rope.ROPE;
errorLog: IO.STREAM;
utilty procedures:
Get: GetProc = {
[data: REF, index: INT] RETURNS [CHAR]
length: INTEGER ← Rope.Length[globalRope];
IF index >= length THEN RETURN[IO.NUL]
ELSE RETURN Rope.Fetch[globalRope, index];
};
Debug:
PROC [msg: Rope.
ROPE] = {
IO.PutF[errorLog, "%g\n", IO.rope[msg]];
IO.Flush[errorLog];
}; -- Debug
SyntaxError:
PROC = {
this routine is called any time a syntax error is discovered.
IO.PutF[errorLog, "Syntax error found at token beginning in position [%d]\n", IO.int[t.start]];
ERROR;
};
CurrentContentsAre:
PROC [r: Rope.
ROPE]
RETURNS [
BOOLEAN] = {
RETURN[Rope.Equal[r, ContentsFromToken[[Get,NIL], t] ] ];
};
CurrentContents:
PROC []
RETURNS [Rope.
ROPE] = {
RETURN[ContentsFromToken[[Get,NIL], t] ];
};
ContentsAre:
PROC [r: Rope.
ROPE]
RETURNS [
BOOLEAN] = {
RETURN[ Rope.Equal[r, ContentsFromToken[[Get,NIL],t], FALSE] ];
};
GetNext:
PROC[kind:TokenKind ← tokenERROR, string: Rope.
ROPE ←
NIL, first:
BOOL ←
FALSE] = {
t ← GetToken[[Get, NIL], IF first THEN 0 ELSE t.next];
Debug[Rope.Cat["Just Got Token -->", ContentsFromToken[[Get, NIL], t], "<--"] ];
IF kind # tokenERROR
THEN {
IF t.kind # kind THEN SyntaxError;
IF string # NIL THEN IF NOT Rope.Equal[string, ContentsFromToken[[Get, NIL], t]] THEN SyntaxError;
};
};
MustBe:
PROC[string: Rope.
ROPE ←
NIL] = {
IF NOT Rope.Equal[string, ContentsFromToken[[Get, NIL], t]] THEN SyntaxError;
};
SeparateQualifiedName:
PROC[name:Rope.
ROPE]
RETURNS[first:Rope.
ROPE, second:Rope.
ROPE] = {
periodIndex: INTEGER ← Rope.Index[s1: name, s2: "."];
IF periodIndex = Rope.Length[name] THEN SyntaxError;
first ← Rope.Substr[name, 0, periodIndex];
second ← Rope.Substr[name, periodIndex + 1, Rope.Length[name] - periodIndex -1];
RETURN[first, second];
};
GetForNamesFromRope: GetProc = {
[data: REF, index: INT] RETURNS [CHAR]
length: INTEGER ← Rope.Length[NARROW[data]];
IF index >= length THEN RETURN[IO.NUL]
ELSE RETURN Rope.Fetch[NARROW[data], index];
};
GetNamesFromRope:
PROC[string: Rope.
ROPE]
RETURNS[answer:
LIST
OF Rope.
ROPE] = {
myToken: Token;
answer ← NIL;
myToken ← GetToken[[GetForNamesFromRope, string], 0];
WHILE myToken.kind = tokenID
DO
answer ← CONS[RopeFromToken[[GetForNamesFromRope, string], myToken], answer];
myToken ← GetToken[[GetForNamesFromRope, string], myToken.next];
ENDLOOP;
RETURN[answer];
};
the user entry
ParseDescription:
PROC [def: Rope.
ROPE, prevlist:
LIST
OF Menus.Menu ←
NIL, errorFile: Rope.
ROPE ← "menuParse.errlog"]
RETURNS [
LIST
OF Menus.Menu] = {
errorLog ← FileIO.Open[errorFile, overwrite];
Debug["starting"];
globalRope ← def;
IO.Put[errorLog, IO.rope["Menu ErrorLog:\n\n"] ];
GetNext[first: TRUE];
menus ← prevlist;
WHILE t.kind # tokenEOF
DO
SELECT t.kind
FROM
tokenID => {
MustBe["Relabel"];
ParseRelabel[];
};
tokenSINGLE => {
MustBe["["];
ParseMenu[];
};
ENDCASE => SyntaxError;
GetNext;
ENDLOOP;
RETURN[menus];
};
internal parsing routines
ParseRelabel:
PROC [] = {
menuname: Rope.ROPE ← NIL;
module: Rope.ROPE;
oldname: Rope.ROPE;
newname: Rope.ROPE;
GetNext[tokenID, "["];
GetNext[tokenROPE];
oldname ← CurrentContents[];
[module, oldname] ← SeparateQualifiedName[oldname];
GetNext[tokenROPE];
newname ← CurrentContents[];
FOR l:
LIST
OF Menus.Menu ← menus, l.rest
UNTIL l =
NIL
DO
IF menuname =
NIL
OR Rope.Equal[menuname, l.first.name]
THEN
FOR e:
LIST
OF Menus.Entry ← l.first.entries, e.rest
UNTIL e =
NIL
DO
IF Rope.Equal[e.first.name, oldname] THEN e.first.name ← newname;
ENDLOOP;
ENDLOOP;
};
ParseMenu:
PROC= {
replacement: BOOLEAN ← FALSE;
prev: LIST OF Menus.Menu ← NIL;
newMenu: REF Menus.Menu ← NEW[Menus.Menu];
GetNext[tokenID];
newMenu.name ← CurrentContents[];
GetNext;
ParseMenuParms[newMenu];
MustBe["["];
ParseEntries[newMenu];
MustBe["]"];
FOR l:
LIST
OF Menus.Menu ← menus, l.rest
UNTIL l =
NIL
DO
IF Rope.Equal[newMenu.name, l.first.name]
THEN {
l.first ← newMenu^;
replacement ← TRUE;
};
prev ← l;
ENDLOOP;
IF
NOT replacement
THEN {
IF menus = NIL THEN menus ← LIST[newMenu^]
ELSE prev.first ← newMenu^;
};
};
ParseMenuParms:
PROC [newMenu:
REF Menus.Menu] = {
assumes first token already read for it by the caller.
WHILE t.kind = tokenID
DO
IF ContentsAre["breakBefore"]
THEN {
GetNext[tokenSINGLE, ":"];
newMenu.breakBefore ← GetBoolean[];
}
ELSE
IF ContentsAre["breakAfter"]
THEN {
GetNext[tokenSINGLE, ":"];
newMenu.breakAfter ← GetBoolean[];
}
ELSE
IF ContentsAre["beginsActive"]
THEN {
GetNext[tokenSINGLE, ":"];
newMenu.beginsActive ← GetBoolean[];
}
ELSE
IF ContentsAre["notifyProc"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext[tokenSINGLE, "{"];
newMenu.notify ← GetNotifyProc[];
}
ELSE SyntaxError;
GetNext;
ENDLOOP; -- while
};
ParseEntries:
PROC [newMenu:
REF Menus.Menu] = {
newEntry: REF Menus.Entry;
WHILE CurrentContentsAre["["]
DO
newEntry ← NEW[Menus.Entry];
GetNext[tokenID];
newEntry.name ← CurrentContents[];
ParseEntryParms[newEntry];
GetNext[tokenDOUBLE, "=>"];
GetNext;
IF CurrentContentsAre["["] THEN newEntry.actions ← GetComplex[]
ELSE {
MustBe["=>"];
newEntry.actions ← LIST[ Menus.EntryNotifyRecord[trigger: LIST[all], notifyData: GetListOfRefAny[]] ];
GetNext[tokenSINGLE, "]"];
};
add entry to the menu, at the end of the list:
IF newMenu.entries = NIL THEN newMenu.entries ← LIST[newEntry^]
ELSE {
prev: LIST OF Menus.Entry ← NIL;
FOR l:
LIST
OF Menus.Entry ← newMenu.entries, l.rest
UNTIL l =
NIL
DO
prev ← l;
ENDLOOP;
prev.rest ← LIST[newEntry^];
};
ENDLOOP;
};
ParseEntryParms:
PROC [newEntry:
REF Menus.Entry] = {
assumes first token already read for it by the caller.
WHILE t.kind = tokenID
DO
IF ContentsAre["guarded"]
THEN {
GetNext[tokenSINGLE, ":"];
newEntry.guarded ← GetBoolean[];
}
ELSE
IF ContentsAre["display"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext;
IF t.kind = tokenROPE THEN newEntry.displayData ← CurrentContents[]
ELSE newEntry.displayData ← GetDisplayProc[];
}
ELSE SyntaxError;
GetNext;
ENDLOOP; -- while
};
GetComplex:
PROC []
RETURNS[answer:
LIST
OF Menus.EntryNotifyRecord] = {
newEntryNotifyRec: REF Menus.EntryNotifyRecord;
assumes first token already read for it by the caller.
answer ← NIL;
MustBe["["];
WHILE CurrentContentsAre["["]
DO
newEntryNotifyRec ← NEW[Menus.EntryNotifyRecord];
newEntryNotifyRec.trigger ← GetTriggers[];
ParseTriggerParms[newEntryNotifyRec];
MustBe["=>"];
newEntryNotifyRec.notifyData ← GetListOfRefAny[];
MustBe["]"];
add newEntryNotifyRec to answer, at the end of the list:
IF answer = NIL THEN answer ← LIST[newEntryNotifyRec^]
ELSE {
prev: LIST OF Menus.EntryNotifyRecord ← NIL;
FOR l:
LIST
OF Menus.EntryNotifyRecord ← answer, l.rest
UNTIL l =
NIL
DO
prev ← l;
ENDLOOP;
prev.rest ← LIST[newEntryNotifyRec^];
};
GetNext;
ENDLOOP;
RETURN[answer];
};
GetTriggers:
PROC []
RETURNS [answer:
LIST
OF Menus.MenuEntryTrigger] = {
NOTE: triggers can either be a single ID, or '[' ID* ']' — we do NOT assume that the caller has read our first token! We DO NOT eat the next token, either.
answer ← NIL;
GetNext;
IF CurrentContentsAre["["]
THEN {
GetNext[tokenID];
WHILE t.kind = tokenID
DO
order for this list doesn't matter, so I can just CONS it onto the front
TriggerFromRope[CurrentContents[], answer];
ENDLOOP;
MustBe["]"];
}
ELSE IF t.kind = tokenID THEN TriggerFromRope[CurrentContents[], answer]
ELSE SyntaxError;
};
TriggerFromRope:
PROC [r: Rope.
ROPE, l:
LIST
OF Menus.MenuEntryTrigger] = {
the Trigger(s) the rope indicates are CONSed onto the front of the list
SELECT
TRUE
FROM
Rope.Equal["notrigger", r] => NULL;
Rope.Equal["all", r] => l ← CONS[all, l];
Rope.Equal["leftup", r] => l ← CONS[leftup, l];
Rope.Equal["middleup", r] => l ← CONS[middleup, l];
Rope.Equal["rightup", r] => l ← CONS[rightup, l];
Rope.Equal["shiftleftup", r] => l ← CONS[shiftleftup, l];
Rope.Equal["shiftmiddleup", r] => l ← CONS[shiftmiddleup, l];
Rope.Equal["shiftrightup", r] => l ← CONS[shiftrightup, l];
Rope.Equal["allnonshifts", r] => {
l ← CONS[leftup, l];
l ← CONS[middleup, l];
l ← CONS[rightup, l]
};
Rope.Equal["allshifts", r] => {
l ← CONS[shiftleftup, l];
l ← CONS[shiftmiddleup, l];
l ← CONS[shiftrightup, l]
};
ENDCASE => SyntaxError;
};
ParseTriggerParms:
PROC [newEntryNotifyRec:
REF Menus.EntryNotifyRecord] = {
Does NOT assume first token already read for it by the caller.
GetNext;
WHILE t.kind = tokenID
DO
IF ContentsAre["popUpDoc"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext[tokenROPE];
newEntryNotifyRec.popupDoc ← CurrentContents[];
}
ELSE
IF ContentsAre["makeActive"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext[tokenROPE];
newEntryNotifyRec.makeActive ← GetNamesFromRope[CurrentContents[]];
}
ELSE
IF ContentsAre["makeInActive"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext[tokenROPE];
newEntryNotifyRec.makeInActive ← GetNamesFromRope[CurrentContents[]];
}
ELSE
IF ContentsAre["toggle"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext[tokenROPE];
newEntryNotifyRec.toggle ← GetNamesFromRope[CurrentContents[]];
}
ELSE
IF ContentsAre["guardResponse"]
THEN {
GetNext[tokenSINGLE, ":"];
GetNext;
IF t.kind = tokenROPE THEN newEntryNotifyRec.guardResponse ← CurrentContents[]
ELSE newEntryNotifyRec.guardResponse ← GetGuardResponseProc[];
}
ELSE SyntaxError;
GetNext;
ENDLOOP; -- while
};
GetListOfRefAny:
PROC []
RETURNS [answer:
LIST
OF
REF
ANY] = {
reads until it hits a ']' — assumes caller has NOT read the first token
r: LIST OF REF ANY ← NIL; -- the reversed list — order matters here
answer ← NIL;
MustBe["=>"];
GetNext;
WHILE
NOT CurrentContentsAre["]"]
DO
SELECT t.kind
FROM
tokenINT => r ← CONS[NEW[INTEGER ← IntFromToken[[Get,NIL], t]], r];
tokenREAL => r ← CONS[NEW[REAL ← RealFromToken[[Get,NIL], t]], r];
tokenROPE => r ← CONS[RopeFromToken[[Get,NIL], t], r]; -- Rope is already a REF
tokenATOM => r ← CONS[NEW[ATOM ← AtomFromToken[[Get,NIL], t]], r];
tokenCOMMENT => NULL;
ENDCASE => SyntaxError;
GetNext;
ENDLOOP;
now build the list we really want to return, which has the proper order:
FOR l:
LIST
OF
REF
ANY ← r, l.rest
UNTIL l =
NIL
DO
answer ← CONS[l.first, answer];
ENDLOOP;
};
GetGuardResponseProc:
PROC []
RETURNS [
REF Menus.UnGuardRec] = {
runtime stuff
ERROR;
};
GetNotifyProc:
PROC []
RETURNS [ViewerClasses.NotifyProc] = {
runtime stuff
ERROR;
};
GetDisplayProc:
PROC []
RETURNS [
REF Menus.DrawingRec] = {
runtime stuff
ERROR;
};
GetBoolean:
PROC []
RETURNS [answer:
BOOLEAN] = {
GetNext[tokenID];
IF ContentsAre["TRUE"] THEN RETURN[TRUE]
ELSE IF ContentsAre["FALSE"] THEN RETURN[FALSE]
ELSE SyntaxError;
};
END.