DIRECTORY AMBridge, CedarScanner, FileIO, Interpreter, IO, Menus, Rope, RTBasic, ViewerClasses; MenuParserImpl: CEDAR PROGRAM IMPORTS AMBridge, CedarScanner, FileIO, Interpreter, IO, Rope EXPORTS Menus = BEGIN OPEN CedarScanner; t: Token; menus: LIST OF Menus.Menu _ NIL; globalRope: Rope.ROPE; errorLog: Rope.ROPE; syntaxErrorFound: SIGNAL; ParseDescription: PUBLIC PROC [def: Rope.ROPE, prevlist: LIST OF Menus.Menu _ NIL, errorFile: Rope.ROPE _ NIL] RETURNS [LIST OF Menus.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.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.ROPE] RETURNS [BOOLEAN] = { RETURN[Rope.Equal[r, CurrentContents[], FALSE] ]; }; CurrentContents: PROC [] RETURNS [Rope.ROPE] = { IF t.kind = tokenROPE THEN { dummy: Rope.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.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.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.ROPE] RETURNS[answer: LIST OF Rope.ROPE] = { myToken: Token; answer _ NIL; myToken _ GetToken[[GetForNamesFromRope, string], 0]; WHILE myToken.kind = tokenID DO answer _ CONS[ContentsFromToken[[GetForNamesFromRope, string], myToken], answer]; myToken _ GetToken[[GetForNamesFromRope, string], myToken.next]; ENDLOOP; RETURN[answer]; }; ParseRelabel: PROC [] = { firstParm: Rope.ROPE _ NIL; secondParm: Rope.ROPE _ NIL; menuname: Rope.ROPE _ NIL; oldname: Rope.ROPE; newname: Rope.ROPE; GetNext[tokenSINGLE, "["]; GetNext[tokenID]; firstParm _ CurrentContents[]; GetNext[tokenID]; secondParm _ CurrentContents[]; GetNext; IF t.kind = tokenID THEN { menuname _ firstParm; oldname _ secondParm; newname _ CurrentContents[]; GetNext[tokenSINGLE, "]"]; } ELSE { menuname _ NIL; oldname _ firstParm; newname _ secondParm; }; MustBe["]"]; 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.displayData _ 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.rest _ LIST[newMenu^]; }; }; ParseMenuParms: PROC [newMenu: REF Menus.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: REF Menus.Menu] = { newEntry: REF Menus.Entry; WHILE ContentsAre["["] DO newEntry _ NEW[Menus.Entry]; GetNext[tokenID]; newEntry.name _ CurrentContents[]; ParseEntryParms[newEntry]; MustBe["=>"]; GetNext; IF ContentsAre["["] THEN newEntry.actions _ GetComplex[] ELSE { newEntry.actions _ LIST[ Menus.EntryNotifyRecord[trigger: LIST[all], notifyData: GetListOfRefAny[]] ]; }; 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^]; }; MustBe["]"]; GetNext[]; ENDLOOP; MustBe["]"]; }; ParseEntryParms: PROC [newEntry: REF Menus.Entry] = { GetNext[]; 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 OR t.kind = tokenID THEN newEntry.displayData_CurrentContents[] ELSE IF ContentsAre["{"] THEN newEntry.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[answer: LIST OF Menus.EntryNotifyRecord] = { newEntryNotifyRec: REF Menus.EntryNotifyRecord; answer _ NIL; MustBe["["]; WHILE ContentsAre["["] DO newEntryNotifyRec _ NEW[Menus.EntryNotifyRecord]; newEntryNotifyRec.trigger _ GetTriggers[]; ParseTriggerParms[newEntryNotifyRec]; MustBe["=>"]; GetNext; newEntryNotifyRec.notifyData _ GetListOfRefAny[]; MustBe["]"]; 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; MustBe["]"]; RETURN[answer]; }; GetTriggers: PROC [] RETURNS [l: LIST OF Menus.MenuEntryTrigger] = { r: Rope.ROPE; l _ NIL; GetNext; IF ContentsAre["["] THEN { GetNext[tokenID]; WHILE t.kind = tokenID DO r _ CurrentContents[]; SELECT TRUE FROM Rope.Equal["notrigger", r, FALSE] => NULL; Rope.Equal["all", r, FALSE] => l _ CONS[all, l]; Rope.Equal["leftup", r, FALSE] => l _ CONS[leftup, l]; Rope.Equal["middleup", r, FALSE] => l _ CONS[middleup, l]; Rope.Equal["rightup", r, FALSE] => l _ CONS[rightup, l]; Rope.Equal["shiftleftup", r, FALSE] => l _ CONS[shiftleftup, l]; Rope.Equal["shiftmiddleup", r, FALSE] => l _ CONS[shiftmiddleup, l]; Rope.Equal["shiftrightup", r, FALSE] => l _ CONS[shiftrightup, l]; Rope.Equal["allnonshifts", r, FALSE] => { l _ CONS[leftup, l]; l _ CONS[middleup, l]; l _ CONS[rightup, l] }; Rope.Equal["allshifts", r, FALSE] => { l _ CONS[shiftleftup, l]; l _ CONS[shiftmiddleup, l]; l _ CONS[shiftrightup, l] }; Rope.Equal["allleft", r, FALSE] => { l _ CONS[leftup, l]; l _ CONS[shiftleftup, l]; }; Rope.Equal["allmiddle", r, FALSE] => { l _ CONS[middleup, l]; l _ CONS[shiftmiddleup, l]; }; Rope.Equal["allright", r, FALSE] => { l _ CONS[rightup, l]; l _ CONS[shiftrightup, l]; }; ENDCASE => SyntaxError["Was expecting a trigger keyword ('all', 'leftup', 'leftshiftup', etc)"]; GetNext; ENDLOOP; MustBe["]"]; } ELSE IF t.kind = tokenID THEN { r _ CurrentContents[]; SELECT TRUE FROM Rope.Equal["notrigger", r, FALSE] => NULL; Rope.Equal["all", r, FALSE] => l _ CONS[all, l]; Rope.Equal["leftup", r, FALSE] => l _ CONS[leftup, l]; Rope.Equal["middleup", r, FALSE] => l _ CONS[middleup, l]; Rope.Equal["rightup", r, FALSE] => l _ CONS[rightup, l]; Rope.Equal["shiftleftup", r, FALSE] => l _ CONS[shiftleftup, l]; Rope.Equal["shiftmiddleup", r, FALSE] => l _ CONS[shiftmiddleup, l]; Rope.Equal["shiftrightup", r, FALSE] => l _ CONS[shiftrightup, l]; Rope.Equal["allnonshifts", r, FALSE] => { l _ CONS[leftup, l]; l _ CONS[middleup, l]; l _ CONS[rightup, l] }; Rope.Equal["allshifts", r, FALSE] => { l _ CONS[shiftleftup, l]; l _ CONS[shiftmiddleup, l]; l _ CONS[shiftrightup, l] }; Rope.Equal["allleft", r, FALSE] => { l _ CONS[leftup, l]; l _ CONS[shiftleftup, l]; }; Rope.Equal["allmiddle", r, FALSE] => { l _ CONS[middleup, l]; l _ CONS[shiftmiddleup, l]; }; Rope.Equal["allright", r, FALSE] => { l _ CONS[rightup, l]; l _ CONS[shiftrightup, l]; }; ENDCASE => SyntaxError["Was expecting a trigger keyword ('all', 'leftup', 'leftshiftup', etc)"]; } ELSE SyntaxError["Was Expecting a trigger keyword or list of them surrounded by brackets '[' (triggers are 'all', 'leftup', 'leftshiftup', etc)"]; }; TriggerFromRope: PROC [r: Rope.ROPE, l: LIST OF Menus.MenuEntryTrigger] = { }; ParseTriggerParms: PROC [newEntryNotifyRec: REF Menus.EntryNotifyRecord] = { 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["Was expecting a trigger parameter ('popUpDoc', 'makeActive', 'makeInActive', 'toggle', or 'guardResponse')"]; GetNext; ENDLOOP; -- while }; GetListOfRefAny: PROC [] RETURNS [answer: LIST OF REF ANY] = { r: LIST OF REF ANY _ NIL; -- the reversed list  order matters here answer _ NIL; WHILE NOT (ContentsAre["]"] OR ContentsAre["}"]) 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]; ENDCASE => SyntaxError["Was expecting a valid token for a LIST OF REF ANY"]; GetNext; ENDLOOP; 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] = { 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 RETURN[ NARROW[GeneralGetProc[], REF ViewerClasses.NotifyProc]^ ]; }; GetDisplayProc: PROC [] RETURNS [REF Menus.DrawingRec] = { RETURN[ NEW [Menus.DrawingRec _ [NARROW[GeneralGetProc[], REF Menus.DrawingProc] ^, GetListOfRefAny[] ] ] ]; }; GetQualifiedName: PROC [] RETURNS [Rope.ROPE] = { firstPart: Rope.ROPE; secondPart: Rope.ROPE; GetNext[tokenID]; firstPart _ CurrentContents[]; GetNext[tokenSINGLE, "."]; GetNext[tokenID]; secondPart _ CurrentContents[]; RETURN[Rope.Cat[firstPart, ".", secondPart] ]; }; GeneralGetProc: PROC [] RETURNS [REF ANY] = { qualifiedName: Rope.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. $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 the menuname is nil now affect the data structure assumes first token already read for it by the caller. add entry to the menu, at the end of the list: assumes first token NOT read for it by the caller. assumes first token already read for it by the caller. add newEntryNotifyRec to answer, at the end of the list: 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. order for this list doesn't matter, so I can just CONS it onto the front the Trigger(s) the rope indicates are CONSed onto the front of the list Does NOT assume first token already read for it by the caller. reads until it hits a ']'  assumes caller has read the first token now build the list we really want to return, which has the proper order: 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 ʯ˜šÏk ˜ J˜ J˜ J˜J˜ Jšœ˜J˜Jšœ˜J˜J˜J˜—Jšœœ˜J˜Jšœ.œœ˜MJ˜Jšœœ˜J˜Jšœ ™ J˜ Jšœœœœ˜ Jšœœ˜Jšœœ˜Jšœœ˜J˜Jšœ™šÏnœœœ œ œœœœœœœœ˜Jšœœ˜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˜—š ž œœ œœœ˜6Jšœ"œ˜1J˜J˜—šžœœœœ˜0Jšœ_™_šœœ˜Jšœ œœ˜3Jšœ1˜7J˜—Jšœœœ˜.J˜J˜—š žœœ+œœ œœ˜\Jš œœœœœ ˜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šœ œ˜ Jšœ5˜5šœ˜Jšœ œD˜QJšœ@˜@Jšœ˜—Jšœ ˜J˜J˜—J™Jšœ™šž œœ˜Jšœ+™+Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœœ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜šœœ˜Jšœ˜Jšœ˜J˜J˜J˜—šœ˜Jšœ™Jšœ œ˜Jšœ˜Jšœ˜J˜—˜ J˜—Jšœ™š œœœœœ˜:šœ œœ$˜<š œœœ'œœ˜EJšœ#œ˜HJšœ˜——Jšœ˜—J˜J˜—šž œœ˜Jšœ œœ˜Jšœœœœ˜Jšœ œœ ˜*J˜Jšœ!˜!J˜J˜J˜ J˜J˜ š œœœœœ˜:šœ(œ˜1Jšœ˜Jšœœ˜J˜—J˜ Jšœ˜—šœœ œ˜Jšœ œœ œ ˜*Jšœ œ ˜ Jšœ˜—J˜—šžœœ œ˜2Jšœ6™6šœ˜šœœ˜$Jšœ˜Jšœ#˜#J˜—šœœœ˜(Jšœ˜Jšœ"˜"J˜—šœœœ˜*Jšœ˜Jšœ$˜$J˜—šœœœ˜(Jšœ˜Jšœ˜J˜!J˜—Jšœv˜zJ˜JšœÏc˜—J˜J˜J˜—šž œœ œ˜0Jšœ œ ˜šœ˜Jšœ œ˜Jšœ˜J˜"J˜Jšœ ˜ J˜Jšœœ ˜8šœ˜Jšœœ#œ(˜fJ˜—Jšœ.™.Jšœœœœ ˜?šœ˜Jšœœœœ˜ š œœœ'œœ˜EJ˜ Jšœ˜—Jšœ œ ˜J˜—J˜ J˜ Jšœ˜—J˜ J˜J˜—šžœœ œ˜5Jšœ2™2Jšœ ˜ šœ˜šœœ˜ Jšœ˜Jšœ ˜ J˜—šœœœ˜%Jšœ˜J˜Jšœœœ'˜UJšœœœ(˜EJšœV˜ZJ˜—JšœR˜VJ˜Jšœ ˜—J˜J˜—š ž œœœ œœ˜HJšœœ˜/Jšœ6™6Jšœ œ˜ Jšœ ˜ šœ˜Jšœœ˜1Jšœ*˜*Jšœ%˜%J˜ J˜Jšœ1˜1J˜ Jšœ8™8Jšœ œœ œ˜6šœ˜Jšœœœœ˜,š œœœ*œœ˜HJ˜ Jšœ˜—Jšœ œ˜&J˜—J˜Jšœ˜—J˜ Jšœ ˜J˜J˜—š ž œœœœœ˜DJšœœ™œJšœœ˜ Jšœœ˜J˜šœœ˜J˜šœ˜JšœH™HJ˜šœœ˜Jšœœœ˜*Jšœœ œ ˜0Jšœœ œ ˜6Jšœœ œ˜:Jšœœ œ ˜8Jšœœ œ˜@Jšœœ œ˜DJšœœ œ˜Bšœœ˜)Jšœœ ˜Jšœœ˜Jšœœ ˜Jšœ˜—šœœ˜&Jšœœ˜Jšœœ˜Jšœœ˜Jšœ˜—šœœ˜$Jšœœ ˜Jšœœ˜Jšœ˜—šœœ˜&Jšœœ˜Jšœœ˜Jšœ˜—šœœ˜%Jšœœ ˜Jšœœ˜Jšœ˜—JšœY˜`—J˜Jšœ˜—J˜ J˜—šœœœ˜J˜šœœ˜Jšœœœ˜*Jšœœ œ ˜0Jšœœ œ ˜6Jšœœ œ˜:Jšœœ œ ˜8Jšœœ œ˜@Jšœœ œ˜DJšœœ œ˜Bšœœ˜)Jšœœ ˜Jšœœ˜Jšœœ ˜Jšœ˜—šœœ˜&Jšœœ˜Jšœœ˜Jšœœ˜Jšœ˜—šœœ˜$Jšœœ ˜Jšœœ˜Jšœ˜—šœœ˜&Jšœœ˜Jšœœ˜Jšœ˜—šœœ˜%Jšœœ ˜Jšœœ˜Jšœ˜—JšœY˜`—J˜—JšœŽ˜’J˜J˜—š žœœ œœœ˜KJšœG™GJ˜J˜—šžœœœ˜LJšœ>™>Jšœ˜šœ˜šœœ˜!Jšœ˜Jšœ˜Jšœ/˜/J˜—šœœœ˜(Jšœ˜Jšœ˜JšœC˜CJ˜—šœœœ˜*Jšœ˜Jšœ˜JšœE˜EJ˜—šœœœ˜$Jšœ˜Jšœ˜Jšœ?˜?J˜—šœœœ˜+Jšœ˜J˜Jšœœ4˜NJšœ:˜>J˜—Jšœ{˜J˜Jšœ ˜—J˜J˜—šžœœœ œœœœ˜>JšœC™CJš œœœœœœ )˜CJšœ œ˜ šœœœ˜4šœ˜Jš œœœœœ ˜CJš œœœœœ ˜BJšœœœ  ˜OJš œœœœœ ˜BJšœE˜L—J˜Jšœ˜—JšœH™Hšœœœœœ œœ˜3Jšœ œ˜Jšœ˜—J˜J˜—šžœœœœ˜@šœ˜Jšœœœ,˜aJšœ˜—J˜J˜—šž œœœ'˜EJšœ œœ˜AJšœ  R˜_Jšœœœ˜BJ˜J˜—šžœœœœ˜:šœ˜Jšœœœ,˜aJšœ˜—J˜J˜—šžœœœœ˜1Jšœ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˜J˜J˜J˜J˜J˜J˜Jšœ˜—…—4ÆL™