<> <> <<>> DIRECTORY AMBridge USING [SomeRefFromTV], Atom USING [MakeAtom], CedarScanner, FileIO USING [Open], Interpreter USING [Evaluate], IO USING [Flush, int, NUL, PutF, rope, STREAM], Menus, Rope USING [Cat, Equal, Fetch, Length, ROPE, Substr], RTBasic USING [TV], ViewerClasses USING [NotifyProc]; MenuParserImpl: CEDAR PROGRAM IMPORTS AMBridge, Atom, CedarScanner, FileIO, Interpreter, IO, Rope EXPORTS Menus = BEGIN OPEN CedarScanner, Menus; ROPE: TYPE = Rope.ROPE; <> t: Token; menus: LIST OF Menu _ NIL; globalRope: ROPE; errorLog: ROPE; syntaxErrorFound: SIGNAL; <> ParseDescription: PUBLIC PROC [def: ROPE, prevlist: LIST OF Menu _ NIL, errorFile: ROPE _ NIL] RETURNS [LIST OF Menu] = { ENABLE syntaxErrorFound => GOTO SyntaxErrorExit; errorLog _ IF errorFile = NIL THEN "menus.errlog" ELSE errorFile; globalRope _ def; GetNext[first: TRUE]; menus _ prevlist; WHILE t.kind # tokenEOF DO SELECT t.kind FROM tokenID => { MustBe["Relabel"]; ParseRelabel[]; }; tokenSINGLE => { MustBe["["]; ParseMenu[]; }; ENDCASE => SyntaxError["Was expecting keyword 'relabel' or '[' to start menu definition"]; MustBe["]"]; GetNext; ENDLOOP; RETURN[menus]; EXITS SyntaxErrorExit => { RETURN[NIL] }; }; <> 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]; }; SyntaxError: PROC [msg: ROPE] = { <> file: IO.STREAM _ FileIO.Open[errorLog, overwrite]; IO.PutF[file, "MenuParse ErrorLog:\n\n"]; IO.PutF[file, "Syntax error found at token beginning in position [%d]\n", IO.int[t.start] ]; IF msg # NIL THEN IO.PutF[file, "TYPE OF ERROR: [%g]\n\n\n", IO.rope[msg] ]; IO.Flush[file]; SIGNAL syntaxErrorFound; }; ContentsAre: PROC [r: ROPE] RETURNS [BOOLEAN] = { RETURN[Rope.Equal[r, CurrentContents[], FALSE] ]; }; CurrentContents: PROC [] RETURNS [ROPE] = { <> IF t.kind = tokenROPE THEN { dummy: ROPE _ ContentsFromToken[[Get,NIL], t]; RETURN[Rope.Substr[dummy, 1, Rope.Length[dummy] - 2] ]; } ELSE RETURN[ContentsFromToken[[Get,NIL], t] ]; }; GetNext: PROC[kind:TokenKind _ tokenERROR, string: ROPE _ NIL, first: BOOL _ FALSE] = { t _ GetToken[[Get, NIL], IF first THEN 0 ELSE t.next]; WHILE t.kind = tokenCOMMENT DO t _ GetToken[[Get, NIL], t.next]; ENDLOOP; IF kind # tokenERROR THEN { IF t.kind # kind THEN { IF string # NIL THEN SyntaxError[Rope.Cat["Was Expecting the token ->", string, "<-"]] ELSE SyntaxError[Rope.Cat["Was expecting ", SELECT kind FROM tokenID => "an identifier, like a menu name, or an entry name, for example", tokenINT => "an integer", tokenREAL => "a real number", tokenROPE => "a quoted string", tokenATOM => "an atom", tokenSINGLE => "a single character token, like '[' or '{', for example", tokenDOUBLE => "a double character token, like '=>', for example", ENDCASE => "a different kind of token"]]; }; IF string # NIL THEN IF NOT Rope.Equal[string, CurrentContents[], FALSE ] THEN SyntaxError[Rope.Cat["Was Expecting the token ->", string, "<-"]]; }; }; MustBe: PROC[string: ROPE _ NIL] = { IF NOT Rope.Equal[string, CurrentContents[], FALSE] THEN SyntaxError[Rope.Cat["Was Expecting the token ->", string, "<-"]]; }; 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] RETURNS[answer: LIST OF MenuName _ NIL] = { myToken: Token _ GetToken[[GetForNamesFromRope, string], 0]; WHILE myToken.kind = tokenID DO name: ROPE = ContentsFromToken[[GetForNamesFromRope, string], myToken]; answer _ CONS[MakeMenuName[name], answer]; myToken _ GetToken[[GetForNamesFromRope, string], myToken.next]; ENDLOOP; RETURN[answer]; }; MakeMenuName: PROC[rope: ROPE] RETURNS[MenuName] = { RETURN[Atom.MakeAtom[rope]] }; MenuNameEqual: PROC[a, b: ATOM] RETURNS[BOOL] = INLINE { RETURN[a=b] }; <> ParseRelabel: PROC [] = { <> firstParm: ROPE _ NIL; secondParm: ROPE _ NIL; menuname: MenuName _ NIL; oldname: ROPE; newname: ROPE; GetNext[tokenSINGLE, "["]; GetNext[tokenID]; firstParm _ CurrentContents[]; GetNext[tokenID]; secondParm _ CurrentContents[]; GetNext; IF t.kind = tokenID THEN { menuname _ MakeMenuName[firstParm]; oldname _ secondParm; newname _ CurrentContents[]; GetNext[tokenSINGLE, "]"]; } ELSE { menuname _ NIL; oldname _ firstParm; newname _ secondParm; }; MustBe["]"]; <> FOR l: LIST OF Menu _ menus, l.rest UNTIL l = NIL DO IF menuname=NIL OR MenuNameEqual[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.displayData _ newname; ENDLOOP; ENDLOOP; }; ParseMenu: PROC = { replacement: BOOLEAN _ FALSE; prev: LIST OF Menu _ NIL; newMenu: Menu _ NEW[Menus.MenuRec]; GetNext[tokenID]; newMenu.name _ MakeMenuName[CurrentContents[]]; GetNext; ParseMenuParms[newMenu]; MustBe["["]; ParseEntries[newMenu]; MustBe["]"]; FOR l: LIST OF Menu _ menus, l.rest UNTIL l = NIL DO IF MenuNameEqual[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.rest _ LIST[newMenu]; }; }; ParseMenuParms: PROC [newMenu: Menu] = { <> 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["Was expecting a menu parameter keyword ('breakBefore', 'breakAfter', 'beginsActive', or 'notifyProc')"]; GetNext; ENDLOOP; -- while }; ParseEntries: PROC [newMenu: Menu] = { Item: TYPE = Entry; list, tail: LIST OF Item _ NIL; Append: PROC[item: Item] = { prev: LIST OF Item = tail; tail _ LIST[item]; IF prev=NIL THEN list _ tail ELSE prev.rest _ tail; }; WHILE ContentsAre["["] DO entry: Entry = NEW[EntryRec]; GetNext[tokenID]; entry.name _ CurrentContents[]; ParseEntryParms[entry]; MustBe["=>"]; GetNext; IF ContentsAre["["] THEN entry.actions _ GetComplex[] ELSE { action: Action = NEW[ActionRec _ [triggers: allTriggers, input: GetListOfRefAny[]]]; entry.actions _ LIST[action]; }; Append[entry]; MustBe["]"]; GetNext[]; ENDLOOP; newMenu.entries _ list; MustBe["]"]; }; ParseEntryParms: PROC [entry: Entry] = { <> GetNext[]; WHILE t.kind = tokenID DO IF ContentsAre["guarded"] THEN { GetNext[tokenSINGLE, ":"]; entry.guarded _ GetBoolean[]; } ELSE IF ContentsAre["display"] THEN { GetNext[tokenSINGLE, ":"]; GetNext; IF t.kind = tokenROPE OR t.kind = tokenID THEN entry.displayData _ CurrentContents[] ELSE IF ContentsAre["{"] THEN entry.displayData _ GetDisplayProc[] ELSE SyntaxError["Was Expecting an word, quoted string, or {...} for display: parameter"]; } ELSE SyntaxError["Was expecting an entry keyword parameter ('guarded' or 'display')"]; GetNext; ENDLOOP; -- while }; GetComplex: PROC [] RETURNS[LIST OF Action] = { <> Item: TYPE = Action; list, tail: LIST OF Item _ NIL; Append: PROC[item: Item] = { prev: LIST OF Item = tail; tail _ LIST[item]; IF prev=NIL THEN list _ tail ELSE prev.rest _ tail; }; MustBe["["]; WHILE ContentsAre["["] DO action: Action = NEW[ActionRec]; action.triggers _ GetTriggers[]; ParseTriggerParms[action]; MustBe["=>"]; GetNext; action.input _ GetListOfRefAny[]; MustBe["]"]; Append[action]; GetNext; ENDLOOP; MustBe["]"]; RETURN[list]; }; SetTriggersFromRope: PROC[triggers: TriggerSet, r: ROPE] RETURNS[TriggerSet] = { SELECT TRUE FROM Rope.Equal["none", r, FALSE] => NULL; Rope.Equal["all", r, FALSE] => triggers _ ALL[TRUE]; Rope.Equal["leftUp", r, FALSE] => triggers[leftUp] _ TRUE; Rope.Equal["middleUp", r, FALSE] => triggers[middleUp] _ TRUE; Rope.Equal["rightUp", r, FALSE] => triggers[rightUp] _ TRUE; Rope.Equal["shiftLeftUp", r, FALSE] => triggers[shiftLeftUp] _ TRUE; Rope.Equal["shiftMiddleUp", r, FALSE] => triggers[shiftMiddleUp] _ TRUE; Rope.Equal["shiftRightUp", r, FALSE] => triggers[shiftRightUp] _ TRUE; Rope.Equal["allNonShifts", r, FALSE] => triggers[leftUp] _ triggers[middleUp] _ triggers[rightUp] _ TRUE; Rope.Equal["allShifts", r, FALSE] => triggers[shiftLeftUp] _ triggers[shiftMiddleUp] _ triggers[shiftRightUp] _ TRUE; Rope.Equal["allLeft", r, FALSE] => triggers[leftUp] _ triggers[shiftLeftUp] _ TRUE; Rope.Equal["allMiddle", r, FALSE] => triggers[middleUp] _ triggers[shiftMiddleUp] _ TRUE; Rope.Equal["allRight", r, FALSE] => triggers[rightUp] _ triggers[shiftRightUp] _ TRUE; ENDCASE => SyntaxError["Was expecting a trigger keyword ('all', 'leftUp', 'leftshiftup', etc)"]; RETURN[triggers]; }; GetTriggers: PROC [] RETURNS [TriggerSet] = { <> triggers: TriggerSet _ ALL[FALSE]; GetNext; IF ContentsAre["["] THEN { GetNext[tokenID]; WHILE t.kind = tokenID DO triggers _ SetTriggersFromRope[triggers, CurrentContents[]]; GetNext; ENDLOOP; MustBe["]"]; } ELSE IF t.kind = tokenID THEN { triggers _ SetTriggersFromRope[triggers, CurrentContents[]]; } ELSE SyntaxError["Was Expecting a trigger keyword or list of them surrounded by brackets '[' (triggers are 'all', 'leftUp', 'shiftLeftUp', etc)"]; RETURN[triggers]; }; ParseTriggerParms: PROC [action: Action] = { <> GetNext; WHILE t.kind = tokenID DO IF ContentsAre["popUpDoc"] THEN { GetNext[tokenSINGLE, ":"]; GetNext[tokenROPE]; action.popupDoc _ CurrentContents[]; } ELSE IF ContentsAre["makeActive"] THEN { GetNext[tokenSINGLE, ":"]; GetNext[tokenROPE]; action.makeActive _ GetNamesFromRope[CurrentContents[]]; } ELSE IF ContentsAre["makeInactive"] THEN { GetNext[tokenSINGLE, ":"]; GetNext[tokenROPE]; action.makeInactive _ GetNamesFromRope[CurrentContents[]]; } ELSE IF ContentsAre["toggle"] THEN { GetNext[tokenSINGLE, ":"]; GetNext[tokenROPE]; action.toggle _ GetNamesFromRope[CurrentContents[]]; } ELSE IF ContentsAre["guardResponse"] THEN { GetNext[tokenSINGLE, ":"]; GetNext; IF t.kind = tokenROPE THEN action.guardResponse _ CurrentContents[] ELSE action.guardResponse _ GetGuardResponseProc[]; } ELSE SyntaxError["Was expecting a trigger parameter ('popUpDoc', 'makeActive', 'makeInactive', 'toggle', or 'guardResponse')"]; GetNext; ENDLOOP; -- while }; GetListOfRefAny: PROC [] RETURNS [LIST OF REF ANY] = { <> Item: TYPE = REF ANY; list, tail: LIST OF Item _ NIL; Append: PROC[item: Item] = { prev: LIST OF Item = tail; tail _ LIST[item]; IF prev=NIL THEN list _ tail ELSE prev.rest _ tail; }; WHILE NOT (ContentsAre["]"] OR ContentsAre["}"]) DO SELECT t.kind FROM tokenINT => Append[NEW[INTEGER _ IntFromToken[[Get,NIL], t]]]; tokenREAL => Append[NEW[REAL _ RealFromToken[[Get,NIL], t]]]; tokenROPE => Append[RopeFromToken[[Get,NIL], t]]; tokenATOM => Append[AtomFromToken[[Get,NIL], t]]; ENDCASE => SyntaxError["Was expecting a valid token for a LIST OF REF ANY"]; GetNext; ENDLOOP; RETURN[list]; }; GetGuardResponseProc: PROC [] RETURNS [REF Menus.UnGuardRec] = { RETURN[ NEW [Menus.UnGuardRec _ [NARROW[GeneralGetProc[], REF Menus.UnGuardProc] ^, GetListOfRefAny[] ] ] ]; }; GetNotifyProc: PROC [] RETURNS [answer: ViewerClasses.NotifyProc] = { answer _ NARROW[GeneralGetProc[], REF ViewerClasses.NotifyProc]^; MustBe["}"]; -- all the other guys call GetListOfRefAny, but notify procs specs don't get parms }; GetDisplayProc: PROC [] RETURNS [REF Menus.DrawingRec] = { RETURN[ NEW [Menus.DrawingRec _ [NARROW[GeneralGetProc[], REF Menus.DrawingProc] ^, GetListOfRefAny[] ] ] ]; }; GetQualifiedName: PROC [] RETURNS [ROPE] = { <> firstPart: ROPE; secondPart: ROPE; GetNext[tokenID]; firstPart _ CurrentContents[]; GetNext[tokenSINGLE, "."]; GetNext[tokenID]; secondPart _ CurrentContents[]; RETURN[Rope.Cat[firstPart, ".", secondPart] ]; }; GeneralGetProc: PROC [] RETURNS [REF ANY] = { <> qualifiedName: ROPE; temp: RTBasic.TV; myRef: REF ANY; MustBe["{"]; qualifiedName _ GetQualifiedName[]; GetNext; -- GetListOfRefAny will always be called right after me, and wants first token read [temp, ----, ----] _ Interpreter.Evaluate[qualifiedName]; TRUSTED {myRef _ AMBridge.SomeRefFromTV[temp]}; RETURN[myRef]; }; GetBoolean: PROC [] RETURNS [answer: BOOLEAN] = { GetNext[tokenID]; IF ContentsAre["TRUE"] THEN RETURN[TRUE] ELSE IF ContentsAre["FALSE"] THEN RETURN[FALSE] ELSE SyntaxError["Was expecting to find a boolean keyword (either 'TRUE' or 'FALSE')"]; }; END.