DIRECTORY AMBridge USING [GetWorld, RemoteFHFromTV, FHFromTV, TVForReferent], AMEventBooted USING [BootedNotifier, RegisterBootedNotifier, UnRegisterBootedNotifier], AMEvents USING [BreakID, Event, Outcome, EventProc, GetEvents, BreakAt, ClearBreak, Debugging, Debugged, Booted, StopEvents], AMModel USING [ContextName, ContextWorld, Section, Context], AMModelLocation USING [CodeLocation, EntryLocations], AMTypes USING [DynamicParent, Error], AMViewerOps USING [SourceError, ViewerFromSection, SectionFromSelection, SourceFromTV, OpenSource, ReportProc], Atom USING [GetPropFromList], BBSafety USING [Mother], Commander USING [CommandProc, Register, CommandObject, Handle], Convert USING [ValueToRope], Interpreter USING [ContextForWorld, ContextForLocalFrame, AbortProc, Evaluate], IO USING [PutRope, PutChar, STREAM, CreateOutputStreamToRope, GetOutputStreamRope, GetSequence, GetChar, Close, ResetUserAbort, SetUserAbort, Reset, PutTV, PutSignal], List USING [PutAssoc], MBQueue USING [Queue, Create, CreateMenuEntry], Menus USING [MenuProc, Menu, InsertMenuEntry, ChangeNumberOfLines], PrintTV USING [PutClosure, Print, PutProc, PrintArguments, PrintVariables], Process USING [Detach, SetPriority, GetPriority, Priority, priorityNormal], ProcessProps USING [AddPropList, GetPropList], Rope USING [ROPE, Cat, Equal, IsEmpty], RTBasic USING [TV], SymTab USING [Create, Ref, Store], TiogaOps USING [GetSelection, ViewerDoc, SelectPoint, LastLocWithin], TypeScript USING [TS, Create, Destroy], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerIO USING [CreateViewerStreams], ViewerOps USING [DestroyViewer, AddProp, FetchProp, PaintViewer, EstablishViewerPosition], UserProfile USING [ProfileChangedProc, Boolean, CallWhenProfileChanges], WorldVM USING [WorldName, LocalWorld, World]; InterpreterTool: MONITOR -- protects individual interpreters LOCKS h.LOCK USING h: Handle IMPORTS AMBridge, AMEventBooted, AMEvents, AMModel, AMModelLocation, AMTypes, AMViewerOps, Atom, BBSafety, Commander, Convert, Interpreter, IO, List, MBQueue, Menus, PrintTV, Process, ProcessProps, Rope, SymTab, TiogaOps, TypeScript, UserProfile, ViewerEvents, ViewerIO, ViewerOps, WorldVM = BEGIN OPEN Interpreter, Rope, RTBasic; Handle: TYPE = REF InterpreterObject; -- one per viewer InterpreterObject: PUBLIC TYPE = MONITORED RECORD [ remoteWorld: WorldVM.World, -- NIL => local only in this nest context: AMModel.Context _ NIL, event: AMEvents.Event _ NIL, nestingLevel: NAT _ 0, symTab: SymTab.Ref _ NIL, nextVarIndex: INT _ 0, ts: TypeScript.TS _ NIL, tsInStream: IO.STREAM _ NIL, tsOutStream: IO.STREAM _ NIL, Stuffable: CONDITION, menuHitQueue: MBQueue.Queue _ NIL, terminateRequested: BOOL _ FALSE, proceedRequested: BOOL _ FALSE, abortRequested: BOOL _ FALSE, stopRequested: BOOL _ FALSE, bootReturnRequested: BOOL _ FALSE, bootedWorld: WorldVM.World _ NIL, stuffAble: BOOL _ TRUE ]; BreakIndex: TYPE = INT; nullBreakIndex: BreakIndex = 0; nextBI: BreakIndex _ 1; -- BEWARE that this guy isn't protected BreakObject: TYPE = RECORD[index: BreakIndex, breakID: AMEvents.BreakID, world: WorldVM.World, section: AMModel.Section]; Break: TYPE = REF BreakObject; NewInterpreter: Commander.CommandProc = TRUSTED{ Process.Detach[LOOPHOLE[FORK CreateTool[NIL--no event--], PROCESS]]; }; CreateTool: PROC[event: AMEvents.Event] RETURNS[outcome: AMEvents.Outcome] = TRUSTED{ h: Handle; context: AMModel.Context = IF event = NIL THEN ContextForWorld[] ELSE ContextForLocalFrame[event.frame]; remoteWorld: WorldVM.World _ NIL; oldPriority: Process.Priority _ Process.GetPriority[]; Process.SetPriority[Process.priorityNormal]; IF context = NIL THEN ERROR; IF WorldVM.LocalWorld[] # AMModel.ContextWorld[context] THEN remoteWorld _ AMModel.ContextWorld[context]; h _ NEW[ InterpreterObject _ [remoteWorld: remoteWorld, context: context, event: event, menuHitQueue: MBQueue.Create[], symTab: SymTab.Create[]]]; NewViewer[h]; IF remoteWorld # NIL THEN AMEventBooted.RegisterBootedNotifier[BootReturnRequested, remoteWorld, h]; outcome _ MainLoop[h]; IF remoteWorld # NIL THEN AMEventBooted.UnRegisterBootedNotifier[BootReturnRequested, remoteWorld, h]; Process.SetPriority[oldPriority]; }; EventHandler: AMEvents.EventProc = { h: Handle _ NIL; nestingLevel: NAT; -- that of saved interp status context: AMModel.Context; IF event.type = booted THEN RETURN[[quit[]]]; context _ ContextForLocalFrame[event.frame]; [h, nestingLevel] _ FindInProgressEvaluation[]; IF h # NIL AND h.nestingLevel = nestingLevel THEN { -- if this event is "under" an interpreter oldRemoteWorld: WorldVM.World = h.remoteWorld; -- save remoteWorld; oldContext: AMModel.Context = h.context; -- save context; oldEvent: AMEvents.Event = h.event; -- save event; h.tsOutStream.PutRope [Cat["***Nesting this InterpreterTool to be a handler for ", EventToName[event], "\n"]]; IF oldEvent = NIL THEN ChangeLines[h.ts, 2]; IF oldRemoteWorld = NIL THEN h.remoteWorld _ IF WorldVM.LocalWorld[] = AMModel.ContextWorld[context] THEN NIL ELSE AMModel.ContextWorld[context] ELSE { -- some lower event of this tool is for a remote world IF WorldVM.LocalWorld[] # AMModel.ContextWorld[context] AND oldRemoteWorld # AMModel.ContextWorld[context] THEN ERROR; }; h.context _ context; h.event _ event; h.nestingLevel _ h.nestingLevel + 1; IF oldRemoteWorld = NIL AND h.remoteWorld # NIL THEN AMEventBooted.RegisterBootedNotifier [BootReturnRequested, h.remoteWorld, h]; outcome _ MainLoop[h]; IF oldRemoteWorld = NIL AND h.remoteWorld # NIL THEN AMEventBooted.UnRegisterBootedNotifier [BootReturnRequested, h.remoteWorld, h]; h.nestingLevel _ h.nestingLevel - 1; h.event _ oldEvent; h.context _ oldContext; h.remoteWorld _ oldRemoteWorld; IF oldEvent = NIL THEN ChangeLines[h.ts, 1]; } ELSE outcome _ CreateTool[event]; -- if this event is not "under" an interpreter }; NewViewer: PROC[h: Handle] = { name: ROPE; [] _ SymTab.Store [h.symTab, "&H", AMBridge.TVForReferent[NEW[Handle _ h] ! AMTypes.Error => CONTINUE]]; [] _ SymTab.Store [h.symTab, "&WalkStack", AMBridge.TVForReferent [NEW[PROC[nFrames: INT _ 1, h: Handle _ NIL] _ WalkStack] ! AMTypes.Error => CONTINUE]]; [] _ SymTab.Store [h.symTab, "&ShowFrame", AMBridge.TVForReferent [NEW[PROC[h: Handle _ NIL] _ ShowFrame] ! AMTypes.Error => CONTINUE]]; [] _ SymTab.Store [h.symTab, "&Source", AMBridge.TVForReferent [NEW[PROC[h: Handle _ NIL] _ Source] ! AMTypes.Error => CONTINUE]]; [] _ SymTab.Store [h.symTab, "&SetBreak", AMBridge.TVForReferent [NEW[PROC[h: Handle _ NIL] _ SetBreak] ! AMTypes.Error => CONTINUE]]; [] _ SymTab.Store [h.symTab, "&ClearBreak", AMBridge.TVForReferent [NEW[PROC[h: Handle _ NIL] _ ClearBreak] ! AMTypes.Error => CONTINUE]]; IF h.event # NIL THEN name _ Cat["Event handler for ", EventToName[h.event]] ELSE name _ Cat["Interpreter for context ", AMModel.ContextName[h.context]]; h.ts _ TypeScript.Create [info: [name: name, column: right, iconic: FALSE]]; CreateInterpreterMenu[h]; ViewerOps.AddProp[h.ts, $InterpreterHandle, h]; [h.tsInStream, h.tsOutStream] _ ViewerIO.CreateViewerStreams[name: NIL, viewer: h.ts]; }; EventToName: PROC[event: AMEvents.Event] RETURNS[name: ROPE _ NIL] = { reason: ROPE; s: IO.STREAM _ NIL; WITH e: event SELECT FROM booted => reason _ "Booted"; break => { -- [id: BreakID, clientData: REF ANY] break: Break _ NIL; IF e.clientData # NIL THEN break _ NARROW[e.clientData, Break ! ANY => CONTINUE]; IF break = NIL THEN reason _ Cat["Unknown breakpoint ", " hit."] ELSE reason _ Cat["Breakpoint # ", Convert.ValueToRope[[signed[break.index]]], " hit."]; }; call => reason _ Cat["Call: ", e.msg]; signal => { -- [signal, args: RTBasic.TV ] inner: SAFE PROC = TRUSTED {s.PutSignal[e.signal, e.args]}; s _ IO.CreateOutputStreamToRope[]; s.PutRope[BBSafety.Mother[inner]]; name _ IO.GetOutputStreamRope[s]; RETURN; }; interrupt => reason _ "Interrupt"; unknown => reason _ Cat["Unknown: ", e.why]; -- client bug; psbi and frame are valid. ENDCASE => ERROR; name _ Cat[reason, ", world: ", WorldVM.WorldName[event.world]]; { inner: SAFE PROC = TRUSTED {s.PutRope[AMModel.ContextName[event.frame]]}; s _ IO.CreateOutputStreamToRope[]; s.PutRope[BBSafety.Mother[inner]]; name _ Cat[name, ", context: ", IO.GetOutputStreamRope[s]]; }; }; CreateInterpreterMenu: PROC[h: Handle] = { menu: Menus.Menu _ h.ts.menu; Menus.InsertMenuEntry [menu: menu, line: 0, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "STOP!", proc: StopHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 0, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "SetBreak", proc: SetBreakHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 0, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "ClearBreak(s)", proc: ClearBreakHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 1, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "WalkStack", proc: WalkStackHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 1, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "ShowFrame", proc: ShowFrameHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 1, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "Proceed", proc: ProceedHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 1, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "Abort", proc: AbortHit, clientData: h]]; Menus.InsertMenuEntry [menu: menu, line: 1, entry: MBQueue.CreateMenuEntry [q: h.menuHitQueue, name: "Source", proc: SourceHit, clientData: h]]; IF h.event # NIL THEN ChangeLines[h.ts, 2] ELSE ViewerOps.PaintViewer[h.ts, menu]; }; ChangeLines: PROC[v: ViewerClasses.Viewer, nLines: NAT] = { Menus.ChangeNumberOfLines[v.menu, nLines]; ViewerOps.EstablishViewerPosition[v, v.wx, v.wy, v.ww, v.wh]; ViewerOps.PaintViewer[v, all]; }; MainLoop: PROC[h: Handle] RETURNS[outcome: AMEvents.Outcome _ [proceed[NIL]]] = { somethingHappened: BOOL _ TRUE; h.tsOutStream.PutChar['\n]; UNTIL WasTerminateRequested[h] -- the interpreter viewer was destroyed DO line: ROPE _ NIL; result: TV _ NIL; noResult: BOOL _ TRUE; errorRope: ROPE _ NIL; prompt: ROPE _ Cat["&", Convert.ValueToRope[[signed[NextVarIndex[h, somethingHappened]]]], " _ "]; leader: ROPE _ NIL; inner: SAFE PROC = TRUSTED {IF NOT noResult THEN h.tsOutStream.PutTV[result];}; brq: BOOL; brqWorld: WorldVM.World _ NIL; somethingHappened _ FALSE; IF WasProceedRequested[h] THEN { ResetProceedRequested[h]; IF h.event # NIL THEN { IF h.nestingLevel = 0 THEN {SetDormant[h]; h.event _ NIL}; RETURN; }; }; IF WasAbortRequested[h] THEN { ResetAbortRequested[h]; IF h.nestingLevel > 0 THEN RETURN[[quit[]]] -- RETURN => event proc returns ELSE IF h.event # NIL THEN {SetDormant[h]; h.event _ NIL; RETURN[[quit[]]]}; }; [brq, brqWorld] _ WasBootReturnRequested[h]; IF brq THEN { IF brqWorld = h.remoteWorld THEN { -- RETURN => event proc returns IF h.nestingLevel = 0 THEN {SetDormant[h]; h.event _ NIL}; RETURN[[quit[]]]; } ELSE ResetBootReturnRequested[h]; }; FOR i: NAT IN [0..h.nestingLevel) DO leader _ Cat[leader, "<***>"]; ENDLOOP; h.tsOutStream.PutRope[leader ! ANY => CONTINUE]; h.tsOutStream.PutRope[prompt ! ANY => CONTINUE]; line _ PullLine[h ! ABORTED, UNWIND => { h.tsInStream.Reset[ ! ANY => CONTINUE]; h.tsOutStream.PutRope[" XXX\n" ! ANY => CONTINUE]; h.tsInStream.ResetUserAbort[ ! ANY => CONTINUE]; CONTINUE; }; AMEvents.Debugging, AMEvents.Debugged => REJECT; ANY => { h.tsInStream.Reset[ ! ANY => CONTINUE]; h.tsOutStream.PutRope[" XXX\n" ! ANY => CONTINUE]; h.tsInStream.ResetUserAbort[ ! ANY => CONTINUE]; CONTINUE; }; ]; SetStuffable[h]; IF IsEmpty[line] OR Equal[line, "\n"] THEN LOOP; somethingHappened _ TRUE; ResetStopRequested[h]; { -- open for call on ProcessProps.AddPropList inner: SAFE PROC = TRUSTED{ [result, errorRope, noResult] _ Interpreter.Evaluate [rope: Cat[prompt, line], context: h.context, symTab: h.symTab, abort: [abortProc, h] ! AMEvents.Booted => {errorRope _ " XXX (client world booted)\n"; CONTINUE}; ABORTED, UNWIND => {errorRope _ " XXX\n"; CONTINUE}; ]; }; ch: Commander.Handle = NEW [Commander.CommandObject _ [in: h.tsInStream, out: h.tsOutStream, err: h.tsOutStream, commandLine: line, command: "_", propertyList: NIL]]; ProcessProps.AddPropList[ propList: List.PutAssoc [key: $CommanderHandle, val: ch, aList: List.PutAssoc[ key: $InterpreterHandle, val: h, aList: List.PutAssoc[ key: $InterpreterNestingLevel, val: NEW[NAT _ h.nestingLevel], aList: NIL]]], inner: inner ]; }; -- close for call on ProcessProps.AddPropList IF errorRope # NIL THEN h.tsOutStream.PutRope [IF errorRope # NIL THEN Cat["***", errorRope, "\n"] ELSE "*** XXX\n" ! ANY => CONTINUE] ELSE h.tsOutStream.PutRope[Cat[BBSafety.Mother[inner], "\n"] ! ANY => CONTINUE]; ENDLOOP; IF h.nestingLevel > 0 THEN RETURN[[quit[]]] ELSE Finalize[h]; }; abortProc: Interpreter.AbortProc = TRUSTED { RETURN[WasStopRequested[NARROW[data, Handle]]]; }; FindInProgressEvaluation: PROC RETURNS[h: Handle _ NIL, nestingLevel: NAT _ 0] = { hn: Handle = NARROW[Atom.GetPropFromList[ProcessProps.GetPropList[], $InterpreterHandle]]; rn: REF NAT = NARROW[Atom.GetPropFromList[ProcessProps.GetPropList[], $InterpreterNestingLevel]]; IF hn # NIL AND rn # NIL THEN RETURN[hn, rn^]; }; GetHandlePlease: PROC RETURNS[Handle] = { r: REF ANY = Atom.GetPropFromList[ProcessProps.GetPropList[], $InterpreterHandle]; RETURN[NARROW[r, Handle]]; }; Finalize: PROC [h: Handle] = { h.tsOutStream.PutRope["\n\n ~~~~~~~~~~~~~TERMINATED~~~~~~~~~~~~~ \n\n" ! ANY => CONTINUE]; h.context _ NIL; h.event _ NIL; h.symTab _ NIL; IF h.ts # NIL THEN TypeScript.Destroy[h.ts ! ANY => CONTINUE]; h.ts _ NIL; h.tsInStream _ NIL; IF h.tsOutStream # NIL THEN h.tsOutStream.Close[! ANY => CONTINUE]; h.tsOutStream _ NIL; h.menuHitQueue _ NIL; h.ts _ NIL; }; printOneChar: SAFE PROC [data: REF, c: CHAR] = TRUSTED { NARROW[data, IO.STREAM].PutChar[c ! ANY => CONTINUE]; }; NextVarIndex: ENTRY PROC[h: Handle, somethingHappened: BOOL] RETURNS[INT] = { ENABLE UNWIND => NULL; IF NOT somethingHappened THEN RETURN[h.nextVarIndex]; RETURN[h.nextVarIndex _ h.nextVarIndex + 1]; }; ResetStopRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.stopRequested _ FALSE; }; SetStopRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.stopRequested _ TRUE; }; WasStopRequested: ENTRY PROC[h: Handle] RETURNS[BOOL] = { ENABLE UNWIND => NULL; RETURN[h.stopRequested]; }; StuffIt: ENTRY PROC[h: Handle, rope: ROPE] = { ENABLE UNWIND => NULL; UNTIL h.stuffAble DO WAIT h.Stuffable; ENDLOOP; h.stuffAble _ FALSE; h.tsInStream.Reset[]; IF IsEmpty[rope] THEN RETURN; IF TiogaOps.GetSelection[].viewer = h.ts THEN TiogaOps.SelectPoint[viewer: h.ts, caret: TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[h.ts]]]; h.ts.class.notify[h.ts, LIST[rope]]; }; SetStuffable: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.stuffAble _ TRUE; NOTIFY h.Stuffable; }; SetDormant: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.tsOutStream.PutRope["\n~~~~~~~~~~~~~~~~~~~~~\n" ! ANY => CONTINUE]; ChangeLines[h.ts, 0]; }; SetProceedRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.proceedRequested _ TRUE; }; ResetProceedRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.proceedRequested _ FALSE; }; WasProceedRequested: ENTRY PROC[h: Handle] RETURNS[BOOL] = { ENABLE UNWIND => NULL; RETURN[h.proceedRequested]; }; SetBootReturnRequested: ENTRY PROC[h: Handle, world: WorldVM.World] = { ENABLE UNWIND => NULL; h.bootReturnRequested _ TRUE; h.bootedWorld _ world; }; ResetBootReturnRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.bootReturnRequested _ FALSE; }; WasBootReturnRequested: ENTRY PROC[h: Handle] RETURNS[yes: BOOL, world: WorldVM.World] = { ENABLE UNWIND => NULL; RETURN[h.bootReturnRequested, h.bootedWorld]; }; SetAbortRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.abortRequested _ TRUE; }; ResetAbortRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; h.abortRequested _ FALSE; }; WasAbortRequested: ENTRY PROC[h: Handle] RETURNS[BOOL] = { ENABLE UNWIND => NULL; RETURN[h.abortRequested]; }; SetTerminateRequested: ENTRY PROC[h: Handle] = { ENABLE UNWIND => NULL; IF h.tsInStream # NIL THEN h.tsInStream.Close[! ANY => CONTINUE]; h.terminateRequested _ TRUE; }; WasTerminateRequested: ENTRY PROC[h: Handle] RETURNS[BOOL] = { ENABLE UNWIND => NULL; RETURN[h.terminateRequested]; }; PullLine: PROC[h: Handle] RETURNS[line: ROPE _ NIL] = { line _ h.tsInStream.GetSequence[]; [] _ h.tsInStream.GetChar[]; -- heave the CR line _ Cat[line, "\n"]; }; StopHit: Menus.MenuProc = TRUSTED { SetStopRequested[NARROW[clientData, Handle]]; }; NextBreakIndex: PROC RETURNS[bi: BreakIndex] = { bi _ nextBI; nextBI _ nextBI + 1; }; SectionsMatch: PROC [s1, s2: AMModel.Section] RETURNS[BOOL] = { w1, w2: WorldVM.World; cl1, cl2: LIST OF AMModelLocation.CodeLocation; [w1, cl1] _ AMModelLocation.EntryLocations[s1]; [w2, cl2] _ AMModelLocation.EntryLocations[s2]; IF w1 # w2 THEN RETURN[FALSE]; FOR cl: LIST OF AMModelLocation.CodeLocation _ cl1, cl.rest UNTIL cl = NIL DO IF cl2 = NIL OR cl.first # cl2.first THEN RETURN[FALSE]; cl2 _ cl2.rest ENDLOOP; RETURN[TRUE]; }; HighlightBreakPoint: PROC [break: Break, err: IO.STREAM] = { report: AMViewerOps.ReportProc = -- [msg: ROPE, severity: Severity] TRUSTED {err.PutRope[msg]}; IF break # NIL THEN [] _ AMViewerOps.ViewerFromSection[break.section, report ! AMViewerOps.SourceError => {err.PutRope[reason]; CONTINUE}]; }; SetBreakHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; StuffIt[h, Cat[" ---- &SetBreak[h: &H]\n"]]; }; SetBreak: PROC[h: Handle _ NIL] = { break: Break _ NIL; msg: ROPE _ NIL; world: WorldVM.World; inner: SAFE PROC = TRUSTED { section: AMModel.Section _ NIL; warning: REF; h.tsOutStream.PutRope["Setting break..."]; [section, warning] _ AMViewerOps.SectionFromSelection[world]; IF warning = NIL THEN h.tsOutStream.PutRope["(possible source version mismatch)"]; break _ NEW[BreakObject _ [index: 0, breakID: NIL, world: world, section: section]]; break.breakID _ AMEvents.BreakAt[world, section, break]; break.index _ NextBreakIndex[]; }; IF h = NIL THEN h _ GetHandlePlease[]; world _ IF h.event # NIL THEN h.event.world ELSE AMModel.ContextWorld[h.context]; msg _ BBSafety.Mother[inner]; IF msg = NIL AND break # NIL THEN {h.tsOutStream.PutRope[Cat["Break #", Convert.ValueToRope[[signed[break.index]]], " set."]]; HighlightBreakPoint[break: break, err: h.tsOutStream]; RETURN}; IF msg = NIL THEN msg _ "can't, most likely because it already is set here."; h.tsOutStream.PutRope[msg]; IF break # NIL THEN HighlightBreakPoint[break: break, err: h.tsOutStream]; }; ClearBreakHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; StuffIt[h, Cat[" ---- &ClearBreak[h: &H]\n"]]; }; ClearBreak: PROC [h: Handle _ NIL] = TRUSTED { msg, err: ROPE _ NIL; break: Break _ NIL; inner: SAFE PROC = TRUSTED { IF h.event # NIL THEN WITH e: h.event SELECT FROM break => { AMEvents.ClearBreak[e.id]; break _ NARROW[e.clientData, Break ! ANY => CONTINUE]; IF break = NIL THEN err _ "I didn't place this break but I cleared it anyhow"; }; ENDCASE; err _ "not a break event"; }; IF h = NIL THEN h _ GetHandlePlease[]; h.tsOutStream.PutRope["Clearing break..."]; msg _ BBSafety.Mother[inner]; IF msg # NIL THEN {h.tsOutStream.PutRope[Cat["Break not cleared: ", msg]]; RETURN} ELSE IF break = NIL THEN {h.tsOutStream.PutRope[err]; RETURN}; h.tsOutStream.PutRope[Cat["Break #", Convert.ValueToRope[[signed[break.index]]], " cleared."]]; }; WalkStackHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; StuffIt[h, Cat[" ---- &WalkStack[nFrames: ", SELECT mouseButton FROM red => "1", yellow => "0", blue => "-1" ENDCASE => ERROR, ", h: &H]\n"]]; }; WalkStack: PROC[nFrames: INT _ 1, h: Handle _ NIL] = { inner: SAFE PROC = TRUSTED { put: PrintTV.PutClosure; put _ [proc: printOneChar, data: h.tsOutStream]; IF h.event = NIL OR h.event.frame = NIL THEN RETURN; WalkContext[h, nFrames]; PrintTV.Print[tv: h.context, put: put, depth: 1, verbose: TRUE]; }; IF h = NIL THEN h _ GetHandlePlease[]; h.tsOutStream.PutRope[BBSafety.Mother[inner]]; }; WalkContext: ENTRY PROC[h: Handle, n: INT] = { ENABLE UNWIND => NULL; SELECT n FROM = 0 => h.context _ ContextForLocalFrame[h.event.frame]; > 0 => { next: TV _ NIL; this: TV _ h.context; FOR i: NAT IN [1..n] DO this _ AMTypes.DynamicParent[this]; IF this = NIL THEN {h.tsOutStream.PutRope["Can't go any further..."]; EXIT}; next _ this; ENDLOOP; IF next # NIL THEN h.context _ ContextForLocalFrame[next]; }; < 0 => { this: TV _ h.context; prev: TV _ h.event.frame; FOR i: NAT IN [1..-n] DO t: TV; IF FHBits[prev] = FHBits[this] THEN { h.tsOutStream.PutRope["Can't go any further."]; h.context _ ContextForLocalFrame[prev]; RETURN}; t _ AMTypes.DynamicParent[prev]; UNTIL FHBits[t] = FHBits[this] DO prev _ t; t _ AMTypes.DynamicParent[prev] ENDLOOP; this _ prev; ENDLOOP; h.context _ ContextForLocalFrame[prev]; }; ENDCASE; }; FHBits: PROC[lf: TV] RETURNS[fhBits: CARDINAL] = { local: BOOL = AMBridge.GetWorld[lf] = WorldVM.LocalWorld[]; fhBits _ IF local THEN LOOPHOLE[AMBridge.FHFromTV[lf], CARDINAL] ELSE LOOPHOLE[AMBridge.RemoteFHFromTV[lf].fh, CARDINAL]; }; ShowFrameHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; StuffIt[h, Cat[" ---- &ShowFrame[h: &H]\n"]]; }; ProceedHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; SetProceedRequested[h]; h.tsInStream.SetUserAbort[]; StuffIt[h, "\n"]; }; AbortHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; SetAbortRequested[h]; h.tsInStream.SetUserAbort[]; StuffIt[h, "\n"]; }; BootReturnRequested: AMEventBooted.BootedNotifier = TRUSTED { h: Handle = NARROW[clientData, Handle]; SetBootReturnRequested[h, world]; IF h.tsInStream # NIL THEN { h.tsInStream.SetUserAbort[ ! ANY => CONTINUE]; StuffIt[h, "\n" ! ANY => CONTINUE]; }; }; SourceHit: Menus.MenuProc = TRUSTED { h: Handle = NARROW[clientData, Handle]; StuffIt[h, Cat[" ---- &Source[h: &H]\n"]]; }; Source: PROC [h: Handle _ NIL] = { name: ROPE; index: INT; report: AMViewerOps.ReportProc = -- [msg: ROPE, severity: Severity] TRUSTED {h.tsOutStream.PutRope[msg]}; IF h = NIL THEN h _ GetHandlePlease[]; [name, index] _ AMViewerOps.SourceFromTV[h.context, report]; IF name # NIL THEN AMViewerOps.OpenSource[name: name, index: index, report: report]; }; ViewerEvent: ViewerEvents.EventProc = TRUSTED { SELECT event FROM destroy => { prop: REF _ ViewerOps.FetchProp[viewer, $InterpreterHandle]; IF prop # NIL THEN { IF NARROW[prop, Handle].event # NIL THEN RETURN[TRUE]; SetTerminateRequested[NARROW[prop, Handle]]; }; }; ENDCASE; }; ShowFrame: PROC [h: Handle _ NIL] = { put: PrintTV.PutClosure; lf: TV; IF h = NIL THEN h _ GetHandlePlease[]; put _ [proc: printOneChar, data: h.tsOutStream]; IF h.event = NIL OR h.event.frame = NIL THEN RETURN; lf _ h.context; PrintTV.Print[tv: lf, put: put, depth: 1, verbose: TRUE]; h.tsOutStream.PutRope["\nArguments--\n"]; PrintTV.PrintArguments[tv: lf, put: put, breakBetweenItems: TRUE]; h.tsOutStream.PutRope["\nVariables--\n"]; PrintTV.PrintVariables[tv: lf, put: put, all: TRUE, breakBetweenItems: TRUE]; }; worldSwapDebug: BOOL; SetWorldSwapDebug: -- NOTE ENTRY-- UserProfile.ProfileChangedProc = TRUSTED { wsd: BOOL = UserProfile.Boolean[key: "WorldSwapDebug", default: FALSE]; IF reason = firstTime THEN { worldSwapDebug _ wsd; IF NOT worldSwapDebug THEN AMEvents.GetEvents[WorldVM.LocalWorld[], NIL, EventHandler]; RETURN; }; IF wsd # worldSwapDebug THEN { -- user changed his WorldSwapDebug profile entry IF NOT wsd THEN AMEvents.GetEvents[WorldVM.LocalWorld[], NIL, EventHandler] ELSE AMEvents.StopEvents[WorldVM.LocalWorld[]]; worldSwapDebug _ wsd; }; }; [] _ ViewerEvents.RegisterEventProc[proc: ViewerEvent, event: destroy]; Commander.Register[key: "Interpreter", proc: NewInterpreter, doc: "Create a new interpreter tool"]; UserProfile.CallWhenProfileChanges[SetWorldSwapDebug]; END. ΒInterpreterTool.mesa Paul Rovner, May 3, 1983 10:48 am Russ Atkinson, April 13, 1983 7:27 pm Stuff left to do Features ? &procs if no symbols OctalRead etc PROC [cmd: Commander.Handle] PROC[data: REF ANY, event: AMEvents.Event] RETURNS[outcome: AMEvents.Outcome] First, ask if this event is occurring under an interpreter. Ifso, nest the interpreter. NOTE assumption of order in FindInProgressEvaluation [PROCESS, nestingLevel] is in Evaluate, i.e. this event is occurring under an interpreter. Nest the event. Now do the recursive call of MainLoop. Return from recursive call of MainLoop. Now pop h's state (i.e. restore h). Won't raise any signals. flushes viewer cache STOPGAP 'till a viewers bug is fixed Read/Eval/Print loop. FORK'd for each interpreter viewer. Called recursively if an error is raised during interpretation. Here if the interpreter viewer was destroyed. h.tsInStream will have been closed. PROC [data: REF] RETURNS [abort: BOOL]; Here to finalize an InterpreterObject (e.g. to break circular structures, close streams) [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] PROC[world: WorldVM.World, clientData: REF] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] PROC [viewer: ViewerClasses.Viewer, event: ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] ; out.PutRope["\nG- "]; PrintTV.PrintVariables[tv: AMTypes.GlobalParent[lf], put: put]; [reason: ProfileChangeReason] NOTE ENABLE UNWIND => NULL; START HERE Κ!A– "unit" style˜JšΟiœ™Jšœ!™!Jšœ%™%šœ™™J™J™J™ ——˜šΟk ˜ Jšœ žœ5˜CJšœžœD˜WJšœ žœo˜}Jšœžœ/˜˜OJšžœžœžœ…˜§Jšœžœ ˜Jšœžœ"˜/Jšœžœ8˜CJšœžœ>˜KJšœžœ>˜KJšœ žœ˜.Jšœžœžœ˜'Jšœžœžœ˜Jšœžœ˜"Jšœ žœ7˜EJšœ žœžœ˜'Jšœžœ ˜Jšœ žœ ˜2Jšœ žœ˜%Jšœ žœK˜ZJšœ žœ7˜HJšœžœ ˜-—J˜šœžœΟc#˜=Jšžœžœžœ ˜Jšžœ…žœ“˜‘—J˜Jšœžœžœ˜(J˜JšœžœžœŸ˜8šœžœžœ˜ šž œžœ˜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šœ žœžœ_˜yšœžœžœ ˜J˜—šΟbœžœ˜0Jšžœ™Jš œžœžœ žŸ œžœ˜DJ˜J˜—šΟn œžœ˜'Jšžœžœ˜-Jšœ ˜ šœ˜Jšžœ žœžœžœ#˜M—Jšœžœ˜!Jšœ6˜6J˜Jšœ,˜,Jšžœ žœžœžœ˜šžœ5˜7Jšžœ-˜1—šœžœ˜šœ˜šœ˜Jšœ˜Jšœ˜Jšœ!˜!Jšœ˜———Jšœ ˜ šžœž˜šž˜JšœJ˜J——Jšœ˜šžœž˜šž˜JšœL˜L——Jšœ!˜!Jšœ˜—J˜šœ$˜$šœM™MJšœ žœ˜JšœžœŸ˜2Jšœ˜Jšžœžœžœ ˜-Jšœ,˜,J˜JšœX™XJšœ™4Jšœ/˜/šžœžœžœ˜,šžœŸ'˜2Jšœk™kJšœ0Ÿ˜DJšœ*Ÿœ˜:Jšœ%Ÿœ˜3šœ˜šœ<˜šžœ5˜7Jšžœ/˜2Jšžœžœ˜ —Jšœ˜——Jšœ˜Jšœ˜Jšœ$˜$š žœžœžœžœž˜4šœ$˜$Jšœ(˜(—J™Jšœ&™&—šœ˜JšœL™L—J˜š žœžœžœžœž˜4šœ&˜&Jšœ(˜(——Jšœ$˜$Jšœ˜Jšœ˜Jšœ˜Jšžœ žœžœ˜,J˜—JšžœŸ.˜P—Jšœ˜J˜——š‘ œžœ˜Jšœžœ˜ šœ˜šœ˜šœžœ ˜(Jšœžœ˜———šœ˜šœ˜šœ˜Jš œžœžœ žœžœ˜;Jšœžœ˜———šœ˜šœ˜šœ˜Jšœžœžœ žœ˜)Jšœžœ˜———šœ˜šœ˜šœ˜Jšœžœžœ žœ ˜&Jšœžœ˜———šœ˜šœ˜šœ˜Jšœžœžœ žœ ˜(Jšœžœ˜———šœ˜šœ˜šœ˜Jšœžœžœ žœ˜*Jšœžœ˜———šžœ ž˜Jšžœ7˜;JšžœH˜L—šœ˜Jšœ-žœ˜5—Jšœ˜Jšœ/˜/JšœCžœ˜Všœ˜J˜——Jšœ™š Πbn œžœžœžœžœ˜FJšœžœ˜ Jšœžœžœžœ˜šžœ žœž˜Jšœ˜šœ Ÿ&˜2Jšœžœ˜Jš žœžœžœ žœžœžœ˜Qšžœ ž˜Jšžœ-˜1šžœ@žœ ˜NJšœ˜——J˜—Jšœ'˜'šœ Ÿ˜+Jšœžœžœžœ!˜;Jšœžœ˜"Jšœ"˜"Jšœžœ˜!Jšžœ˜J˜—Jšœ"˜"Jšœ-Ÿ(˜U—Jšœžœžœ˜Jšœ@˜@šœ žœžœžœ/˜LJšœžœ˜"Jšœ"˜"Jšœ žœ˜;—J˜Jšœ˜J˜—š‘œžœ˜*Jšœ˜šœ˜Jšœ ˜ J˜ šœ˜JšœB˜B——šœ˜Jšœ ˜ J˜ šœ˜JšœI˜I——šœ˜Jšœ ˜ J˜ šœ˜JšœP˜P——J˜šœ˜Jšœ ˜ J˜ šœ˜JšœK˜K——šœ˜Jšœ ˜ J˜ šœ˜JšœK˜K——šœ˜Jšœ ˜ J˜ šœ˜JšœG˜G——šœ˜Jšœ ˜ J˜ šœ˜JšœC˜C——šœ˜Jšœ ˜ J˜ šœ˜JšœE˜E——Jšžœ žœžœžœ#˜RJšœ˜—J˜š‘ œžœ"žœ˜;Jšœ*˜*šœ=˜=Jšœ9™9—Jšœ˜Jšœ˜J˜—Jšœy™yš‘œžœ ˜Jšžœ&žœ˜7Jšœžœžœ˜Jšœ˜JšžœŸ'˜Gšžœ˜Jšœžœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜šœž˜ šœ ˜ JšœF˜FJšœ ˜ ——Jšœžœžœ˜Jš œžœžœžœžœžœ žœ˜OJšœžœ˜ Jšœžœ˜J˜Jšœžœ˜šžœ˜šžœ˜Jšœ˜šžœ žœžœ˜Jšžœžœžœ˜:Jšžœ˜Jšœ˜—J˜——šžœ˜šžœ˜Jšœ˜šžœ˜Jšžœžœ Ÿ ˜7Jš žœžœ žœžœžœžœ ˜L—J˜——Jšœ,˜,šžœžœ˜ šžœ˜šžœŸ ˜(Jšžœžœžœ˜:Jšžœ ˜J˜—Jšžœ˜!—J˜—Jš žœžœžœžœ žœ˜LJšœžœžœ˜0Jšœžœžœ˜0šœ˜šœžœžœ˜Jšœžœžœ˜'Jšœ!žœžœ˜2Jšœžœžœ˜0Jšž ˜ Jšœ˜—Jšœ)žœ˜0šžœ˜Jšœžœžœ˜'Jšœ!žœžœ˜2Jšœžœžœ˜0Jšž ˜ Jšœ˜—Jšœ˜—Jšœ˜Jšžœžœžœžœ˜0Jšœžœ˜Jšœ˜J˜šœŸ,˜/šœžœžœžœ˜šœ˜šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—JšœBžœ˜LJšœžœžœžœ˜5J˜—J˜—šœ˜šœž˜šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœžœ˜———šœ˜šœ ˜ šœ ˜ Jšœ˜Jšœ ˜ šœ˜šœ˜Jšœ˜Jšœ˜šœ˜šœ˜Jšœ˜Jšœžœžœ˜Jšœžœ˜——————Jšœ ˜ Jšœ˜——JšœŸ-˜1J˜šžœ ž˜šž˜šœ˜šœžœ ž˜Jšžœ˜ Jšžœžœžœ˜#———Jšžœ;žœžœ˜P——Jšžœ˜J˜JšœR™RJšžœžœžœ žœ ˜=Jšœ˜—J˜šœ#žœ˜,Jšœ'™'Jšžœžœ˜/Jšœ˜J˜—š ‘œžœžœ žœžœ ˜Ršœ ˜ JšœžœG˜O—šœž˜ JšœžœM˜U—Jš žœžœžœžœžœžœ ˜.Jšœ˜J˜—š‘œžœžœ ˜)JšœžœžœH˜RJšžœžœ ˜Jšœ˜J˜—š‘œžœ˜JšœX™XJšœIžœžœ˜ZJšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jš žœžœžœžœžœ˜>Jšœžœ˜ Jšœžœ˜Jš žœžœžœžœžœ˜CJšœžœ˜Jšœžœ˜Jšœžœ˜ J˜J˜—š œž œžœžœžœ˜8Jš žœžœžœžœžœ˜5J˜J˜—š ‘ œžœžœžœžœžœ˜MJšžœžœžœ˜Jšžœžœžœžœ˜5Jšžœ&˜,Jšœ˜J˜—š‘œžœžœ˜-Jšžœžœžœ˜Jšœžœ˜Jšœ˜J˜—š‘œžœžœ˜+Jšžœžœžœ˜Jšœžœ˜Jšœ˜J˜—š ‘œžœžœ žœžœ˜9Jšžœžœžœ˜Jšžœ˜Jšœ˜J˜—procš‘œž œžœ˜/Jšžœžœžœ˜Jšžœ žœžœžœ˜/Jšœžœ˜Jšœ˜Jšžœžœžœ˜Jšžœ'žœ]˜ŠJšœžœ˜$Jšœ˜J˜—š‘ œžœžœ˜'Jšžœžœžœ˜Jšœžœ˜Jšžœ ˜Jšœ˜J˜—š‘ œžœžœ˜%Jšžœžœžœ˜Jšœ4žœžœ˜EJšœ˜Jšœ˜J˜—š‘œžœžœ˜.Jšžœžœžœ˜Jšœžœ˜Jšœ˜J˜—š‘œžœžœ˜0Jšžœžœžœ˜Jšœžœ˜Jšœ˜J˜—š ‘œžœžœ žœžœ˜Jšžœžœžœ˜Jšžœ˜Jšœ˜—J˜š ‘œžœ žœžœžœ˜7Jšœ"˜"JšœŸ˜-Jšœ˜Jšœ˜—J˜š œžœ˜#JšœN™NJšœžœ˜-Jšœ˜—J˜š‘œžœžœ˜0Jšœ ˜ Jšœ˜J˜J˜—š’ œžœžœžœ˜?Jšœ˜Jšœ žœžœ˜/Jšœ/˜/Jšœ/˜/Jšžœ žœžœžœ˜š žœžœžœ-žœž˜Jšž˜Jš žœžœžœžœžœžœ˜8J˜—Jšžœ˜—Jšžœžœ˜ Jšœ˜J˜—š‘œžœžœžœ˜<šœ"Ÿ"˜DJšžœ˜—šžœ žœž˜šœ8˜8Jšœ3žœ˜>——J˜J˜—š  œžœ˜'JšœN™NJšœ žœ˜'Jšœ.˜.Jšœ˜J˜—š’œžœ žœ˜#Jšœžœ˜Jšœžœžœ˜Jšœ˜J˜šœžœžœžœ˜Jšœžœ˜Jšœ žœ˜ Jšœ*˜*Jšœ=˜=Jšžœ žœžœ=˜RJšœžœ#žœ#˜TJšœ8˜8Jšœ˜Jšœ˜—J˜Jšžœžœžœ˜&Jš œžœ žœžœžœ!˜QJ˜šžœžœžœ ž˜šžœ&˜*JšœJ˜JJšžœ6˜:Jšž œ˜ ——šžœž˜ Jšžœ<˜@—Jšœ˜Jšžœ žœžœ7˜JJšœ˜—J˜š  œžœ˜)JšœN™NJšœ žœ˜'Jšœ0˜0Jšœ˜J˜—š  œžœžœžœ˜.Jšœ žœžœ˜Jšœžœ˜J˜šœžœžœžœ˜šžœ žœž˜šžœ žœž˜šœ ˜ Jšœ˜Jšœžœžœžœ˜6Jšžœ žœžœ;˜NJ˜—Jšžœ˜——Jšœ˜Jšœ˜—J˜Jšžœžœžœ˜&Jšœ+˜+J˜šžœž˜ Jšžœ:žœ˜EJš žœžœ žœžœžœ˜>—Jšœ_˜_Jšœ˜J˜—š  œžœ˜(JšœN™NJšœ žœ˜'šœ ˜ šœ%˜%Jšœžœ žœ(˜BJšœžœžœ˜Jšœ˜——Jšœ˜J˜—š’ œžœ žœžœ˜6šœžœžœžœ˜Jšœ˜Jšœ0˜0Jš žœ žœžœžœžœžœ˜4Jšœ˜Jšœ:žœ˜@Jšœ˜—Jšžœžœžœ˜&Jšœ.˜.Jšœ˜J˜—š’ œžœžœžœ˜.Jšžœžœžœ˜šžœž˜ Jšœ7˜7˜Jšœžœžœ˜Jšœžœ ˜šžœžœžœž˜Jšœ#˜#Jšžœžœžœ4žœ˜LJšœ ˜ —Jšžœ˜Jšžœžœžœ(˜:J˜—˜Jšœžœ ˜Jšœžœ˜šžœžœžœ ž˜Jšœžœ˜šžœžœ˜%Jšœ/˜/Jšœ'˜'Jšžœ˜—Jšœ ˜ šžœ˜Jšžœ*žœ˜5—Jšœ ˜ —Jšžœ˜Jšœ'˜'J˜——Jšžœ˜Jšœ˜J˜—š ’œžœžœžœ žœ˜2Jšœžœ0˜;šœ˜šžœ˜Jšžœžœžœ˜.Jšžœžœ!žœ˜8——Jšœ˜J˜—š  œžœ˜(JšœN™NJšœ žœ˜'Jšœ/˜/Jšœ˜J˜—š  œžœ˜&JšœN™NJšœ žœ˜'Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—š œžœ˜$JšœN™NJšœ žœ˜'Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—š œ!žœ˜=Jšœ+™+Jšœ žœ˜'Jšœ!˜!šžœžœžœ˜Jšœžœžœ˜.Jšœžœžœ˜#Jšœ˜—Jšœ˜J˜—š  œžœ˜%JšœN™NJšœ žœ˜'Jšœ,˜,Jšœ˜J˜—š œžœžœ˜"Jšœžœ˜ Jšœžœ˜ šœ"Ÿ"˜DJšžœ˜%—Jšžœžœžœ˜&Jšœ<˜