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 = { 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 = { 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. .MenuParseImpl.mesa; Written by Randy Pausch Last edited by Doug Wyatt, October 12, 1983 3:12 pm global data: the user entry utilty procedures: [data: REF, index: INT] RETURNS [CHAR] this routine is called any time a syntax error is discovered. one special case: A rope literal would normally return with the quote marks still in the string [data: REF, index: INT] RETURNS [CHAR] internal parsing routines assumes that the first '[' hasn't been read now affect the data structure assumes first token already read for it by the caller. assumes first token NOT read for it by the caller. assumes first token already read for it by the caller. 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. Does NOT assume first token already read for it by the caller. reads until it hits a ']'  assumes caller has read the first token assumes first token not read, doesn't read past where it needs to assumes the "{" read by caller, doesn't read past where it needs to ÊјJ™+J™3J™šÏk ˜ Jšœ œ˜Jšœœ ˜J˜ Jšœœ˜Jšœ œ ˜Jšœœœœ˜/J˜Jšœœœ ˜5Jšœœœ˜Jšœœ˜!J˜—Jšœœ˜Jšœ4œ˜CJšœ˜ Jšœœœ˜!J˜Jšœœœ˜J˜Jšœ ™ J˜ Jšœœœœ˜Jšœ œ˜Jšœ œ˜Jšœœ˜J˜Jšœ™šÏnœœœœ œœœ œœœœœ ˜yJšœœ˜0Jš œ œ œœœ ˜AJšœ˜Jšœœ˜J˜šœ˜šœ˜šœ ˜ Jšœ˜Jšœ˜J˜—šœ˜Jšœ ˜ Jšœ ˜ J˜—JšœS˜Z—J˜ J˜Jšœ˜Jšœ˜—Jšœœœ˜)J˜J˜—J˜Jšœ™šœ˜Jšœ&™&Jšœœ˜*Jš œœœœœ˜&Jšœœ˜*J˜J˜—šž œœœ˜!Jšœ=™=Jšœœœ$˜3Jšœ'˜)JšœHœ˜\Jš œœœœ)œ ˜LJšœ ˜Jšœ˜J˜J˜—š ž œœœœœ˜1Jšœ"œ˜1J˜J˜—šžœœœœ˜+Jšœ_™_šœœ˜Jšœœœ˜.Jšœ1˜7J˜—Jšœœœ˜.J˜J˜—š žœœ&œœ œœ˜WJš œœœœœ ˜6šœ˜Jšœœ ˜!Jšœ˜J˜—šœœ˜šœœ˜Jšœ œœB˜Všœ'˜+šœ˜JšœL˜LJšœ˜Jšœ˜Jšœ˜Jšœ˜JšœH˜HJšœB˜BJšœ"˜)——J˜—Jšœ œœœœ'œœC˜‘J˜—J˜J˜—šžœœ œœ˜$Jšœœ'œœC˜{J˜J˜—šÏbœ ˜ Jšœ&™&Jšœœœ˜,Jš œœœœœ˜&Jšœœ œ˜,J˜J˜—šžœœ œœ œœ œ˜PJšœ<˜<šœ˜Jšœœ=˜GJšœ œ˜*Jšœ@˜@Jšœ˜—Jšœ ˜J˜J˜—šž œœœœ˜4Jšœ˜—J˜Jšž œœœœœœœ˜GJ˜Jšœ™šž œœ˜Jšœ+™+Jšœ œœ˜Jšœ œœ˜Jšœœ˜Jšœ œ˜Jšœ œ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜šœœ˜Jšœ#˜#Jšœ˜J˜J˜J˜—šœ˜Jšœ œ˜Jšœ˜Jšœ˜J˜—˜ J˜—Jšœ™š œœœœœ˜4šœ œœ'˜=š œœœ'œœ˜EJšœ#œ˜HJšœ˜——Jšœ˜—J˜J˜—šž œœ˜Jšœ œœ˜Jšœœœœ˜Jšœœ˜#J˜Jšœ/˜/J˜J˜J˜ J˜J˜ š œœœœœ˜4šœ+œ˜4Jšœ˜Jšœœ˜J˜—J˜ Jšœ˜—šœœ œ˜Jšœ œœ œ ˜)Jšœ œ ˜Jšœ˜—J˜J˜—šžœœ˜(Jšœ6™6šœ˜šœœ˜$Jšœ˜Jšœ#˜#J˜—šœœœ˜(Jšœ˜Jšœ"˜"J˜—šœœœ˜*Jšœ˜Jšœ$˜$J˜—šœœœ˜(Jšœ˜Jšœ˜J˜!J˜—Jšœv˜zJ˜JšœÏc˜—J˜J˜—šž œœ˜&Jšœœ ˜Jšœ œœœ˜šžœœ˜Jšœœœœ˜-Jšœœœ œ˜3J˜—šœ˜Jšœœ ˜Jšœ˜J˜J˜Jšœ ˜ J˜Jšœœ˜5šœ˜Jšœœ@˜TJšœœ ˜J˜—J˜J˜ J˜ Jšœ˜—J˜J˜ J˜J˜—šžœœ˜(Jšœ2™2Jšœ ˜ šœ˜šœœ˜ Jšœ˜Jšœ˜J˜—šœœœ˜%Jšœ˜J˜Jšœœœ&˜TJšœœœ%˜BJšœV˜ZJ˜—JšœR˜VJ˜Jšœ ˜—J˜J˜—š ž œœœœœ ˜/Jšœ6™6Jšœœ ˜Jšœ œœœ˜šžœœ˜Jšœœœœ˜-Jšœœœ œ˜3J˜—Jšœ ˜ šœ˜Jšœœ ˜ Jšœ ˜ Jšœ˜J˜ J˜Jšœ!˜!J˜ J˜J˜Jšœ˜—J˜ Jšœ˜ J˜J˜—šžœœœœ˜Pšœœ˜Jšœœœ˜%Jšœœœœ˜4Jšœœœ˜:Jšœœœ˜>Jšœœœ˜™>Jšœ˜šœ˜šœœ˜!Jšœ˜Jšœ˜Jšœ$˜$J˜—šœœœ˜(Jšœ˜Jšœ˜Jšœ8˜8J˜—šœœœ˜*Jšœ˜Jšœ˜Jšœ:˜:J˜—šœœœ˜$Jšœ˜Jšœ˜Jšœ4˜4J˜—šœœœ˜+Jšœ˜J˜Jšœœ)˜CJšœ/˜3J˜—Jšœ{˜J˜Jšœ ˜—J˜J˜—šžœœœœœœœ˜6JšœC™CJšœœœœ˜Jšœ œœœ˜šžœœ˜Jšœœœœ˜-Jšœœœ œ˜3J˜—šœœœ˜4šœ˜Jšœœœœ˜>Jšœœœœ˜=Jšœ'œ˜1Jšœ'œ˜1JšœE˜L—J˜Jšœ˜—Jšœ˜ J˜J˜—šžœœœœ˜@šœ˜Jšœœœ,˜aJšœ˜—J˜J˜—šž œœœ'˜EJšœ œœ˜AJšœ  R˜_J˜J˜—šžœœœœ˜:šœ˜Jšœœœ,˜aJšœ˜—J˜J˜—šžœœœœ˜,JšœA™AJšœ œ˜Jšœ œ˜J˜J˜J˜J˜J˜Jšœ(˜.J˜J˜—š žœœœœœ˜-JšœC™CJšœœ˜Jšœœ˜Jšœœœ˜J˜J˜ Jšœ#˜#Jšœ  S˜\J˜Jšœ œ œ(˜9Jšœ(˜/Jšœ˜J˜J˜J˜—šž œœœ œ˜1Jšœ˜Jšœœœœ˜(Jš œœœœœ˜/JšœS˜WJ˜J˜—Jšœ˜—…—0REQ