InterpreterToolImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, April 25, 1985 0:15:51 am PST
Russ Atkinson (RRA) January 9, 1986 5:53:32 pm PST
DIRECTORY
AMBridge USING [FHFromTV, GetWorld, RemoteFHFromTV, TVForReferent, TVForSignal, TVForType, TVToCardinal, TVToLI],
AMEvents USING [BootedNotifier, BreakAfter, BreakAt, BreakID, ClearBreak, DuplicateBreakpoint, Event, EventProc, GetEvents, NextBreak, Outcome, RegisterBootedNotifier, StopEvents, UnRegisterBootedNotifier],
AMMiniModel USING [GetInterfaceRecord],
AMModel USING [Context, ContextName, ContextWorld, MostRecentNamedContext, ParentSection, RootContext, Section, SectionClass, SectionSource, Source],
AMModelBridge USING [ContextForFrame],
AMModelLocation USING [EntryLocations],
AMTypes USING [DynamicParent, Error, GetEmptyTV, TV, TVToName, TVType, Type, TypeClass],
AMViewerOps USING [ReportProc, SectionFromSelection, SourceFromTV, ViewerFromSection],
Atom USING [GetPropFromList],
BackStop USING [Call],
CedarProcess USING [DoWithPriority],
Commander USING [CommandObject, CommandProc, Handle, Register],
EvalQuote USING [EvalQuoteProc, Register],
FileViewerOps USING [OpenSource],
Interpreter USING [AbortProc, Evaluate],
InterpreterOps USING [Eval, GetArg, RegisterTV, Tree, TreeToName, WorldFromHead],
InterpreterToolPrivate USING [Break, BreakIndex, BreakObject, Handle, InterpreterObject, nullBreakIndex],
IO USING [Close, GetLineRope, Put, PutChar, PutF1, PutFR1, PutRope, Reset, RopeFromROS, ROS, STREAM],
List USING [AList, PutAssoc],
MBQueue USING [Create, CreateMenuEntry, Queue],
Menus USING [ChangeNumberOfLines, InsertMenuEntry, Menu, MenuProc],
PrincOps USING [BytePC],
PrintTV USING [Print, PrintArguments, PrintSignal, PrintType, PrintVariables],
Process USING [Detach, GetCurrent],
ProcessProps USING [AddPropList, GetPropList],
Rope USING [Cat, Concat, Fetch, IsEmpty, Length, ROPE, Substr],
RuntimeError USING [UNCAUGHT],
SafeStorage USING [NarrowRefFault],
SymTab USING [Create, Fetch, Ref],
TiogaOps USING [GetSelection, LastLocWithin, SelectPoint, ViewerDoc],
TypeScript USING [Create, Destroy, TS],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [AddProp, EstablishViewerPosition, FetchProp, OpenIcon, PaintViewer],
UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc],
WorldVM USING [GetWorld, LocalWorld, World, WorldName];
InterpreterToolImpl: CEDAR MONITOR -- protects individual interpreters
LOCKS h.LOCK USING h: Handle
IMPORTS AMBridge, AMEvents, AMMiniModel, AMModel, AMModelBridge, AMModelLocation, AMTypes, AMViewerOps, Atom, BackStop, CedarProcess, Commander, EvalQuote, FileViewerOps, Interpreter, InterpreterOps, IO, List, MBQueue, Menus, PrintTV, Process, ProcessProps, Rope, RuntimeError, SafeStorage, SymTab, TiogaOps, TypeScript, UserProfile, ViewerEvents, ViewerIO, ViewerOps, WorldVM
EXPORTS InterpreterToolPrivate -- nextBI
= BEGIN OPEN Interpreter, Rope;
TV: TYPE = AMTypes.TV;
Type: TYPE = AMTypes.Type;
Handle: TYPE = InterpreterToolPrivate.Handle; -- one per viewer
InterpreterObject: TYPE = InterpreterToolPrivate.InterpreterObject;
BreakIndex: TYPE = InterpreterToolPrivate.BreakIndex;
nullBreakIndex: BreakIndex = InterpreterToolPrivate.nullBreakIndex;
nextBI: PUBLIC BreakIndex ← 1; -- BEWARE that these guys aren't protected
dormantH: Handle ← NIL;
BreakObject: TYPE = InterpreterToolPrivate.BreakObject;
Break: TYPE = InterpreterToolPrivate.Break;
NewInterpreter: Commander.CommandProc = TRUSTED {
[cmd: Commander.Handle]
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 AMModel.RootContext[WorldVM.LocalWorld[]]
ELSE AMModelBridge.ContextForFrame[event.frame];
remoteWorld: WorldVM.World ← NIL;
inner: PROC = TRUSTED {
IF context = NIL THEN ERROR;
IF WorldVM.LocalWorld[] # AMModel.ContextWorld[context] THEN
remoteWorld ← AMModel.ContextWorld[context];
IF dormantH = NIL
THEN {
h ← NEW[
InterpreterObject
← [remoteWorld: remoteWorld,
context: context,
event: event,
menuHitQueue: MBQueue.Create[],
symTab: SymTab.Create[],
readEvalPrintProcess: LOOPHOLE[Process.GetCurrent[]]]];
NewViewer[h];
}
ELSE {
name: ROPENIL;
h ← dormantH; -- NOTE unmonitored
dormantH ← NIL;
h.remoteWorld ← remoteWorld;
h.context ← context;
h.event ← event;
h.readEvalPrintProcess ← LOOPHOLE[Process.GetCurrent[]];
IF h.event # NIL
THEN {
IF h.event.world # WorldVM.LocalWorld[] THEN name ← "WORLDSWAP ";
name ← Cat[name, "Event: ", EventToName[h.event]];
}
ELSE name ← Rope.Concat["Interp: ", AMModel.ContextName[h.context]];
h.ts.name ← name;
IF h.ts.iconic THEN ViewerOps.OpenIcon[h.ts];
IF h.event # NIL THEN ChangeLines[h.ts, 2] ELSE ChangeLines[h.ts, 1];
h.tsOutStream.PutRope[Rope.Concat[name, "\n"]];
};
IF remoteWorld # NIL THEN
AMEvents.RegisterBootedNotifier[BootReturnRequested, remoteWorld, h];
outcome ← MainLoop[h];
IF remoteWorld # NIL THEN
AMEvents.UnRegisterBootedNotifier[BootReturnRequested, remoteWorld, h];
};
CedarProcess.DoWithPriority[normal, inner];
};
EventHandler: AMEvents.EventProc = TRUSTED {
[data: REF ANY, event: AMEvents.Event] RETURNS [outcome: AMEvents.Outcome]
h: Handle ← NIL;
nestingLevel: NAT; -- that of saved interp status
context: AMModel.Context = AMModelBridge.ContextForFrame[event.frame];
First, ask if this event is occurring under an interpreter. Ifso, nest the interpreter.
NOTE assumption of order in FindInProgressEvaluation
[h, nestingLevel] ← FindInProgressEvaluation[];
IF h # NIL AND h.nestingLevel = nestingLevel
THEN { -- if this event is "under" an interpreter
[PROCESS, nestingLevel] is in Evaluate, i.e. this event is occurring under an interpreter. Nest the event.
oldRemoteWorld: WorldVM.World = h.remoteWorld; -- save remoteWorld;
oldContext: AMModel.Context = h.context; -- save context;
oldGlobalContext: AMModel.Context = h.globalContext; -- save globalContext;
oldEvent: AMEvents.Event = h.event; -- save event;
oldName: ROPE = h.ts.name; -- save viewer name;
newName: ROPENIL;
IF event.world # WorldVM.LocalWorld[] THEN newName ← "WORLDSWAP ";
newName ← Cat[newName, "Event: ", EventToName[event]];
IO.PutF1[h.tsOutStream, "***Nesting this InterpreterTool to be a handler for %g\n", [rope[newName]] ];
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.globalContext ← NIL;
h.event ← event;
h.ts.name ← newName;
ViewerOps.PaintViewer[h.ts, caption];
h.nestingLevel ← h.nestingLevel + 1;
IF oldRemoteWorld = NIL AND h.remoteWorld # NIL THEN
AMEvents.RegisterBootedNotifier[BootReturnRequested, h.remoteWorld, h];
Now do the recursive call of MainLoop.
outcome ← MainLoop[h];
Return from recursive call of MainLoop. Now pop h's state (i.e. restore h).
IF oldRemoteWorld = NIL AND h.remoteWorld # NIL THEN
AMEvents.UnRegisterBootedNotifier
[BootReturnRequested, h.remoteWorld, h];
h.ts.name ← oldName;
h.nestingLevel ← h.nestingLevel - 1;
h.event ← oldEvent;
h.globalContext ← oldGlobalContext;
h.context ← oldContext;
h.remoteWorld ← oldRemoteWorld;
ViewerOps.PaintViewer[h.ts, caption];
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 ← NIL;
LocalRegister: PROC [name: ROPE, ref: REF, help: ROPE] = TRUSTED {
ENABLE AMTypes.Error => GO TO err;
InterpreterOps.RegisterTV[
name: name, tv: AMBridge.TVForReferent[ref], help: help, symTab: h.symTab
];
EXITS err => {
IO.PutF1[h.tsOutStream, "** Failed to register %g\n", [rope[name]]];
};
};
TRUSTED {
IF h.event # NIL
THEN {
IF h.event.world # WorldVM.LocalWorld[] THEN name ← "WORLDSWAP ";
name ← Cat[name, "Event: ", EventToName[h.event]];
}
ELSE name ← Rope.Concat["Interp: ", 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];
h.tsOutStream.PutRope[Rope.Concat[name, "\n"]];
LocalRegister[
"&H", NEW[Handle ← h], "this interpretertool's handle"];
LocalRegister[
"&depth", NEW[INT ← 4], "printing depth for this interpretertool"];
LocalRegister[
"&width", NEW[INT ← 32], "printing width for this interpretertool"];
LocalRegister[
"&WalkStack", NEW[PROC [nFrames: INT ← 1, h: Handle ← NIL] ← WalkStack],
"set a local frame context (eventhandler)"];
LocalRegister[
"&slf", NEW[PROC [nFrames: INT ← 1, h: Handle ← NIL] ← WalkStack],
"set a local frame context (eventhandler)"];
LocalRegister[
"&Proceed", NEW[PROC [h: Handle ← NIL] ← SetProceedRequested],
"proceed the current event"];
LocalRegister[
"&Abort", NEW[PROC [h: Handle ← NIL] ← SetStopRequested],
"abort the current event"];
LocalRegister[
"&ShowFrame", NEW[PROC [h: Handle ← NIL] ← ShowFrame],
"show the current lf context (eventhandler)"];
LocalRegister[
"&Source", NEW[PROC [h: Handle ← NIL] ← Source],
"show source loc of current lf context (eventhandler)"];
LocalRegister[
"&SetBreak", NEW[PROC [h: Handle ← NIL, exit: BOOLFALSE] ← SetBreak],
"set break at selected source loc (eventhandler)"];
LocalRegister[
"&ClearBreak",
NEW[PROC [h: Handle ← NIL, breakIndex: BreakIndex ← nullBreakIndex] ← ClearBreak],
"clear specified break"];
LocalRegister[
"&ClearAllBreaks",
NEW[PROC [h: Handle ← NIL] ← ClearAllBreaks],
"clear all breaks"];
LocalRegister[
"&ListBreaks",
NEW[PROC [h: Handle ← NIL] ← ListBreaks],
"list all breaks"];
EvalQuote.Register["&sgf", SetGlobalFrameHelper, h.symTab, h];
LocalRegister[
"&sgf",
NEW[PROC [progName: ROPE, h: Handle ← NIL] ← SetGlobalFrameContext],
"&sgf[rope]\tset context to be that for the given module"];
EvalQuote.Register["&gf", GlobalFrameHelper, h.symTab, h];
LocalRegister[
"&gf",
NEW[PROC [progName: ROPE, h: Handle ← NIL] RETURNS [TV] ← GlobalFrameContext],
"&gf[rope]\tget the given module's context"];
EvalQuote.Register["&sw", SetWorldHelper, h.symTab, h];
LocalRegister[
"&sw",
NEW[PROC [worldName: ROPE, h: Handle ← NIL] ← SetWorldContext],
"&sw[rope]\tset context to be that for the given world"];
EvalQuote.Register["&sir", SetIRHelper, h.symTab, h];
LocalRegister[
"&sir",
NEW[PROC [interfaceName: ROPE, h: Handle ← NIL] ← SetIRContext],
"&sir[rope]\tset context to be that for the given IR"];
EvalQuote.Register["&type", TypeHelper, h.symTab, h];
LocalRegister[
"&type",
NEW[PROC [expr: ROPE, h: Handle ← NIL] RETURNS [Type] ← TypeGetter],
"&type[expr]\tget the type of the given expr"];
EvalQuote.Register["&ir", IRHelper, h.symTab, h];
LocalRegister[
"&ir",
NEW[PROC [interfaceName: ROPE, h: Handle ← NIL] RETURNS [TV] ← IRContext],
"&ir[rope]\tget the given IR"];
};
EventToName: PROC [event: AMEvents.Event] RETURNS [name: ROPENIL] = TRUSTED {
Won't raise any signals.
reason: ROPE;
s: IO.STREAMNIL;
WITH e: event SELECT FROM
break => {
[id: BreakID, clientData: REF ANY]
break: Break ← NIL;
IF e.clientData # NIL
THEN break ← NARROW[e.clientData, Break
! SafeStorage.NarrowRefFault => CONTINUE];
IF break = NIL
THEN reason ← "Unknown breakpoint hit."
ELSE reason ← IO.PutFR1["Breakpoint #%g hit.", [integer[break.index]]];
};
call => reason ← Rope.Concat["Call: ", e.msg];
signal => {
[signal, args: TV ]
inner: SAFE PROC = TRUSTED {
PrintTV.PrintSignal[e.signal, e.args, s];
IF AMTypes.TypeClass[AMTypes.TVType[e.signal]] = cardinal THEN
AMEvents gave up on symbols; get the error message for BackStop.Call
[] ← AMBridge.TVForSignal[LOOPHOLE[AMBridge.TVToCardinal[e.signal], ERROR ANY RETURNS ANY]];
};
s ← IO.ROS[];
s.PutRope[BackStop.Call[inner]];
reason ← IO.RopeFromROS[s];
};
unknown => reason ← Rope.Concat["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[", context: "];
s.PutRope[AMModel.ContextName[event.frame]];
s.PutRope[", process: "];
PrintTV.Print[event.process, s, 2, 16];
};
s ← IO.ROS[];
s.PutRope[BackStop.Call[inner]];
name ← Rope.Concat[name, IO.RopeFromROS[s]];
IF Rope.Length[name] > 1000 THEN {
Too bloody long, so truncate it
name ← Rope.Concat[Rope.Substr[name, 0, 1000], "..."];
};
};
};
CreateInterpreterMenu: PROC [h: Handle] = {
menu: Menus.Menu ← h.ts.menu;
InsME: PROC [line: NAT, name: ROPE, proc: Menus.MenuProc] = {
Menus.InsertMenuEntry
[menu: menu,
line: line,
entry: MBQueue.CreateMenuEntry
[q: h.menuHitQueue, name: name, proc: proc, clientData: h]];
};
InsME[line: 0, name: "STOP!", proc: StopHit];
InsME[line: 0, name: "SetBreak", proc: SetBreakHit];
InsME[line: 0, name: "ClearBreak(s)", proc: ClearBreakHit];
InsME[line: 0, name: "ListBreaks", proc: ListBreaksHit];
InsME[line: 1, name: "WalkStack", proc: WalkStackHit];
InsME[line: 1, name: "ShowFrame", proc: ShowFrameHit];
InsME[line: 1, name: "Proceed", proc: ProceedHit];
InsME[line: 1, name: "Abort", proc: AbortHit];
InsME[line: 1, name: "Source", proc: SourceHit];
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];
flushes viewer cache STOPGAP 'till a viewers bug is fixed
ViewerOps.PaintViewer[v, all];
};
GetIntFromTable: PROC [h: Handle, name: ROPE, default: INT] RETURNS [INT] = TRUSTED {
WITH SymTab.Fetch[h.symTab, name].val SELECT FROM
tv: TV => RETURN [AMBridge.TVToLI[tv ! AMTypes.Error => CONTINUE]];
ENDCASE;
RETURN [default];
};
MainLoop: PROC [h: Handle] RETURNS [outcome: AMEvents.Outcome ← [proceed[NIL]]] = {
Read/Eval/Print loop. FORK'd for each interpreter viewer. Called recursively if an error is raised during interpretation.
ENABLE UNWIND => h.event ← NIL;
somethingHappened: BOOLTRUE;
out: IO.STREAM ← h.tsOutStream;
out.PutChar['\n];
UNTIL WasTerminateRequested[h] DO
the interpreter viewer was destroyed
line: ROPENIL;
result: TVNIL;
noResult: BOOLTRUE;
errorRope: ROPENIL;
prompt: ROPEIO.PutFR1["&%g ← ", [integer[NextVarIndex[h, somethingHappened]]]];
leader: ROPENIL;
printType: BOOLFALSE;
depth: INT ← GetIntFromTable[h, "&depth", 4];
width: INT ← GetIntFromTable[h, "&width", 32];
innerPrint: SAFE PROC = TRUSTED {
IF noResult THEN RETURN;
IO.PutRope[out, "=> "];
IF printType
THEN {
IO.PutRope[out, "(type) "];
PrintTV.PrintType[
put: out,
type: AMTypes.TVType[result],
depth: depth,
width: width,
verbose: depth > 4
];
}
ELSE PrintTV.Print[
put: out,
tv: result,
depth: depth,
width: width,
verbose: depth > 4
];
};
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 {ResetBootReturnRequested[h]; SetDormant[h]; h.event ← NIL};
RETURN[[quit[]]];
}
ELSE ResetBootReturnRequested[h];
};
FOR i: NAT IN [0..h.nestingLevel) DO leader ← Rope.Concat[leader, "<***>"]; ENDLOOP;
out.PutRope[leader ! RuntimeError.UNCAUGHT => CONTINUE];
out.PutRope[prompt ! RuntimeError.UNCAUGHT => CONTINUE];
{
line ← h.tsInStream.GetLineRope[
! RuntimeError.UNCAUGHT => GOTO handleAborted;
];
EXITS handleAborted => {
ENABLE RuntimeError.UNCAUGHT => CONTINUE;
h.tsInStream.Reset[];
out.PutRope[" XXX\n"];
};
};
SetStuffable[h];
IF line.IsEmpty[] THEN LOOP;
UNTIL line.IsEmpty[] DO
strip postfix !'s and ?'s
ch: CHAR ← line.Fetch[line.Length[] - 1];
SELECT ch FROM
'! => {depth ← depth + 1; width ← width + width};
'? => printType ← TRUE;
ENDCASE => EXIT;
line ← Rope.Substr[base: line, len: line.Length[] - 1];
ENDLOOP;
line ← Rope.Concat[line, "\n"];
somethingHappened ← TRUE;
ResetStopRequested[h];
{
innerEval: SAFE PROC = TRUSTED {
[result, errorRope, noResult]
← Interpreter.Evaluate
[rope: Rope.Concat[prompt, line],
context: IF h.globalContext # NIL THEN h.globalContext ELSE h.context,
symTab: h.symTab,
abort: [abortProc, h]
! ABORTED, UNWIND => {errorRope ← " XXX\n"; CONTINUE};
];
};
ch: Commander.Handle = NEW [Commander.CommandObject ← [
in: h.tsInStream,
out: out,
err: out,
commandLine: line,
command: "←",
propertyList: NIL]];
props: List.AList ← NIL;
props ← List.PutAssoc[$CommanderHandle, ch, props];
props ← List.PutAssoc[$InterpreterHandle, h, props];
props ← List.PutAssoc[$InterpreterNestingLevel, NEW[NAT ← h.nestingLevel], props];
props ← List.PutAssoc[$InterpreterSymTab, h.symTab, props];
ProcessProps.AddPropList[props, innerEval];
};
IF errorRope.Length[] # 0
THEN IO.PutF1[out, "*** %g\n", [rope[errorRope]] ]
ELSE IO.PutF1[out, " %g\n", [rope[BackStop.Call[innerPrint]]] ];
ENDLOOP;
Here if the interpreter viewer was destroyed. h.tsInStream will have been closed.
IF h.nestingLevel > 0 THEN RETURN[[quit[]]] ELSE Finalize[h];
};
abortProc: Interpreter.AbortProc = TRUSTED {
PROC [data: REF] RETURNS [abort: BOOL];
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: PUBLIC PROC RETURNS [Handle] = {
r: REF = Atom.GetPropFromList[ProcessProps.GetPropList[], $InterpreterHandle];
RETURN[NARROW[r, Handle]];
};
Finalize: PROC [h: Handle] = {
Here to finalize an InterpreterObject (e.g. to break circular structures, close streams)
out: IO.STREAM ← h.tsOutStream;
ts: TypeScript.TS ← h.ts;
h.context ← NIL;
h.globalContext ← NIL;
h.event ← NIL;
h.symTab ← NIL;
h.ts ← NIL;
h.tsInStream ← NIL;
h.tsOutStream ← NIL;
h.menuHitQueue ← NIL;
IF h = dormantH THEN dormantH ← NIL;
{
ENABLE RuntimeError.UNCAUGHT => CONTINUE;
IF out # NIL THEN
IO.PutRope[out, "\n\n ~~~~~~~~~~~~~TERMINATED~~~~~~~~~~~~~ \n\n"];
IF ts # NIL THEN TypeScript.Destroy[ts];
IF out # NIL THEN IO.Close[out];
};
};
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: PROC [h: Handle] = {
ENABLE UNWIND => NULL;
h.stopRequested ← TRUE;
StuffIt[h, "\n"];
};
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;
abort: BOOLFALSE;
h.readEvalPrintProcess ← NIL;
h.tsOutStream.PutRope["\n~~~~~~~~~~~~~~~~~~~~~\n"
! RuntimeError.UNCAUGHT => CONTINUE];
ChangeLines[h.ts, 0 ! RuntimeError.UNCAUGHT => {abort ← TRUE; CONTINUE}];
IF NOT abort THEN dormantH ← h;
};
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[ ! RuntimeError.UNCAUGHT => CONTINUE];
IF h = dormantH THEN dormantH ← NIL;
h.terminateRequested ← TRUE;
};
WasTerminateRequested: ENTRY PROC [h: Handle] RETURNS [BOOL] = {
ENABLE UNWIND => NULL;
RETURN[h.terminateRequested];
};
StopHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
SetStopRequested[NARROW[clientData, Handle]];
};
HighlightBreakPoint: PROC [break: Break, err: IO.STREAM] = {
report: AMViewerOps.ReportProc = TRUSTED {err.PutRope[msg]};
[msg: ROPE, severity: Severity]
inner: SAFE PROC = TRUSTED {
[] ← AMViewerOps.ViewerFromSection[break.section, report];
}; -- yekk.
msg: ROPE ← BackStop.Call[inner];
IF msg # NIL THEN err.PutRope[msg];
};
SetBreakHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
SELECT mouseButton FROM
blue => StuffIt[h, " ---- &SetBreak[h: &H, exit: TRUE]\n"];
ENDCASE => StuffIt[h, " ---- &SetBreak[h: &H]\n"];
};
SetBreak: PROC [h: Handle ← NIL, exit: BOOLFALSE] = TRUSTED {
break: Break ← NIL;
msg: ROPENIL;
err: ROPENIL;
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 --"];
SELECT AMModel.SectionClass[section] FROM
proc => {};
statement => {
IF exit THEN section ← AMModel.ParentSection[section];
};
ENDCASE => {
err ← "-- section is not valid --";
RETURN;
};
break ← NEW[BreakObject ← [index: 0, breakID: NIL, world: world, section: section]];
{ENABLE AMEvents.DuplicateBreakpoint => GO TO bogus;
break.breakID ← IF exit
THEN AMEvents.BreakAfter[world, section, break]
ELSE AMEvents.BreakAt[world, section, break];
break.index ← nextBI;
nextBI ← nextBI + 1;
EXITS bogus => {
break ← NIL;
err ← "-- duplicate breakpoints not allowed --";
};
};
};
IF h = NIL THEN h ← GetHandlePlease[];
world ← IF h.event # NIL THEN h.event.world ELSE AMModel.ContextWorld[h.context];
msg ← BackStop.Call[inner];
IF msg # NIL THEN err ← msg;
IF err.Length[] = 0 AND break # NIL THEN {
h.tsOutStream.PutF1["Break #%g set.", [integer[break.index]]];
HighlightBreakPoint[break: break, err: h.tsOutStream];
RETURN;
};
h.tsOutStream.PutRope[err];
};
ListBreaksHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
StuffIt[h, " ---- &ListBreaks[h: &H]\n"];
};
SetGlobalFrameContext: PROC [progName: ROPE, h: Handle ← NIL] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
GlobalFrameContext: PROC [progName: ROPE, h: Handle ← NIL] RETURNS [TV] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
SetWorldContext: PROC [worldName: ROPE, h: Handle ← NIL] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
SetIRContext: PROC [interfaceName: ROPE, h: Handle ← NIL] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
IRContext: PROC [interfaceName: ROPE, h: Handle ← NIL] RETURNS [TV] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
TypeGetter: PROC [expr: ROPE, h: Handle ← NIL] RETURNS [Type] = TRUSTED {
ERROR -- defined only for &help; the evalquote proc does the work
};
ListBreaks: PROC [h: Handle ← NIL] = TRUSTED {
msg: ROPENIL;
out: IO.STREAMNIL;
inner: SAFE PROC = TRUSTED {
id: AMEvents.BreakID ← NIL;
clientData: REF;
DO
world: WorldVM.World
= IF h.event # NIL THEN h.event.world ELSE AMModel.ContextWorld[h.context];
[id, clientData] ← AMEvents.NextBreak[world: world, prev: id];
IF id = NIL THEN EXIT;
WITH clientData SELECT FROM
b: Break => {
source: AMModel.Source = AMModel.SectionSource[b.section];
pc: PrincOps.BytePC = AMModelLocation.EntryLocations[b.section].list.first.pc;
firstCharIndex: INT ← 0;
WITH s: source SELECT FROM
field => firstCharIndex ← s.firstCharIndex;
ENDCASE;
IF b.world # world THEN {out.PutRope["DISASTER"]; ERROR};
out.PutF1[" Break #%g at ", [integer[b.index]]];
out.Put[[rope[source.fileName]], [rope["["]], [integer[firstCharIndex]]];
out.PutF1["], bytePC = %bb\n", [cardinal[pc]]];
};
ENDCASE;
ENDLOOP;
};
START HERE
IF h = NIL THEN h ← GetHandlePlease[];
out ← h.tsOutStream;
out.PutRope["Listing all breaks for this world...\n"];
msg ← BackStop.Call[inner];
IF msg # NIL
THEN out.PutF1["Error: %g\n", [rope[msg]]]
ELSE out.PutRope["...Done.\n"];
};
ClearBreakHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
SELECT mouseButton FROM
red => StuffIt[h, " ---- &ClearBreak[h: &H]\n"];
blue => StuffIt[h, " ---- &ClearAllBreaks[h: &H]\n"];
ENDCASE;
};
ClearAllBreaks: PROC [h: Handle ← NIL] = TRUSTED {
msg: ROPENIL;
out: IO.STREAMNIL;
inner: SAFE PROC = TRUSTED {
id: AMEvents.BreakID ← NIL;
clientData: REF;
DO
world: WorldVM.World
= IF h.event # NIL THEN h.event.world ELSE AMModel.ContextWorld[h.context];
[id, clientData] ← AMEvents.NextBreak[world: world, prev: id];
IF id = NIL THEN EXIT;
WITH clientData SELECT FROM
b: Break => {
IF b.world # world THEN {h.tsOutStream.PutRope["DISASTER"]; ERROR};
AMEvents.ClearBreak[b.breakID];
out.Put[[rope[" Break #"]], [integer[b.index]], [rope[" cleared.\n"]] ];
};
ENDCASE;
ENDLOOP;
};
START HERE
IF h = NIL THEN h ← GetHandlePlease[];
out ← h.tsOutStream;
out.PutRope["Clearing all breaks...\n"];
msg ← BackStop.Call[inner];
IF msg # NIL
THEN out.PutF1["Error: %g\n", [rope[msg]]]
ELSE out.PutRope["...Done.\n"];
};
ClearBreak: PROC [h: Handle ← NIL, breakIndex: BreakIndex ← nullBreakIndex] = TRUSTED {
msg, err: ROPENIL;
break: Break ← NIL;
out: IO.STREAMNIL;
inner: SAFE PROC = TRUSTED {
IF breakIndex = nullBreakIndex
THEN {
clear the break that caused this event
IF h.event = NIL
THEN err ← "not an event handler"
ELSE
WITH e: h.event SELECT FROM
break => {
AMEvents.ClearBreak[e.id];
WITH e.clientData SELECT FROM
br: Break => break ← br;
ENDCASE;
IF break = NIL
THEN err ← "I didn't place this break but I cleared it anyhow"
ELSE breakIndex ← break.index;
};
ENDCASE => err ← "not a break event";
}
ELSE {
clear the specified break
id: AMEvents.BreakID ← NIL;
clientData: REF;
DO
world: WorldVM.World
= IF h.event # NIL THEN h.event.world ELSE AMModel.ContextWorld[h.context];
[id, clientData] ← AMEvents.NextBreak[world: world, prev: id];
IF id = NIL THEN {err ← "No break found with the specified index"; EXIT};
WITH clientData SELECT FROM
b: Break => {
IF b.world # world THEN {h.tsOutStream.PutRope["DISASTER"]; ERROR};
IF b.index = breakIndex THEN {
break ← b;
AMEvents.ClearBreak[b.breakID];
};
};
ENDCASE;
ENDLOOP;
};
};
START HERE
IF h = NIL THEN h ← GetHandlePlease[];
out ← h.tsOutStream;
IO.PutRope[out, "Clearing break..."];
msg ← BackStop.Call[inner];
SELECT TRUE FROM
msg # NIL => IO.PutF1[out, "Break not cleared: %g", [rope[msg]] ];
break = NIL => IO.PutRope[out, err];
ENDCASE => IO.PutF1[out, "Break #%g cleared.", [integer[breakIndex]] ];
};
WalkStackHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
SELECT mouseButton FROM
red => StuffIt[h, " ---- &WalkStack[nFrames: 1, h: &H]\n"];
yellow => StuffIt[h, " ---- &WalkStack[nFrames: -1000, h: &H]\n"];
blue => StuffIt[h, " ---- &WalkStack[nFrames: -1, h: &H]\n"];
ENDCASE => ERROR;
};
WalkStack: PROC [nFrames: INT ← 1, h: Handle ← NIL] = {
inner: SAFE PROC = TRUSTED {
IF h.event = NIL OR h.event.frame = NIL THEN RETURN;
WalkContext[h, nFrames];
PrintTV.Print[tv: h.context, put: h.tsOutStream, depth: 1, verbose: TRUE];
};
IF h = NIL THEN h ← GetHandlePlease[];
h.tsOutStream.PutRope[BackStop.Call[inner]];
};
WalkContext: ENTRY PROC [h: Handle, n: INT] = TRUSTED {
ENABLE UNWIND => NULL;
h.globalContext ← NIL;
SELECT n FROM
> 0 => {
next: TVNIL;
this: TV ← h.context;
THROUGH [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 ← AMModelBridge.ContextForFrame[next];
};
< -999 => h.context ← AMModelBridge.ContextForFrame[h.event.frame];
< 0 => {
this: TV ← h.context;
prev: TV ← h.event.frame;
THROUGH [1..-n] DO
t: TV;
IF FHBits[prev] = FHBits[this] THEN {
h.tsOutStream.PutRope["Can't go any further."];
h.context ← AMModelBridge.ContextForFrame[prev];
RETURN};
t ← AMTypes.DynamicParent[prev];
UNTIL FHBits[t] = FHBits[this]
DO prev ← t; t ← AMTypes.DynamicParent[prev] ENDLOOP;
this ← prev;
ENDLOOP;
h.context ← AMModelBridge.ContextForFrame[prev];
};
ENDCASE;
};
FHBits: PROC [lf: TV] RETURNS [fhBits: CARDINAL] = TRUSTED {
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 {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
StuffIt[h, " ---- &ShowFrame[h: &H]\n"];
};
ProceedHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
SetProceedRequested[h];
StuffIt[h, "\n"];
};
AbortHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
SetAbortRequested[h];
StuffIt[h, "\n"];
};
BootReturnRequested: AMEvents.BootedNotifier = TRUSTED {
[world: WorldVM.World, clientData: REF]
h: Handle = NARROW[clientData, Handle];
SetBootReturnRequested[h, world];
StuffIt[h, "\n"];
};
SourceHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle = NARROW[clientData, Handle];
StuffIt[h, " ---- &Source[h: &H]\n"];
};
Source: PROC [h: Handle ← NIL] = {
name: ROPE;
index: INT;
report: AMViewerOps.ReportProc = TRUSTED {
[msg: ROPE, severity: Severity]
h.tsOutStream.PutRope[msg]};
IF h = NIL THEN h ← GetHandlePlease[];
[name, index] ← AMViewerOps.SourceFromTV[h.context, report];
IF name # NIL THEN
FileViewerOps.OpenSource[fileName: name, index: index, feedBack: h.tsOutStream];
};
ViewerEvent: ViewerEvents.EventProc = {
[viewer: Viewer, event: ViewerEvent, before: BOOL] RETURNS [abort: BOOLFALSE]
SELECT event FROM
destroy =>
WITH ViewerOps.FetchProp[viewer, $InterpreterHandle] SELECT FROM
h: Handle => {
IF h.event # NIL THEN RETURN [TRUE];
IF NOT h.terminateRequested THEN TRUSTED {
SetTerminateRequested[h];
Process.Detach[FORK StuffIt[h, "\n"]];
};
IF h = dormantH THEN dormantH ← NIL;
};
ENDCASE;
ENDCASE;
};
ShowFrame: PROC [h: Handle ← NIL] = {
lf: TV;
depth: INT ← 4;
width: INT ← 32;
IF h = NIL THEN h ← GetHandlePlease[];
IF h.event = NIL OR h.event.frame = NIL OR h.globalContext # NIL THEN RETURN;
depth ← GetIntFromTable[h, "&depth", depth];
width ← GetIntFromTable[h, "&width", width];
lf ← h.context;
PrintTV.Print[tv: lf, put: h.tsOutStream, depth: 1, verbose: TRUE];
h.tsOutStream.PutRope["\nArguments--\n"];
PrintTV.PrintArguments[tv: lf, put: h.tsOutStream, depth: depth, width: width, breakBetweenItems: TRUE];
h.tsOutStream.PutRope["\nVariables--\n"];
PrintTV.PrintVariables[tv: lf, put: h.tsOutStream, depth: depth, width: width, all: TRUE, breakBetweenItems: TRUE];
};
worldSwapDebug: BOOL;
SetWorldSwapDebug: UserProfile.ProfileChangedProc = TRUSTED {
[reason: ProfileChangeReason]
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;
};
};
TypeHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
arg: InterpreterOps.Tree ← InterpreterOps.GetArg[tree, 1];
return ← AMBridge.TVForType[AMTypes.TVType[InterpreterOps.Eval[arg, head, target]]];
};
IRHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
arg: InterpreterOps.Tree ← InterpreterOps.GetArg[tree, 1];
rope: ROPE ← InterpreterOps.TreeToName[arg];
IF rope = NIL THEN rope ← AMTypes.TVToName[InterpreterOps.Eval[arg, head, target]];
return ← AMMiniModel.GetInterfaceRecord[rope, InterpreterOps.WorldFromHead[head]];
};
SetIRHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
h: Handle = NARROW[data, Handle];
return ← IRHelper[head, tree, target, data];
h.tsOutStream.PutRope["***Setting interface record context...\n"];
h.globalContext ← return;
};
SetWorldHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
context: AMModel.Context;
world: WorldVM.World;
h: Handle ← NARROW[data];
arg: InterpreterOps.Tree ← InterpreterOps.GetArg[tree, 1];
rope: ROPE ← InterpreterOps.TreeToName[arg];
IF rope = NIL THEN rope ← AMTypes.TVToName[InterpreterOps.Eval[arg, head, target]];
return ← AMTypes.GetEmptyTV[];
world ← WorldVM.GetWorld[rope];
h.tsOutStream.PutRope["***Setting World context...\n"];
h.tsOutStream.PutRope[WorldVM.WorldName[world]];
context ← AMModel.RootContext[world];
h.globalContext ← context;
};
GlobalFrameHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
arg: InterpreterOps.Tree ← InterpreterOps.GetArg[tree, 1];
rope: ROPE ← InterpreterOps.TreeToName[arg];
IF rope = NIL THEN rope ← AMTypes.TVToName[InterpreterOps.Eval[arg, head, target]];
return ← AMModel.MostRecentNamedContext[
rope,
AMModel.RootContext[InterpreterOps.WorldFromHead[head]]
];
};
SetGlobalFrameHelper: EvalQuote.EvalQuoteProc = TRUSTED {
[head: EvalHead, tree: Tree, target: Type, data: REF] RETURNS [return: TV]
h: Handle ← NARROW[data];
return ← GlobalFrameHelper[head, tree, target, data];
h.tsOutStream.PutRope["***Setting Global Frame context...\n"];
h.globalContext ← return;
};
START HERE
[] ← ViewerEvents.RegisterEventProc[proc: ViewerEvent, event: destroy];
Commander.Register[key: "///Commands/Interpreter", proc: NewInterpreter, doc: "Create a new interpreter tool"];
UserProfile.CallWhenProfileChanges[SetWorldSwapDebug];
END.