DIRECTORY Atom, BackStop, Basics, DefDWIM, DocCache, FileDWIM, FS, IO, JaMAndStyleAnalyses, Menus, MesaAnalyses, RefTab, RegularExpression, Rope, RopeHash, RopeList, RopeReader, SimpleFeedback, SymTab, TDJaMScanner, TDLexing, TEditDocument, TEditDocumentPrivate, TEditInput, TEditOps, TEditProfile, TEditRefresh, TEditSelection, TEditSelectionOps, TextFind, TextNode, TiogaFind, TiogaMenuOps, TiogaOps, TiogaOpsDefs, TiogaRopes, TiogaStreams, UserProfile, ViewerClasses, ViewerForkers, VM; DefDWIMImpl: CEDAR PROGRAM IMPORTS Atom, BackStop, DocCache, FileDWIM, FS, IO, JaMAndStyleAnalyses, MesaAnalyses, RefTab, RegularExpression, Rope, RopeHash, RopeList, RopeReader, SimpleFeedback, SymTab, TDJaMScanner, TDLexing, TEditDocumentPrivate, TEditInput, TEditOps, TEditProfile, TEditRefresh, TEditSelection, TextFind, TextNode, TiogaFind, TiogaMenuOps, TiogaOps, TiogaRopes, TiogaStreams, UserProfile, ViewerForkers, VM EXPORTS DefDWIM SHARES ViewerClasses --in order to change existing Def button, which doesn't decode control = BEGIN OPEN TDLexing, DefDWIM; LORA: TYPE = LIST OF REF ANY; ROPEList: TYPE = LIST OF ROPE; RefTextNode: TYPE = TextNode.RefTextNode; MesaAnalysis: TYPE = MesaAnalyses.MesaAnalysis; Viewer: TYPE = ViewerClasses.Viewer; RegistrationList: TYPE = LIST OF Registration; Registration: TYPE = RECORD [Test: LanguageTest, Find: Finder]; Failure: PUBLIC ERROR [where: Place, why: ROPE] = CODE; registry: RegistrationList ¬ NIL; FindDef: PUBLIC PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { FOR rl: RegistrationList ¬ registry, rl.rest WHILE rl # NIL DO IF rl.first.Test[from] THEN RETURN [rl.first.Find[subjectRope, from, deep, verbose, interrupt]]; ENDLOOP; RETURN [DefaultFinder[subjectRope, from, deep, verbose, interrupt]]; }; Register: PUBLIC PROC [languageTest: LanguageTest, finder: Finder] = { registry ¬ CONS[[languageTest, finder], registry]; }; Msg: PROC [msgType: SimpleFeedback.MsgType, r1, r2, r3, r4, r5: ROPE ¬ NIL] = { msg: ROPE = Rope.Cat[r1, r2, r3, r4, r5]; SimpleFeedback.Append[$DefDWIM, msgType, $FYI, msg]; }; DefaultFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { quick: TextFind.Target ~ TextFind.TargetFromRope[rope: Rope.Concat[subjectRope, ":"], pattern: FALSE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; start: INT = 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT ¬ 0; foundLoc, nameEnd: Location; foundNode: TextNode.Ref ¬ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: forward, target: quick, loc1: fromLoc, loc2: TextNode.LastLocWithin[textRoot]--short by one, but probably won't matter--, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; TryBkwd: PROC RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: backward, target: quick, loc1: startLoc, loc2: from.loc, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; found: BOOL ¬ SELECT from.searchOrder FROM fromStart => TryFwd[[textRoot, 0]], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF NOT found THEN { dot: INT ~ subjectRope.FindBackward["."]; subjLen: INT ~ subjectRope.Length; IF dot IN (0 .. subjLen-1) THEN { module: ROPE ~ subjectRope.Substr[len: dot]; sub: ROPE ~ subjectRope.Substr[start: dot+1]; next: Place ~ EnterCedarModule[from, module, verbose]; fp ¬ FindCedarDef[sub, next, deep, verbose, FALSE, NIL, MakeStack[], interrupt].cfr.fp; RETURN}; }; IF found THEN { foundLoc ¬ TextNode.LocRelative[[foundNode, 0], before]; nameEnd ¬ TextNode.LocRelative[foundLoc, after-2 - before]; after ¬ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; fp ¬ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE Failure[from, Rope.Concat["Couldn't find ", subjectRope]]; }; RecognizeJS: PROC [place: Place] RETURNS [is: BOOL] --LanguageTest-- = { is ¬ WhatJSType[place] # Other; }; JSType: TYPE = {JaM, Style, Other}; WhatJSType: PROC [place: Place] RETURNS [type: JSType] = { cp: FS.ComponentPositions; fullFName, ext: ROPE; IF place.fileName.Length[] = 0 THEN RETURN [Other]; [fullFName, cp, ] ¬ FS.ExpandName[place.fileName]; ext ¬ fullFName.Substr[cp.ext.start, cp.ext.length]; type ¬ SELECT TRUE FROM ext.Equal["JaM", FALSE] => JaM, ext.Equal["Style", FALSE] => Style, ENDCASE => Other; }; JSFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { type: JSType = WhatJSType[from]; IF deep THEN subjectRope ¬ Rope.Concat["look.", subjectRope]; fp ¬ FindJSDef[subjectRope, from, verbose, NIL, MakeStack[], interrupt]; }; FindJSDef: PROC [subjectRope: ROPE, from: Place, verbose: BOOL, searchedOpens: SymTab.Ref--file name ¬ $T--, stack: RefTab.Ref--Frame ¬ $T--, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { frame: Frame = NEW [FramePrivate ¬ [subjectRope, from]]; IF stack.Fetch[frame].found THEN { Failure[from, Rope.Concat["Recursion detected for ", subjectRope]]; }; IF NOT stack.Insert[frame, $T] THEN ERROR; IF verbose THEN Msg[oneLiner, "Looking for ", subjectRope, " in ", from.fileName]; {ENABLE UNWIND => [] ¬ stack.Delete[frame]; quick: RegularExpression.Finder = RegularExpression.CreateFromRope[pattern: Rope.Cat["(", subjectRope, ")"], literal: TRUE, word: TRUE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; anal: JaMAndStyleAnalyses.JSAnalysis = JaMAndStyleAnalyses.GetAnalysis[textRoot, from.fileName, verbose]; start: INT = IF anal # NIL THEN anal.bodyStart ELSE 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; here: INT = TextNode.LocOffset[[textRoot, 0], from.loc]; before, after: INT ¬ 0; foundLoc, nameEnd: Location; TryFwd: PROC [searchFrom: INT] RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; found ¬ FALSE; WHILE NOT found DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRope[finder: quick, rope: searchRope, start: searchFrom, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT JaMOKAround[searchRope, subjectRope, before, after] THEN {searchFrom ¬ after; found ¬ FALSE}; ENDLOOP; }; TryBkwd: PROC RETURNS [found: BOOL] = { searchRope: ROPE = MF[TiogaRopes.RopeFromTioga[textRoot]]; searchLength: INT ¬ here - start; found ¬ FALSE; WHILE NOT found DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRopeBackwards[finder: quick, rope: searchRope, start: start, len: searchLength, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT JaMOKAround[searchRope, subjectRope, before, after] THEN {searchLength ¬ before - start; found ¬ FALSE}; ENDLOOP; }; found: BOOL ¬ SELECT from.searchOrder FROM fromStart => TryFwd[0], fwdThenBkwd => TryFwd[here] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[here], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[textRoot, 0], before+1]; nameEnd ¬ TextNode.LocRelative[foundLoc, after-2 - (before+1)]; after ¬ TextNode.LocOffset[[textRoot, 0], nameEnd]+1; fp ¬ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE { Try: PROC [files: ROPEList, ext: ROPE] RETURNS [found: BOOL] = { FOR files ¬ files, files.rest WHILE files # NIL DO fileName: ROPE = files.first.Concat[ext]; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF searchedOpens = NIL THEN searchedOpens ¬ SymTab.Create[case: TRUE]; found ¬ TRUE; IF searchedOpens.Fetch[fileName].found THEN found ¬ FALSE ELSE { IF NOT searchedOpens.Insert[fileName, $T] THEN ERROR; fp ¬ FindJSDef[subjectRope, JSModuleBody[from, fileName, verbose, stack, interrupt].where, verbose, searchedOpens, stack, interrupt !Failure => {found ¬ FALSE; CONTINUE}]; }; IF found THEN RETURN; ENDLOOP; found ¬ FALSE; }; IF anal # NIL AND (Try[anal.runs, ".JaM"] OR Try[anal.attachs, ".Style"]) THEN NULL ELSE Failure[from, Rope.Concat["Couldn't find ", subjectRope]]; }; }; IF NOT stack.Delete[frame] THEN ERROR; }; JSModuleBody: PROC [from: Place, filename: ROPE, verbose: BOOL, stack: RefTab.Ref--Frame ¬ $T--, interrupt: REF BOOL] RETURNS [fp: FoundPlace] = { ans: FileDWIM.Answer = FileDWIM.ResolveHint[filename, from.fileName, FALSE]; IF ans.fullFileName = NIL THEN ERROR Failure[from, Rope.Concat["couldn't find file ", filename]]; {next: Place = [ans.fullFileName, [GetDoc[from, ans.fullFileName], 0], fromStart]; fp ¬ [next, next.loc]; }}; JaMOKAround: PROC [searchRope, target: ROPE, before, after: INT] RETURNS [ok: BOOL] = { Token: TYPE = TDJaMScanner.Token; reader: RopeReader.Ref = RopeReader.Create[]; Match: PROC [left: {leftSquare, leftCurly}] RETURNS [ok: BOOL] = { DO token: Token = TDJaMScanner.GetToken[reader]; SELECT token.type FROM nil => RETURN [FALSE--because EOF--]; name => { a: ATOM = NARROW[TDJaMScanner.ParseToken[token, searchRope]]; SELECT a FROM leftSquare => IF NOT Match[leftSquare] THEN RETURN [FALSE]; rightSquare => RETURN [left = leftSquare]; ENDCASE => NULL; }; string, int, real => NULL; lbrace => IF NOT Match[leftCurly] THEN RETURN [FALSE]; rbrace => RETURN [left = leftCurly]; comment => NULL; ENDCASE => ERROR; ENDLOOP; }; stack: LORA ¬ LIST[target]; IF after >= searchRope.Length[] THEN RETURN [FALSE]; reader.SetPosition[searchRope, after]; DO token: Token = TDJaMScanner.GetToken[reader]; SELECT token.type FROM nil => RETURN [FALSE--because EOF--]; name => { a: ATOM = NARROW[TDJaMScanner.ParseToken[token, searchRope]]; SELECT a FROM dotCvx, $bp => NULL; dotDef, $StyleParam => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest = NIL]; slashDef, $StyleRule, $ScreenRule, $PrintRule => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest # NIL AND stack.rest.rest.rest = NIL]; slashXdef => RETURN [stack # NIL AND stack.rest # NIL AND stack.rest.rest # NIL AND stack.rest.rest.rest = NIL]; leftSquare => IF Match[leftSquare] THEN stack ¬ CONS[$square, stack] ELSE RETURN [FALSE]; dotTrue => stack ¬ CONS[a, stack]; ENDCASE => RETURN [FALSE]; }; string, int, real => stack ¬ CONS[TDJaMScanner.ParseToken[token, searchRope], stack]; lbrace => IF Match[leftCurly] THEN stack ¬ CONS[$curly, stack] ELSE RETURN [FALSE]; rbrace => RETURN [FALSE]; comment => NULL; ENDCASE => ERROR; ENDLOOP; }; dotDef: ATOM = Atom.MakeAtom[".def"]; dotCvx: ATOM = Atom.MakeAtom[".cvx"]; dotTrue: ATOM = Atom.MakeAtom[".true"]; slashDef: ATOM = Atom.MakeAtom["/def"]; slashXdef: ATOM = Atom.MakeAtom["/xdef"]; leftSquare: ATOM = Atom.MakeAtom["["]; rightSquare: ATOM = Atom.MakeAtom["]"]; RecognizeLisp: PROC [place: Place] RETURNS [is: BOOL] = { textRoot: RefTextNode = TextNode.Root[place.loc.node]; asRope: ROPE = MF[TiogaRopes.RopeFromTioga[node: textRoot, skipCommentNode: TRUE]]; length: INT = asRope.Length[]; in: IO.STREAM = IO.RIS[asRope]; [] ¬ in.SkipWhitespace[]; IF in.EndOf[] THEN RETURN [FALSE]; {firstNonwhite: CHAR = in.GetChar[]; in.Close[]; IF firstNonwhite # '( THEN RETURN [FALSE]; FOR i: INT ¬ length-1, i-1 WHILE i > 0 DO SELECT asRope.Fetch[i] FROM IN ['\000 .. IO.SP] => NULL; ') => RETURN [TRUE]; ENDCASE => RETURN [FALSE]; ENDLOOP; RETURN [FALSE]; }}; LispFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { quick: TextFind.Target ¬ TextFind.TargetFromRope[rope: Rope.Concat["(defun ", subjectRope], pattern: FALSE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; start: INT = 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT ¬ 0; foundLoc, nameEnd: Location; foundNode: TextNode.Ref ¬ NIL; eod: Location ~ TextNode.LastLocWithin[textRoot]; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: forward, target: quick, loc1: from.loc, loc2: eod, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; TryBkwd: PROC RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: backward, target: quick, loc1: startLoc, loc2: from.loc, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; found: BOOL ¬ SELECT from.searchOrder FROM fromStart => TryFwd[startLoc], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[foundNode, 0], before+7]; nameEnd ¬ TextNode.LocRelative[foundLoc, after-1 - (before+7)]; after ¬ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; fp ¬ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE Failure[from, Rope.Concat["Couldn't find ", subjectRope]]; }; RecognizeM3: PROC [place: Place] RETURNS [BOOL] --LanguageTest-- = { cp: FS.ComponentPositions; fullFName, ext: ROPE; c0, c1: CHAR; IF place.fileName.Length[] = 0 THEN RETURN [FALSE]; [fullFName, cp, ] ¬ FS.ExpandName[place.fileName]; ext ¬ fullFName.Substr[cp.ext.start, cp.ext.length]; IF ext.Length[] # 2 THEN RETURN [FALSE]; c0 ¬ ext.Fetch[0]; IF c0#'i AND c0#'m THEN RETURN [FALSE]; c1 ¬ ext.Fetch[1]; IF c1#'3 AND c1#'g THEN RETURN [FALSE]; RETURN [TRUE]}; M3Finder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { dot: INT ~ subjectRope.FindBackward["."]; subjLen: INT ~ subjectRope.Length; IF dot IN (0 .. subjLen-1) THEN { module: ROPE ~ subjectRope.Substr[len: dot]; sub: ROPE ~ subjectRope.Substr[start: dot+1]; ans: FileDWIM.Answer = FileDWIM.ResolveHint[module, from.fileName]; IF ans.fullFileName#NIL THEN { root: TextNode.Ref ~ GetDoc[from, ans.fullFileName]; loc: Location ~ TextNode.LocRelative[[root, 0], 0]; RETURN M3Finder[sub, [ans.fullFileName, loc, fwdThenBkwd], deep, verbose, interrupt]}; ERROR Failure[from, IO.PutFR1["can't find a file for %g", [rope[subjectRope]] ]]} ELSE { target: ROPE ~ subjectRope; quick: TextFind.Target ~ TextFind.TargetFromRope[rope: Rope.Concat[subjectRope, ":"], pattern: FALSE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; start: INT = 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT ¬ 0; foundLoc, nameEnd: Location; foundNode: TextNode.Ref ¬ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: forward, target: quick, loc1: fromLoc, loc2: TextNode.LastLocWithin[textRoot]--short by one, but probably won't matter--, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; TryBkwd: PROC RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: backward, target: quick, loc1: startLoc, loc2: from.loc, match: word, interrupt: interrupt]; RETURN [foundNode#NIL]}; found: BOOL ¬ SELECT from.searchOrder FROM fromStart => TryFwd[[textRoot, 0]], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", subjectRope]]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[foundNode, 0], before]; nameEnd ¬ TextNode.LocRelative[foundLoc, after-2 - before]; after ¬ TextNode.LocOffset[[textRoot, 0], [foundNode, after]]; fp ¬ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE { xStart: INT ~ 0; xHere: INT ~ TextNode.LocOffset[[textRoot, 0], from.loc, 1, excludeComments]; targLen: INT = target.Length[]; vslow: RegularExpression.Finder = RegularExpression.CreateFromRope[Rope.Concat[target, "[ \n\t]**(',[ \n\t]**[a..zA..Z][a..zA..Z0..9_]**[ \n\t]**)**(':|=|'<':)"]]; pslow: RegularExpression.Finder = RegularExpression.CreateFromRope[Rope.Concat[target, "[ \n\t]**('(|';)" ]]; searchRope: ROPE ~ MF[TiogaRopes.RopeFromTioga[node: textRoot, skipCommentNode: excludeComments]]; TryFwd: PROC [slow: RegularExpression.Finder, searchFrom: INT] RETURNS [found: BOOL] = { DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRope[finder: slow, rope: searchRope, start: searchFrom, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT (IF slow=vslow THEN M3OKBefore ELSE ProcOrExnBefore)[searchRope, before, after] THEN {searchFrom ¬ after; found ¬ FALSE} ELSE RETURN [TRUE]; ENDLOOP; }; TryBkwd: PROC [slow: RegularExpression.Finder] RETURNS [found: BOOL] = { searchLength: INT ¬ xHere - xStart; DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRopeBackwards[finder: slow, rope: searchRope, start: xStart, len: searchLength, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT (IF slow=vslow THEN M3OKBefore ELSE ProcOrExnBefore)[searchRope, before, after] THEN {searchLength ¬ before - xStart; found ¬ FALSE} ELSE RETURN [TRUE]; ENDLOOP; }; IF verbose THEN Msg[begin, "Looking hard for ", target, " in ", from.fileName]; found ¬ SELECT from.searchOrder FROM fromStart => TryFwd[pslow, 0] OR TryFwd[vslow, 0], fwdThenBkwd => TryFwd[pslow, xHere] OR TryFwd[vslow, xHere] OR TryBkwd[pslow] OR TryBkwd[vslow], bkwdThenFwd => TryBkwd[pslow] OR TryBkwd[vslow] OR TryFwd[pslow, xHere] OR TryFwd[vslow, xHere], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF verbose THEN Msg[end, " ."]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[textRoot, 0], before, 1, excludeComments]; nameEnd ¬ TextNode.LocRelative[foundLoc, targLen-1]; fp ¬ [[from.fileName, foundLoc, fwdThenBkwd], nameEnd]; } ELSE Failure[from, Rope.Concat["Couldn't find ", subjectRope]]; }; }; }; ProcOrExnBefore: PROC [searchRope: ROPE, before, after: INT] RETURNS [ok: BOOL] = { x: INT ~ before; EatWhite: PROC RETURNS [i: INT] ~ { i ¬ before; WHILE i>0 DO SELECT searchRope.Fetch[i-1] FROM IN [0C .. ' ] => i ¬ i-1; ENDCASE => EXIT; ENDLOOP; RETURN}; GetId: PROC RETURNS [id: ROPE] ~ { end: INT ~ before; WHILE before>0 DO char: CHAR ~ searchRope.Fetch[before-1]; SELECT char FROM IN['a..'z], IN['A..'Z], IN['0..'9], '_ => before ¬ before-1; ENDCASE => EXIT; ENDLOOP; RETURN [searchRope.Substr[start: before, len: end-before]]}; EndsIn: PROC [e: ROPE] RETURNS [ok: BOOL] ~ { got: ROPE ~ GetId[]; RETURN [e.Equal[got]]}; got: ROPE; before ¬ EatWhite[]; IF before=x THEN RETURN [FALSE]; got ¬ GetId[]; RETURN [got.Equal["PROCEDURE"] OR got.Equal["EXCEPTION"]]}; M3OKBefore: PROC [searchRope: ROPE, before, after: INT] RETURNS [ok: BOOL] = { hard: BOOL ~ after>0 AND after0 DO SELECT searchRope.Fetch[i-1] FROM IN [0C .. ' ] => i ¬ i-1; ENDCASE => EXIT; ENDLOOP; RETURN}; GetId: PROC RETURNS [id: ROPE] ~ { end: INT ~ before; WHILE before>0 DO char: CHAR ~ searchRope.Fetch[before-1]; SELECT char FROM IN['a..'z], IN['A..'Z], IN['0..'9], '_ => before ¬ before-1; ENDCASE => EXIT; ENDLOOP; RETURN [searchRope.Substr[start: before, len: end-before]]}; EndsIn: PROC [e: ROPE] RETURNS [ok: BOOL] ~ { got: ROPE ~ GetId[]; RETURN [e.Equal[got]]}; IF NOT hard THEN { char: CHAR; IF before<=0 THEN RETURN [TRUE]; char ¬ searchRope.Fetch[before-1]; SELECT char FROM IN [0C .. ' ], ', => RETURN [TRUE]; ENDCASE => RETURN [FALSE]}; WHILE (before ¬ EatWhite[]) > 0 DO char: CHAR ¬ searchRope.Fetch[before ¬ before - 1]; SELECT state FROM seekComma => SELECT char FROM ', => state ¬ seekId; '( => state ¬ seekProcname; 'Y => RETURN EndsIn["READONL"]; 'E => RETURN EndsIn["VALU"]; 'R => RETURN EndsIn["VA"]; ENDCASE => RETURN [FALSE]; seekId => SELECT char FROM IN['a..'z], IN['A..'Z], IN['0..'9], '_ => {[] ¬ GetId[]; state ¬ seekId}; ENDCASE => RETURN [FALSE]; seekProcname => SELECT char FROM IN['a..'z], IN['A..'Z], IN['0..'9], '_ => { [] ¬ GetId[]; before ¬ EatWhite[]; RETURN EndsIn["PROCEDURE"]}; ENDCASE => RETURN [FALSE]; ENDCASE => ERROR; ENDLOOP; RETURN [FALSE]}; CedarTree: TYPE = REF ANY --actually UNION [FieldSelection, ROPE]--; FieldSelection: TYPE = REF FieldSelectionPrivate; FieldSelectionPrivate: TYPE = RECORD [ record: CedarTree, field: ROPE ]; Frame: TYPE = REF FramePrivate; FramePrivate: TYPE = RECORD [tree: CedarTree, from: Place]; guessDIRECTORY: BOOL ¬ TRUE; RecognizeCedar: PROC [place: Place] RETURNS [isCedar: BOOL] --LanguageTest-- = { cp: FS.ComponentPositions; fullFName, ext: ROPE; IF place.fileName.Length[] = 0 THEN RETURN [FALSE]; [fullFName, cp, ] ¬ FS.ExpandName[place.fileName]; ext ¬ fullFName.Substr[cp.ext.start, cp.ext.length]; isCedar ¬ ext.Equal["mesa", FALSE] OR ext.Equal["cedar", FALSE]; }; CedarFinder: PROC [subjectRope: ROPE, from: Place, deep, verbose: BOOL, interrupt: REF BOOL ¬ NIL] RETURNS [fp: FoundPlace] --Finder-- = { in: IO.STREAM = IO.RIS[subjectRope]; p: Lexer = MakeLexer[in, NIL, excludeComments]; {ENABLE IO.Error => IF stream = in THEN Failure[noplace, IF ec = SyntaxError THEN "selection not of form ID(\".\"ID)*" ELSE "IO.Error parsing selection"]; t1: Token = GetToken[p]; IF t1.kind # tokenID THEN Failure[noplace, "selection not of form ID(\".\"ID)*"]; {tree: CedarTree = GetTree[p, t1]; in.Close[]; IF p.tokenStack = NIL OR p.tokenStack.rest # NIL OR p.tokenStack.first.kind # tokenEOF THEN Failure[noplace, "selection not of form ID(\".\"ID)*"]; fp ¬ FindCedarDef[tree, from, deep, verbose, TRUE, NIL, MakeStack[], interrupt].cfr.fp; }}}; excludeComments: BOOL ¬ TRUE; CedarFindResult: TYPE ~ RECORD [ fp: FoundPlace, recTypeLexer: Lexer¬NIL --#NIL => positioned between ("RECORD" or "=>") and "["--, l0: Location--in fp's doc--, i0: INT--index at l0-- ]; fieldNameFollowers: ROPEList ~ LIST[":", ",", "=>", "("]; typeBinders: ROPEList ~ LIST["~", "="]; FindCedarDef: PROC [tree: CedarTree, from: Place, deep, verbose, leftmost: BOOL, searchedOpens: SymTab.Ref--module name ¬ $T--, stack: RefTab.Ref--Frame ¬ $T--, interrupt: REF BOOL ¬ NIL] RETURNS [cfr: CedarFindResult] = { frame: Frame = NEW [FramePrivate ¬ [tree, from]]; IF stack.Fetch[frame].found THEN { Failure[from, Rope.Concat["Recursion detected for ", FmtTree[tree]]]; }; IF NOT stack.Insert[frame, $T] THEN ERROR; {ENABLE UNWIND => [] ¬ stack.Delete[frame]; FinishDeep: PROC [p: Lexer, cfr: CedarFindResult] RETURNS [CedarFindResult] ~ { ENABLE IO.Error => IF stream=p.in THEN Failure[ cfr.fp.where, IF ec=SyntaxError THEN "Cedar Syntax Error" ELSE "IO.Error"]; toke: Token; IF cfr.recTypeLexer#NIL THEN ERROR; SkipMumble[p]; toke ¬ GetToken[p]; IF toke.kind = tokenID THEN { IF NOT ReservedWord[toke.rope] THEN { defTree: CedarTree = GetTree[p, toke]; IF (WITH defTree SELECT FROM rope: ROPE => NOT PredefinedType[rope], ENDCASE => TRUE) THEN RETURN FindCedarDef[defTree, cfr.fp.where, TRUE, verbose, TRUE, NIL, stack, interrupt]; } ELSE IF toke.rope.Equal["TYPE"] THEN { toke2: Token = GetToken[p]; IF toke2.kind = tokenSINGLE AND RopeList.Memb[list: typeBinders, r: toke2.rope] THEN { SkipMumble[p]; {toke3: Token = GetToken[p]; IF toke3.kind#tokenID THEN NULL ELSE IF NOT ReservedWord[toke3.rope] THEN { defTree: CedarTree = GetTree[p, toke3]; IF (WITH defTree SELECT FROM rope: ROPE => NOT PredefinedType[rope], ENDCASE => TRUE) THEN RETURN FindCedarDef[defTree, cfr.fp.where, deep, verbose, TRUE, NIL, stack, interrupt]; } ELSE IF toke3.rope.Equal["RECORD"] THEN { cfr.recTypeLexer ¬ p; RETURN [cfr]}; }}; }; }; ReturnToken[p, toke]; RETURN [cfr]}; WITH tree SELECT FROM fs: FieldSelection => { next: CedarFindResult = FindCedarDef[fs.record, from, TRUE, verbose, leftmost, searchedOpens, stack, interrupt]; nextCopy: CedarFindResult ~ next; l: Lexer ~ next.recTypeLexer; lCopy: Lexer ~ l; IF l#NIL THEN { state: {fieldName, fieldType} ¬ fieldName; toke: Token ¬ GetToken[l]; matched: BOOL ¬ FALSE; first: BOOL ¬ TRUE; ThisToken: PROC [rt: BOOL] ~ { nameBegin: TextNode.Location ~ TextNode.LocRelative[next.l0, toke.before-next.i0, 1, excludeComments]; nameEnd: TextNode.Location ~ TextNode.LocRelative[nameBegin, toke.after-1-toke.before, 1, excludeComments]; cfr ¬ [fp: [where: [next.fp.where.fileName, nameBegin, fwdThenBkwd], nameEnd: nameEnd], recTypeLexer: IF rt THEN l ELSE NIL, l0: nameEnd, i0: toke.after]; matched ¬ TRUE; RETURN}; IF NOT toke.rope.Equal["["] THEN GOTO Not; DO thisFirst: BOOL ~ first; bracketDepth: INT ¬ 0; first ¬ FALSE; toke ¬ GetToken[l]; IF state=fieldType THEN matched ¬ FALSE; IF toke.kind = tokenEOF THEN GOTO Not; IF state=fieldName AND toke.kind=tokenID AND toke.rope.Equal[fs.field] AND RopeList.Memb[fieldNameFollowers, PeekToken[l].rope] THEN ThisToken[TRUE]; IF state=fieldType OR thisFirst THEN {--with another layer of looping, we could handle 1,anonymous,element records in their complete generality. IF toke.kind=tokenID AND toke.rope.Equal["SELECT"] THEN { toke ¬ GetToken[l]; IF toke.kind=tokenID AND toke.rope.Equal[fs.field] THEN ThisToken[FALSE] ELSE { UNTIL toke.kind=tokenEOF OR toke.kind=tokenID AND toke.rope.Equal["FROM"] DO toke ¬ GetToken[l] ENDLOOP; state ¬ fieldName; LOOP} } ELSE cfr.recTypeLexer ¬ NIL; }; DO --skip to the first ',, ':, or "=>" outside balanced brackets SELECT TRUE FROM toke.kind=tokenSINGLE => SELECT toke.rope.Fetch[0] FROM '[, '( => bracketDepth ¬ bracketDepth + 1; '), '] => { bracketDepth ¬ bracketDepth - 1; IF bracketDepth<0 THEN GOTO Not}; ', => IF bracketDepth=0 THEN {state ¬ fieldName; EXIT}; ': => IF bracketDepth=0 THEN { IF state=fieldType OR matched AND cfr.recTypeLexer#NIL THEN GOTO Not; state ¬ fieldType; EXIT}; ENDCASE => NULL; toke.kind=tokenDOUBLE => SELECT TRUE FROM toke.rope.Equal["=>"] => IF bracketDepth=0 THEN { IF state=fieldType OR matched AND cfr.recTypeLexer=NIL THEN GOTO Not; state ¬ fieldType; EXIT}; ENDCASE => NULL; ENDCASE => NULL; toke ¬ GetToken[l]; IF toke.kind = tokenEOF THEN GOTO Not; ENDLOOP; IF state=fieldType AND matched THEN { IF deep AND cfr.recTypeLexer=NIL THEN cfr ¬ FinishDeep[l, cfr]; GOTO Dun}; ENDLOOP; EXITS Not => stack ¬ stack}; cfr ¬ FindCedarDef[fs.field, next.fp.where, deep, verbose, FALSE, NIL, stack, interrupt]; }; target: ROPE => { quick: TextFind.Target ¬ TextFind.TargetFromRope[rope: Rope.Concat[target, ":"], pattern: FALSE]; textRoot: RefTextNode = TextNode.Root[from.loc.node]; eod: Location ~ TextNode.LastLocWithin[textRoot]; T2RIndex: PROC [count: INT] RETURNS [x: INT] ~ {x ¬ IF excludeComments THEN TextNode.LocOffset[[textRoot, 0], TextNode.LocRelative[[textRoot, 0], count], 1, TRUE] ELSE count}; R2TIndex: PROC [count: INT] RETURNS [y: INT] ~ {y ¬ IF excludeComments THEN TextNode.LocOffset[[textRoot, 0], TextNode.LocRelative[[textRoot, 0], count, 1, TRUE]] ELSE count}; ma: MesaAnalysis = MesaAnalyses.GetAnalysis[textRoot, from.fileName, verbose]; start: INT = IF ma # NIL THEN ma.bodyStart ELSE 0; startLoc: Location = TextNode.LocRelative[[textRoot, 0], start]; before, after: INT ¬ 0; found: BOOL; foundLoc, nameEnd: Location; IF verbose THEN Msg[begin, "Looking for ", target, " in ", from.fileName]; {foundNode: TextNode.Ref ¬ NIL; TryFwd: PROC [fromLoc: Location] RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: forward, target: quick, loc1: from.loc, loc2: eod, match: word, checkComment: excludeComments, interrupt: interrupt]--returns a half-open interval--; RETURN [foundNode#NIL]}; TryBkwd: PROC RETURNS [found: BOOL] = { [node: foundNode, matchStart: before, matchEnd: after] ¬ TiogaFind.Search[direction: backward, target: quick, loc1: startLoc, loc2: from.loc, match: word, checkComment: excludeComments, interrupt: interrupt]--returns a half-open interval--; RETURN [foundNode#NIL]}; found ¬ SELECT from.searchOrder FROM fromStart => TryFwd[[textRoot, 0]], fwdThenBkwd => TryFwd[from.loc] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[from.loc], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF verbose THEN Msg[end, " ."]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[foundNode, 0], before]; nameEnd ¬ TextNode.LocRelative[foundLoc, after-2--one for colon, one to close-- - before]; after ¬ TextNode.LocOffset[[textRoot, 0], [foundNode, after], 1, excludeComments]; } }; IF (NOT found) AND ma # NIL THEN { def: REF ANY = ma.globalDefs.Fetch[target].val; IF def # NIL THEN { RightHere: PROC [ir: MesaAnalyses.InterfaceRecord] RETURNS [cfr: CedarFindResult] ~ { nameStart: TextNode.Location ~ TextNode.LocRelative[[textRoot, 0], ir.namePos.before]; nameEnd: TextNode.Location ~ TextNode.LocRelative[nameStart, ir.namePos.after-1 - ir.namePos.before]; cfr ¬ [ fp: [[from.fileName, nameStart, fwdThenBkwd], nameEnd], recTypeLexer: NIL, l0: nameEnd, i0: ir.namePos.after-1]; RETURN}; cfr ¬ WITH def SELECT FROM it: MesaAnalyses.InterfaceType => CedarModuleBody[from, it, verbose, stack, interrupt], ir: MesaAnalyses.InterfaceRecord => IF deep THEN CedarModuleBody[from, ir.type, verbose, stack, interrupt] ELSE RightHere[ir], ie: MesaAnalyses.InterfaceElement => FindCedarDef[ie.name, CedarModuleBody[from, ie.interface.type, verbose, stack, interrupt].cfr.fp.where, deep, verbose, FALSE, searchedOpens, stack, interrupt], ENDCASE => ERROR; GOTO Dun}; }; IF NOT found THEN { here: INT = TextNode.LocOffset[[textRoot, 0], from.loc]; targLen: INT = target.Length[]; slow: RegularExpression.Finder = RegularExpression.CreateFromRope[target.Concat["[ \n\t]**(',[ \n\t]**[a..zA..Z][a..zA..Z0..9]**[ \n\t]**)**('([~'(')]++')[ \n\t]**|)':"]]; xStart: INT ~ T2RIndex[start]; xHere: INT ~ T2RIndex[here]; searchRope: ROPE ~ MF[TiogaRopes.RopeFromTioga[node: textRoot, skipCommentNode: excludeComments]]; TryFwd: PROC [searchFrom: INT] RETURNS [found: BOOL] = { found ¬ FALSE; WHILE NOT found DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRope[finder: slow, rope: searchRope, start: searchFrom, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT CedarOKBefore[searchRope, before] THEN {searchFrom ¬ after; found ¬ FALSE}; ENDLOOP; }; TryBkwd: PROC RETURNS [found: BOOL] = { searchLength: INT ¬ xHere - xStart; found ¬ FALSE; WHILE NOT found DO [found: found, before: before, after: after] ¬ RegularExpression.SearchRopeBackwards[finder: slow, rope: searchRope, start: xStart, len: searchLength, interrupt: interrupt]; IF NOT found THEN EXIT; IF NOT CedarOKBefore[searchRope, before] THEN {searchLength ¬ before - xStart; found ¬ FALSE}; ENDLOOP; }; IF verbose THEN Msg[begin, "Looking hard for ", target, " in ", from.fileName]; found ¬ SELECT from.searchOrder FROM fromStart => TryFwd[0], fwdThenBkwd => TryFwd[xHere] OR TryBkwd[], bkwdThenFwd => TryBkwd[] OR TryFwd[xHere], ENDCASE => ERROR; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF verbose THEN Msg[end, " ."]; IF found THEN { foundLoc ¬ TextNode.LocRelative[[textRoot, 0], before, 1, excludeComments]; nameEnd ¬ TextNode.LocRelative[foundLoc, targLen-1]; }; }; IF (NOT found) AND ma # NIL THEN { FOR owus: MesaAnalyses.IRList ¬ ma.openedWithoutUsingList, owus.rest WHILE owus # NIL DO ir: MesaAnalyses.InterfaceRecord = owus.first; IF interrupt#NIL AND interrupt­ THEN Failure[from, Rope.Concat["interrupted search for ", target]]; IF searchedOpens = NIL THEN searchedOpens ¬ SymTab.Create[case: TRUE]; IF searchedOpens.Insert[ir.type.typeClass, $T] THEN { found ¬ TRUE; cfr ¬ FindCedarDef[target, CedarModuleBody[from, ir.type, verbose, stack, interrupt].cfr.fp.where, deep, verbose, FALSE, searchedOpens, stack, interrupt !Failure => {found ¬ FALSE; CONTINUE}]; IF found THEN GOTO Dun; }; ENDLOOP; }; IF (NOT found) AND guessDIRECTORY AND leftmost THEN { IF verbose THEN Msg[begin, "Guessing ", target, " missing from DIRECTORY in ", from.fileName]; {ans: FileDWIM.Answer = FileDWIM.ResolveHint[target.Concat[".mesa"], from.fileName, FALSE]; IF ans.fullFileName # NIL THEN { next: Place = [ans.fullFileName, [GetDoc[from, ans.fullFileName], 0], fromStart]; found ¬ TRUE; cfr ¬ FindCedarDef[target, next, deep, verbose, FALSE, searchedOpens, stack, interrupt !Failure => {found ¬ FALSE; CONTINUE}]; IF found THEN GOTO Dun; }; IF verbose THEN Msg[end, " ."]; }}; IF NOT found THEN ERROR Failure[from, Rope.Concat["couldn't find ", target]]; cfr ¬ [fp: [[from.fileName, foundLoc, fwdThenBkwd], nameEnd], recTypeLexer: NIL, l0: TextNode.LocRelative[[textRoot, 0], after, 1, excludeComments], i0: after]; IF NOT deep THEN GOTO Dun; {in: IO.STREAM = TiogaStreams.CreateInput[ from: textRoot, commentHandling: IF excludeComments THEN [FALSE[]] ELSE [TRUE[NIL, NIL]] ]; in.SetIndex[after]; {ENABLE IO.Error => IF stream=in THEN Failure[cfr.fp.where, "IO.Error"]; p: Lexer = MakeLexer[in, NIL, excludeComments]; cfr ¬ FinishDeep[p, cfr]; }}}; ENDCASE => ERROR; EXITS Dun => cfr ¬ cfr; }; IF NOT stack.Delete[frame] THEN ERROR; }; CedarOKBefore: PROC [searchRope: ROPE, before: INT] RETURNS [ok: BOOL] = { state: {start, gotWhite, gotN, wantG, wantE, wantB, wantP, wantO, dontWantLetter} ¬ start; WHILE before > 0 DO char: CHAR = searchRope.Fetch[before ¬ before - 1]; SELECT state FROM start => SELECT char FROM ' , '\n, '\t => state ¬ gotWhite; '[, '{, ';, ', => RETURN [TRUE]; ENDCASE => RETURN [FALSE]; gotWhite => SELECT char FROM ' , '\n, '\t => NULL; '[, '{, ';, ', => RETURN [TRUE]; 'N => state ¬ gotN; ENDCASE => RETURN [FALSE]; gotN => SELECT char FROM 'I => state ¬ wantG; 'E => state ¬ wantP; ENDCASE => RETURN [FALSE]; wantG => SELECT char FROM 'G => state ¬ wantE; ENDCASE => RETURN [FALSE]; wantE => SELECT char FROM 'E => state ¬ wantB; ENDCASE => RETURN [FALSE]; wantB => SELECT char FROM 'B => state ¬ dontWantLetter; ENDCASE => RETURN [FALSE]; wantP => SELECT char FROM 'P => state ¬ wantO; ENDCASE => RETURN [FALSE]; wantO => SELECT char FROM 'O => state ¬ dontWantLetter; ENDCASE => RETURN [FALSE]; dontWantLetter => SELECT char FROM IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => RETURN [FALSE]; ENDCASE => RETURN [TRUE]; ENDCASE => ERROR; ENDLOOP; RETURN [TRUE]}; SkipMumble: PROC [p: Lexer] = { DO SELECT TRUE FROM p.NextIs["PRIVATE"] => LOOP; p.NextIs["PUBLIC"] => LOOP; p.NextIs["REF"] => LOOP; p.NextIs["LONG"] => LOOP; p.NextIsSeq[LIST["POINTER", "TO"]] => LOOP; p.NextIsSeq[LIST["MACHINE", "DEPENDENT"]] => LOOP; p.NextIs["READONLY"] => LOOP; p.NextIs["BASE"] => LOOP; p.NextIs["ORDERED"] => LOOP; ENDCASE => NULL; EXIT; ENDLOOP; }; GetTree: PROC [p: Lexer, start: Token] RETURNS [tree: CedarTree] = { want: {dot, name} ¬ dot; tree ¬ start.rope; DO toke: Token = GetToken[p]; SELECT want FROM dot => { IF toke.rope.Equal["^"] OR toke.rope.Equal["­"] THEN LOOP ELSE IF toke.rope.Equal["."] THEN want ¬ name ELSE {ReturnToken[p, toke]; EXIT}; }; name => { IF toke.kind = tokenID AND NOT ReservedWord[toke.rope] THEN { tree ¬ NEW [FieldSelectionPrivate ¬ [tree, toke.rope]]; want ¬ dot; } ELSE {ReturnToken[p, toke]; EXIT}; }; ENDCASE => ERROR; ENDLOOP; }; EnterCedarModule: PROC [from: Place, moduleName: ROPE, verbose: BOOL] RETURNS [Place] = { ans: FileDWIM.Answer = FileDWIM.ResolveHint[moduleName.Concat[".mesa"], from.fileName, FALSE]; IF ans.fullFileName = NIL THEN ERROR Failure[from, Rope.Cat["couldn't find file ", moduleName, ".mesa"]]; RETURN [[ans.fullFileName, [GetDoc[from, ans.fullFileName], 0], fromStart]]}; CedarModuleBody: PROC [from: Place, it: MesaAnalyses.InterfaceType, verbose: BOOL, stack: RefTab.Ref--Frame ¬ $T--, interrupt: REF BOOL] RETURNS [cfr: CedarFindResult] = { ans: FileDWIM.Answer = FileDWIM.ResolveHint[it.fileHint, from.fileName, FALSE]; IF ans.fullFileName = NIL THEN ERROR Failure[from, Rope.Cat["couldn't find file ", it.fileHint, " for module ", it.name]]; {next: Place = [ans.fullFileName, [GetDoc[from, ans.fullFileName], 0], fromStart]; cfr ¬ FindCedarDef[it.typeClass, next, FALSE, verbose, FALSE, NIL, stack, interrupt]; }}; MF: PROC [rope: ROPE] RETURNS [fr: ROPE] = INLINE { fr ¬ IF teledebug THEN rope.Flatten[] ELSE rope}; teledebug: BOOL ¬ FALSE; MakeStack: PROC RETURNS [stack: RefTab.Ref--Frame ¬ $T--] = { stack ¬ RefTab.Create[hash: HashFrame, equal: CompareFrames]; }; HashFrame: PROC [key: REF ANY] RETURNS [hash: CARDINAL] --RefTab.HashProc-- = { f: Frame = NARROW[key]; hash ¬ HashTree[f.tree] + RopeHash.FromRope[f.from.fileName] + HashRef[f.from.loc.node] + HashInt[f.from.loc.where]; }; HashTree: PROC [t: CedarTree] RETURNS [hash: CARDINAL] = { WITH t SELECT FROM x: ROPE => hash ¬ RopeHash.FromRope[x]; x: FieldSelection => hash ¬ HashTree[x.record]*3 + RopeHash.FromRope[x.field]; ENDCASE => ERROR; }; HashRef: PROC [ref: REF ANY] RETURNS [hash: CARDINAL] = TRUSTED INLINE {hash ¬ HashInt[LOOPHOLE[ref]]}; HashInt: PROC [i: INT] RETURNS [hash: CARDINAL] = INLINE {ln: Basics.LongNumber = [li[i]]; hash ¬ ln.lo + ln.hi}; CompareFrames: PROC [key1, key2: REF ANY] RETURNS [equal: BOOL] = { f1: Frame = NARROW[key1]; f2: Frame = NARROW[key2]; equal ¬ CompareTrees[f1.tree, f2.tree] AND f1.from.fileName.Equal[f2.from.fileName, FALSE] AND f1.from.loc = f2.from.loc; }; CompareTrees: PROC [t1, t2: CedarTree] RETURNS [BOOL] = { RETURN [WITH t1 SELECT FROM x: ROPE => WITH t2 SELECT FROM y: ROPE => x.Equal[y], y: FieldSelection => FALSE, ENDCASE => ERROR, x: FieldSelection => WITH t2 SELECT FROM y: ROPE => FALSE, y: FieldSelection => CompareTrees[x.record, y.record] AND x.field.Equal[y.field], ENDCASE => ERROR, ENDCASE => ERROR]}; FmtTree: PROC [t: CedarTree] RETURNS [rope: ROPE] = { rope ¬ WITH t SELECT FROM x: ROPE => x, x: FieldSelection => FmtTree[x.record].Cat[".", x.field], ENDCASE => ERROR; }; GetDoc: PROC [from: Place, fileName: ROPE] RETURNS [doc: TextNode.Ref] ~ { IF fileName=NIL THEN Failure[from, "Trying to fetch NIL-named document"]; IF fileName.Length[]=0 THEN Failure[from, "Trying to fetch empty-named document"]; doc ¬ DocCache.GetDoc[fileName]; IF doc=NIL THEN Failure[from, IO.PutFR1["Bug: DocCache.GetCache[%g] returned NIL", [rope[fileName]]]]; }; cmdEnabled: BOOL ¬ TRUE; workVerbose: BOOL ¬ TRUE; DefButt: PROC [parent: REF ANY, clientData: REF ANY ¬ NIL, mouseButton: Menus.MouseButton ¬ red, shift, control: BOOL ¬ FALSE] --Menus.ClickProc-- = { TEditInput.InterpretAtom[NARROW[parent], defAtoms[mouseButton][control][shift]]; }; defAtoms: ARRAY Menus.MouseButton OF ARRAY --control--BOOL OF ARRAY --shift--BOOL OF ATOM = [ [[$FindNextDef, $FindNextDefCaseless], [$FindNextDefCtl, $FindNextDefCaselessCtl]], [[$FindAnyDef, $FindAnyDefCaseless], [$FindAnyDefCtl, $FindAnyDefCaselessCtl]], [[$FindPrevDef, $FindPrevDefCaseless], [$FindPrevDefCtl, $FindPrevDefCaselessCtl]]]; GetImplButt: PROC [parent: REF ANY, clientData: REF ANY ¬ NIL, mouseButton: Menus.MouseButton ¬ red, shift, control: BOOL ¬ FALSE] --Menus.ClickProc-- = { TEditInput.InterpretAtom[NARROW[parent], implAtoms[mouseButton][control][shift]]; }; implAtoms: ARRAY Menus.MouseButton OF ARRAY --control--BOOL OF ARRAY --shift--BOOL OF ATOM = [ [[$LoadImpl, $LoadSmartImpl], [$LoadImpl, $LoadSmartImpl]], [[$OpenImpl, $OpenSmartImpl], [$OpenImpl, $OpenSmartImpl]], [[$CloseAndOpenImpl, $CloseAndOpenSmartImpl], [$CloseAndOpenImpl, $CloseAndOpenSmartImpl]]]; DefCommand: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] ¬ DefCommandWork[viewer, FALSE, FALSE, open]}; DefDeepCommand: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] ¬ DefCommandWork[viewer, TRUE, FALSE, open]}; LoadSmartImplCommand: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] ¬ DefCommandWork[viewer, TRUE, TRUE, load]}; OpenSmartImplCommand: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] ¬ DefCommandWork[viewer, TRUE, TRUE, open]}; CloseAndOpenSmartImplCommand: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = {[recordAtom, quit] ¬ DefCommandWork[viewer, TRUE, TRUE, closeAndOpen]}; How: TYPE ~ {load, open, closeAndOpen}; DefCommandWork: PROC [viewer: Viewer, deep, getImpl: BOOL, how: How] RETURNS [recordAtom: BOOL ¬ TRUE, quit: BOOL ¬ FALSE] --TiogaOps.CommandProc-- = { subjectRope: ROPE = TEditOps.GetSelContents[]; msg: ROPE ¬ NIL; IF NOT cmdEnabled THEN RETURN [FALSE, FALSE]; recordAtom ¬ FALSE; quit ¬ TRUE; IF subjectRope.IsEmpty[] THEN { Msg[oneLiner, IF getImpl THEN "Select target for impl-search." ELSE "Select target for definition-search."]; SimpleFeedback.Blink[$DefDWIM, $Error]; RETURN; }; { start: Place; def: FoundPlace; inSelf: BOOL ¬ FALSE; sought, backErr: ROPE ¬ NIL; Protected: PROC ~ { ENABLE Failure => { Msg[oneLiner, IF where # noplace THEN Rope.Cat[why, " in ", where.fileName] ELSE why]; SimpleFeedback.Blink[$DefDWIM, $Error]; CONTINUE }; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, primary, WithLocks]; IF getImpl THEN { v: Viewer ~ IF inSelf THEN viewer ELSE TEditDocumentPrivate.DoOpenFile[fileName: def.where.fileName, column: right]; subject, fullFName: ROPE; cp: FS.ComponentPositions; [fullFName, cp] ¬ FS.ExpandName[def.where.fileName]; subject ¬ fullFName.Substr[start: cp.base.start, len: cp.base.length].Cat[".", sought]; SELECT how FROM load => [] ¬ TEditDocumentPrivate.DoLoadImplFile[v, subject, FALSE]; open => [] ¬ TEditDocumentPrivate.DoOpenImplFile[subject, left, v]; closeAndOpen => [] ¬ TEditDocumentPrivate.DoCloseAndOpenImplFile[v, subject]; ENDCASE => ERROR; }; SimpleFeedback.ClearHerald[$DefDWIM, $Error]; RETURN}; WithLocks: PROC [tdd: TEditDocument.TEditDocumentData, tSel: TEditDocument.Selection] = { root: TextNode.Ref = IF TextNode.Parent[tdd.text]=NIL THEN tdd.text ELSE TextNode.Root[tdd.text]; startLoc: Location = IF root = TextNode.Root[tSel.start.pos.node] THEN tSel.start.pos ELSE [root, 0]; IF tiogaInterrupt # NIL THEN tiogaInterrupt­ ¬ FALSE; start ¬ [viewer.file, startLoc, bkwdThenFwd]; def ¬ FindDef[subjectRope, start, deep, workVerbose, tiogaInterrupt]; inSelf ¬ TextNode.Root[startLoc.node] = TextNode.Root[def.where.loc.node]; IF getImpl THEN { IF def.where.loc.node # def.nameEnd.node THEN Failure[def.where, "name stretches across node boundary"]; sought ¬ def.where.loc.node.rope.Substr[start: def.where.loc.where, len: def.nameEnd.where-def.where.loc.where+1]; RETURN; }; IF inSelf THEN { tSel.start.pos ¬ def.where.loc; tSel.end.pos ¬ def.nameEnd; tSel.granularity ¬ word; tSel.viewer ¬ viewer; tSel.data ¬ tdd; tSel.pendingDelete ¬ FALSE; TEditOps.RememberCurrentPosition[viewer]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: primary]; TEditInput.CloseEvent[]; TEditRefresh.ScrollToEndOfSel[viewer, FALSE, primary]; } ELSE { TEditInput.CloseEvent[]; ViewerForkers.ForkCall[viewer, OpenAndSelect, LIST[NEW [Place ¬ start], NEW [FoundPlace ¬ def]]]; }; }; IF protect THEN backErr ¬ BackStop.Call[Protected] ELSE Protected[]; IF backErr # NIL THEN {SimpleFeedback.Append[$DefDWIM, oneLiner, $Error, backErr]; RETURN [TRUE, FALSE]}; }}; tiogaInterrupt: REF BOOL ¬ NIL; protect: BOOL ¬ TRUE; OpenAndSelect: PROC [data: REF ANY] --ViewerForkers.CallBack-- = { args: LORA = NARROW[data]; start: REF Place = NARROW[args.first]; end: REF FoundPlace = NARROW[args.rest.first]; first: INT = TextNode.LocNumber[end.where.loc]; last: INT = first + TextNode.LocOffset[end.where.loc, end.nameEnd]; v: Viewer = TEditDocumentPrivate.DoOpenFile[fileName: end.where.fileName, column: right]; IF v = NIL THEN { Msg[oneLiner, "Couldn't open ", end.where.fileName, "."]; SimpleFeedback.Blink[$DefDWIM, $Error]; RETURN; }; ShowGivenPosition[v, first, last, FALSE]; }; ShowGivenPosition: PUBLIC PROC [viewer: Viewer, first, last: INT, skipCommentNodes: BOOL ¬ TRUE] = { DoPosition: PROC [tdd: TEditDocument.TEditDocumentData, tSel: TEditDocument.Selection] = { tSel.start.pos ¬ TextNode.LocWithin[tdd.text, first, 1, skipCommentNodes]; tSel.end.pos ¬ TextNode.LocRelative[tSel.start.pos, last-first, 1, skipCommentNodes]; tSel.granularity ¬ word; tSel.viewer ¬ viewer; tSel.data ¬ tdd; tSel.pendingDelete ¬ FALSE; tSel.insertion ¬ IF TEditProfile.selectionCaret=before THEN before ELSE after; TEditOps.RememberCurrentPosition[viewer]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: feedback]; TEditRefresh.ScrollToEndOfSel[viewer, FALSE, feedback]; }; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, feedback, DoPosition]; }; AmbushMenuEntry: PROC [first: Menus.MenuEntry, name: ROPE, proc: Menus.ClickProc] = { me: Menus.MenuEntry; FOR me ¬ first, me.link WHILE NOT me.name.Equal[name] DO NULL ENDLOOP; me.proc ¬ proc; }; NoteProfile: PROC [changeReason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = { cmdEnabled ¬ UserProfile.Boolean["DefDWIM.Enabled", TRUE]; guessDIRECTORY ¬ UserProfile.Boolean["DefDWIM.GuessDIRECTORY", TRUE]; workVerbose ¬ UserProfile.Boolean["DefDWIM.Verbose", FALSE]; protect ¬ UserProfile.Boolean["DefDWIM.Protect", TRUE]; }; GetTiogaInterrupt: PROC RETURNS [interrupt: REF BOOL] = {interrupt ¬ TEditInput.interrupt}; Start: PROC = { tiogaInterrupt ¬ GetTiogaInterrupt[!VM.AddressFault => CONTINUE]; TiogaOps.RegisterCommand[$FindAnyDef, DefCommand]; TiogaOps.RegisterCommand[$FindAnyDefCtl, DefDeepCommand]; TiogaOps.RegisterCommand[$LoadSmartImpl, LoadSmartImplCommand]; TiogaOps.RegisterCommand[$OpenSmartImpl, OpenSmartImplCommand]; TiogaOps.RegisterCommand[$CloseAndOpenSmartImpl, CloseAndOpenSmartImplCommand]; AmbushMenuEntry[TEditDocumentPrivate.findMenu, "Def", DefButt]; AmbushMenuEntry[TiogaMenuOps.tiogaMenu.lines[0], "GetImpl", GetImplButt]; UserProfile.CallWhenProfileChanges[NoteProfile]; Register[RecognizeLisp, LispFinder]; Register[RecognizeJS, JSFinder]; Register[RecognizeM3, M3Finder]; Register[RecognizeCedar, CedarFinder]; }; Start[]; END. bDefDWIMImpl.mesa Copyright Σ 1992 by Xerox Corporation. All rights reserved. Mike Spreitzer March 1, 1987 5:45:39 pm PST Last tweaked by Mike Spreitzer on September 10, 1992 10:36 am PDT Stuff Kernel Some particular languages When match ends in ":=", accepts, in reverse: ("VAR"|"VALUE"|"READONLY" white)|("PROCEDURE" white* id white* "(" ) (white* id white* ",")* white* Otherwise, accepts, in reverse: white|"," Controls whether the Cedar finder ignores comment nodes. If not leftmost, we never guess it might be a missing DIRECTORY entry. An OPENed module without a USING list is recorded in searchedOpens, which may be NIL. p is positioned after the colon or Rightarrow. Accepts, in reverse: ((nonAlpha ("BEGIN"|"OPEN") white+) | "{" | "[" | ";" | ",") white* User Interface Copied from TEditSelectionOpsImpl sets bit to cause scroll after refresh Setup Κ2Q–(cedarcode) style•NewlineDelimiter ˜code™Kšœ Οeœ1™Kšžœžœžœ>˜`Kšžœ˜—Kšžœ>˜DK˜K˜—šŸœžœžœ1˜FKšœ žœ#˜2K˜K˜—šŸœžœ7žœžœ˜OKšœžœ ˜)K˜4K˜—K˜L™šŸ œžœžœžœ žœžœžœžœ  œ˜ŒKšœ_žœ˜fK˜5Kšœžœ˜K˜@Kšœžœ˜K˜Kšœžœ˜šŸœžœžœ žœ˜:Kšœ’ +œ%˜ςKšžœ žœ˜—šŸœžœžœ žœ˜'Kšœ±˜±Kšžœ žœ˜—šœžœžœž˜*Kšœ#˜#Kšœ žœ ˜-Kšœžœ˜-Kšžœžœ˜—Kšžœ žœžœ žœD˜hšžœžœžœ˜Kšœžœ!˜)Kšœ žœ˜"šžœžœžœ˜!Kšœžœ ˜,Kšœžœ$˜-Kšœ6˜6Kšœ,žœžœ!˜WKšžœ˜—K˜—šžœžœ˜K˜8K˜;K˜>K˜7K˜—Kšžœ;˜?K˜K˜—š Ÿ œžœžœžœ œ˜HK˜K˜K˜—Kšœžœ˜#K˜šŸ œžœžœ˜:Kšœžœ˜Kšœžœ˜Kšžœžœžœ ˜3Kšœžœ˜2K˜4šœžœžœž˜Kšœžœ ˜Kšœžœ ˜#Kšžœ ˜—K˜K˜—šŸœžœžœžœ žœžœžœžœ  œ˜‡Kšœ ˜ Kšžœžœ1˜=Kšœ+žœ˜HK˜K˜—šŸ œžœžœžœ  œ œ œ œ žœžœžœžœ  œ˜ΠKšœžœ&˜8šžœžœ˜"KšœC˜CK˜—Kšžœžœžœžœ˜*Kšžœ žœC˜RKšœžœžœ˜+Kšœvžœžœ˜ˆK˜5Kšœi˜iKš œžœžœžœžœžœ˜6K˜@Kšœžœ/˜8Kšœžœ˜K˜š Ÿœžœžœžœ žœ˜8Kšœ žœžœ%˜:Kšœžœ˜šžœžœž˜K˜–Kšžœžœžœžœ˜Kšžœžœ5žœžœ˜dKšžœ˜—K˜—šŸœžœžœ žœ˜'Kšœ žœžœ%˜:Kšœžœ˜!Kšœžœ˜šžœžœž˜K˜­Kšžœžœžœžœ˜Kšžœžœ5žœ)žœ˜oKšžœ˜—K˜—šœžœžœž˜*Kšœ˜Kšœžœ ˜)Kšœžœ˜)Kšžœžœ˜—Kšžœ žœžœ žœD˜hšžœžœ˜K˜9K˜?K˜5K˜7K˜—šžœ˜š Ÿœžœžœžœ žœ˜@šžœžœ žœž˜2Kšœ žœ˜)Kšžœ žœžœ žœD˜hKšžœžœžœ%žœ˜FKšœžœ˜ Kšžœ%žœ ž˜9šžœ˜Kšžœžœ$žœžœ˜5Kšœ™žœžœ˜«Kšœ˜—Kšžœžœžœ˜Kšžœ˜—Kšœžœ˜K˜—šžœžœžœžœ˜IKšžœž˜ Kšžœ;˜?—Kšœ˜—K˜Kšžœžœžœžœ˜&K˜K˜—šŸ œžœžœ žœ œ œ žœžœžœ˜’KšœEžœ˜LKšžœžœžœžœ=˜aKšœR˜RK˜K˜K˜—š Ÿ œžœžœžœžœžœ˜WKšœžœ˜!K˜-šŸœžœ!žœžœ˜Bšž˜Kšœ-˜-šžœ ž˜Kšœžœž œ˜%˜ Kšœžœžœ-˜=šžœž˜ Kš œžœžœžœžœžœ˜;Kšœžœ˜*Kšžœžœ˜—K˜—Kšœžœ˜Kš œ žœžœžœžœžœ˜6Kšœ žœ˜$Kšœ žœ˜Kšžœžœ˜—Kšžœ˜—K˜—Kšœžœžœ ˜Kšžœžœžœžœ˜4K˜&šž˜Kšœ-˜-šžœ ž˜Kšœžœž œ˜%˜ Kšœžœžœ-˜=šžœž˜ Kšœžœ˜Kš œžœ žœžœžœžœžœ˜[Kšœ1žœ žœžœžœžœžœžœžœ˜”Kšœ žœ žœžœžœžœžœžœžœ˜pKš œžœžœ žœžœžœžœ˜ZKšœžœ ˜"Kšžœžœžœ˜—K˜—Kšœžœ4˜UKš œ žœžœ žœžœžœžœ˜SKšœ žœžœ˜Kšœ žœ˜Kšžœžœ˜—Kšžœ˜—K˜Kšœžœ˜%Kšœžœ˜%Kšœ žœ˜'Kšœ žœ˜'Kšœ žœ˜)Kšœ žœ˜&Kšœ žœ˜'K˜—šŸ œžœžœžœ˜9Kšœ6˜6Kšœžœžœ;žœ˜SKšœžœ˜Kš œžœžœžœžœ ˜K˜Kšžœ žœžœžœ˜"Kšœžœ˜$K˜ Kšžœžœžœžœ˜*šžœžœžœž˜)šžœž˜Kšžœ žœžœžœ˜Kšœžœžœ˜Kšžœžœžœ˜—Kšžœ˜—Kšžœžœ˜K˜K˜—šŸ œžœžœžœ žœžœžœžœ  œ˜‰Kšœežœ˜lK˜5Kšœžœ˜K˜@Kšœžœ˜K˜Kšœžœ˜K˜1šŸœžœžœ žœ˜:Kšœ«˜«Kšžœ žœ˜—šŸœžœžœ žœ˜'Kšœ±˜±Kšžœ žœ˜—šœžœžœž˜*Kšœ˜Kšœ žœ ˜-Kšœžœ˜-Kšžœžœ˜—Kšžœ žœžœ žœD˜hšžœžœ˜K˜:K˜?K˜>K˜7K˜—Kšžœ;˜?K˜K˜—K˜š Ÿ œžœžœžœ œ˜DKšœžœ˜Kšœžœ˜Kšœžœ˜ Kšžœžœžœžœ˜3Kšœžœ˜2K˜4Kšžœžœžœžœ˜(K˜Kš žœžœžœžœžœ˜'K˜Kš žœžœžœžœžœ˜'Kšžœžœ˜K˜—šŸœžœžœžœ žœžœžœžœ  œ˜‡Kšœžœ!˜)Kšœ žœ˜"šžœžœžœ˜!Kšœžœ ˜,Kšœžœ$˜-K˜Cšžœžœžœ˜K˜4K˜3KšžœP˜V—Kšžœžœ;˜Q—šžœ˜Kšœžœ˜Kšœ_žœ˜fK˜5Kšœžœ˜K˜@Kšœžœ˜K˜Kšœžœ˜šŸœžœžœ žœ˜:Kšœ’ +œ%˜ςKšžœ žœ˜—šŸœžœžœ žœ˜'Kšœ±˜±Kšžœ žœ˜—šœžœžœž˜*Kšœ#˜#Kšœ žœ ˜-Kšœžœ˜-Kšžœžœ˜—Kšžœ žœžœ žœD˜hšžœžœ˜K˜8K˜;K˜>K˜7K˜—šžœ˜Kšœžœ˜KšœžœC˜MKšœ žœ˜K˜£K˜mKšœ žœžœM˜bš Ÿœžœ.žœžœ žœ˜Xšž˜K˜•Kšžœžœžœžœ˜Kšžœžœžœ žœ žœ-žœžœžœžœžœ˜“Kšžœ˜—K˜—šŸœžœ"žœ žœ˜HKšœžœ˜#šž˜K˜­Kšžœžœžœžœ˜Kšžœžœžœ žœ žœ-žœ*žœžœžœžœ˜ŸKšžœ˜—K˜—Kšžœ žœ@˜Ošœžœž˜$Kšœžœ˜2Kšœ$žœžœžœ˜`Kšœžœžœžœ˜`Kšžœžœ˜—Kšžœ žœžœ žœ?˜cKšžœ žœ˜šžœžœ˜K˜KK˜4K˜7K˜—Kšžœ;˜?K˜—K˜—K˜K˜—š Ÿœžœžœžœžœžœ˜SKšœžœ ˜šŸœžœžœžœ˜#K˜ šžœž˜ šžœž˜!Kšžœ˜Kšžœžœ˜—Kšžœ˜—Kšžœ˜—šŸœžœžœžœ˜"Kšœžœ ˜šžœ ž˜Kšœžœ˜(šžœž˜Kšžœ žœ žœ"˜Kšžœ˜—Kšœœžœ#˜ΔKšžœžœ˜—Kšžœ˜ —K˜—šžœžœžœ˜Kšœžœ/˜8Kšœ žœ˜K˜«Kšœžœ˜Kšœžœ˜Kšœ žœžœM˜bš Ÿœžœžœžœ žœ˜8Kšœžœ˜šžœžœž˜K˜•Kšžœžœžœžœ˜Kšžœžœ#žœžœ˜RKšžœ˜—K˜—šŸœžœžœ žœ˜'Kšœžœ˜#Kšœžœ˜šžœžœž˜K˜­Kšžœžœžœžœ˜Kšžœžœ#žœ*žœ˜^Kšžœ˜—K˜—Kšžœ žœ@˜Ošœžœž˜$Kšœ˜Kšœžœ ˜*Kšœžœ˜*Kšžœžœ˜—Kšžœ žœžœ žœ?˜cKšžœ žœ˜šžœžœ˜K˜KK˜4K˜—K˜—š žœžœžœžœžœ˜"šžœBžœžœž˜XKšœ.˜.Kšžœ žœžœ žœ?˜cKšžœžœžœ%žœ˜Fšžœ-žœ˜5Kšœžœ˜ Kšœržœ7žœžœ˜ΐKšžœžœžœ˜Kšœ˜—Kšžœ˜—K˜—š žœžœžœžœ žœ˜5Kšžœ žœO˜^KšœTžœ˜[šžœžœžœ˜ KšœQ˜QKšœžœ˜ Kšœ0žœ7žœžœ˜~Kšžœžœžœ˜K˜—Kšžœ žœ˜K˜—Kšžœžœžœžœ6˜M˜=Kšœžœ˜K˜CK˜ —Kšžœžœžœžœ˜šœžœžœ˜*Kšœ˜Kšœžœžœžœžœžœžœžœ˜HKšœ˜—K˜Kš œžœžœ žœ žœ#˜HKšœžœ˜/K˜K˜—Kšžœžœ˜—Kšžœ˜K˜Kšžœžœžœžœ˜&K˜—K˜š Ÿ œžœžœ žœžœžœ˜JK™XK˜Zšžœ ž˜Kšœžœ)˜3šžœž˜šœ žœž˜K˜!Kšœžœžœ˜ Kšžœžœžœ˜—šœ žœž˜Kšœžœ˜Kšœžœžœ˜ K˜Kšžœžœžœ˜—šœžœž˜K˜K˜Kšžœžœžœ˜—šœ žœž˜K˜Kšžœžœžœ˜—šœ žœž˜K˜Kšžœžœžœ˜—šœ žœž˜K˜Kšžœžœžœ˜—šœ žœž˜K˜Kšžœžœžœ˜—šœ žœž˜K˜Kšžœžœžœ˜—šœžœž˜"Kš žœ žœ žœžœžœ˜>Kšžœžœžœ˜—Kšžœžœ˜—Kšžœ˜—Kšžœžœ˜K˜—šŸ œžœ˜šž˜šžœžœž˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ žœžœ˜+Kšœ žœžœ˜2Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœžœ˜—Kšžœ˜Kšžœ˜—K˜K˜—šŸœžœžœ˜DK˜K˜šž˜K˜šžœž˜˜Kšžœžœžœž˜9Kšžœžœžœ ˜-Kšžœžœ˜"K˜—˜ šžœžœžœžœ˜=Kšœžœ-˜7K˜ K˜—Kšžœžœ˜"K˜—Kšžœžœ˜—Kšžœ˜—K˜K˜—š Ÿœžœžœ žœžœ ˜YKšœWžœ˜^KšžœžœžœžœE˜iKšžœG˜MK˜—šŸœžœ8žœ œ œ žœžœžœ˜«KšœHžœ˜OKšžœžœžœžœV˜zKšœR˜RKšœ'žœ žœžœ˜UK˜K˜—š žœžœžœžœžœžœ˜3Kšœžœ žœžœ˜1Kšœ žœžœ˜K˜—š Ÿ œžœžœ œ œ˜=K˜=K˜K˜—šŸ œžœžœžœžœžœ œ˜OKšœ žœ˜K˜tK˜K˜—šŸœžœžœžœ˜:šžœžœž˜Kšœžœ ˜'K˜NKšžœžœ˜—K˜K˜—š Ÿœžœžœžœžœžœ˜5Kšœžœžœžœ˜1K˜—š Ÿœžœžœžœžœ˜/Kšœžœ9˜AK˜—š Ÿ œžœžœžœžœ žœ˜CKšœ žœ˜Kšœ žœ˜Kšœ'žœ*žœžœ˜yK˜K˜—šŸ œžœžœžœ˜9šžœžœžœž˜šœžœžœžœž˜Kšœžœ˜Kšœžœ˜Kšžœžœ˜—šœžœžœž˜(Kšœžœžœ˜Kšœ6žœ˜QKšžœžœ˜—Kšžœžœ˜—K˜—šŸœžœžœžœ˜5šœžœžœž˜Kšœžœ˜ Kšœ9˜9Kšžœžœ˜—K˜K˜—šŸœžœžœžœ˜JKšžœ žœžœ5˜IKšžœžœ7˜RK˜ KšžœžœžœžœF˜fK˜K˜—L™Kšœ žœžœ˜Kšœ žœžœ˜K˜šŸœžœ žœžœžœžœžœ9žœžœ œ˜—Kšœžœ1˜PKšœ˜šœ žœžœžœ  žœžœžœ  žœžœžœ˜]KšœS˜SKšœO˜OKšœT˜T—K˜—šŸ œžœ žœžœžœžœžœ9žœžœ œ˜›Kšœžœ2˜QKšœ˜šœ žœžœžœ  žœžœžœ  žœžœžœ˜^Kšœ;˜;Kšœ;˜;Kšœ\˜\—K˜—KšŸ œžœžœžœžœžœžœžœ œ0žœžœ ˜»K˜KšŸœžœžœžœžœžœžœžœ œ0žœžœ ˜ΎK˜KšŸœžœžœžœžœžœžœžœ œ0žœžœ ˜ΓK˜KšŸœžœžœžœžœžœžœžœ œ0žœžœ ˜ΓK˜KšŸœžœžœžœžœžœžœžœ œ0žœžœ˜ΣK˜Kšœžœ˜'K˜šŸœžœ!žœ žœžœžœžœžœ œ˜—Kšœ žœ˜.Kšœžœžœ˜Kš žœžœ žœžœžœžœ˜-Kšœ žœ˜Kšœžœ˜ šžœžœ˜Kšœžœ žœ"žœ)˜lK˜'Kšžœ˜K˜—Kšœ˜Kšœ ˜ K˜Kšœžœžœ˜Kšœžœžœ˜šŸ œžœ˜šžœ ˜Kšœžœžœ'žœ˜VK˜'Kšž˜K˜—KšœH˜Hšžœ žœ˜Kšœ žœžœžœN˜tKšœžœ˜Kšœžœ˜Kšœžœ ˜4K˜Wšžœž˜Kšœ=žœ˜DK˜CK˜MKšžœžœ˜—Kšœ˜—K˜-Kšžœ˜—šŸ œžœJ˜YKš œžœžœžœ žœ˜aKšœžœ+žœžœ ˜eKšžœžœžœžœ˜5K˜-K˜EK˜Jšžœ žœ˜Kšžœ'žœ;˜hK˜rKšžœ˜K˜—šžœžœ˜K˜K˜K˜K˜K˜Kšœžœ˜K˜)K˜!K˜žœžœžœ˜iK˜Kšœžœžœžœ˜Kšœ žœžœ˜—K˜š Ÿ œžœžœžœ œ˜BKšœžœžœ˜Kšœžœ žœ ˜&Kšœžœžœ˜.Kšœžœ%˜/Kšœžœ:˜CKšœY˜Yšžœžœžœ˜K˜9K˜'Kšžœ˜K˜—Kšœ"žœ˜)K˜—K˜š Ÿœžœžœžœžœžœ˜dKšœ!™!šŸ œžœJ˜ZK˜JK˜UK˜K˜K˜Kšœžœ˜Kšœžœ$žœžœ˜NK˜)K˜!K˜=Kšœ&žœ ˜7Kšœ&™&K˜—K˜JK˜K˜—šŸœžœ žœ˜UKšœ˜Kš žœžœžœžœžœžœ˜FK˜K˜K˜—L™šŸ œžœ1 "œ˜hKšœ4žœ˜:Kšœ?žœ˜EKšœ5žœ˜