ReadEvalPrintImpl.mesa
A comman read-eval-print loop for use with viewers. Original version by P. Rovner March 30, 1983 3:34 pm
Last Edited by L. Stewart, December 2, 1983 10:35 pm
Russ Atkinson, September 26, 1983 11:16 pm
Last Edited by Paul Rovner, November 29, 1983 8:44 am
DIRECTORY
AMEvents USING [Debugged, Debugging],
EditedStream USING [DeliverWhenProc, SetDeliverWhen, Rubout, IsACR],
IO USING [
Close, -- EndOf, --EndOfStream, Error, GetTokenRope, PeekChar, PutF, PutRope, Reset, STREAM, EndOf, BreakProc],
List USING [AList],
MBQueue USING [Create, CreateMenuEntry, Queue],
Menus USING [InsertMenuEntry, MenuProc],
Process USING [Detach, GetCurrent, Abort],
ProcessProps USING [PushPropList],
ReadEvalPrint,
Rope USING [ROPE, Concat, Length, Fetch],
RuntimeError USING [UNCAUGHT],
TypeScript USING [Create],
ViewerClasses USING [Viewer, ViewerRec],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [AddProp, ComputeColumn, FetchProp];
ReadEvalPrintImpl: CEDAR MONITOR
IMPORTS
AMEvents, EditedStream, IO, MBQueue, Menus, Process, ProcessProps, Rope, RuntimeError, TypeScript, ViewerEvents, ViewerIO, ViewerOps
EXPORTS ReadEvalPrint
= BEGIN
CreateViewerEvaluator: PUBLIC PROC [clientProc: ReadEvalPrint.ClientProc, prompt: Rope.ROPENIL, info: ViewerClasses.ViewerRec ← [], edited: BOOLTRUE, deliverWhen: EditedStream.DeliverWhenProc ← NIL, clientData: REF ANYNIL, topLevel: BOOLTRUE] RETURNS [h: ReadEvalPrint.Handle] = {
h ← NEW[ReadEvalPrint.RObject ← []];
h.clientProc ← clientProc;
IF deliverWhen = NIL THEN deliverWhen ← EditedStream.IsACR;
h.deliverWhenProc ← deliverWhen;
h.menuHitQueue ← MBQueue.Create[];
h.prompt ← prompt;
h.viewer ← TypeScript.Create[info: info, paint: FALSE];
h.clientData ← clientData;
h.topLevel ← topLevel;
ViewerOps.AddProp[h.viewer, $ReadEvalPrint, h];
[in: h.in, out: h.out]
← ViewerIO.CreateViewerStreams[name: NIL, viewer: h.viewer, editedStream: edited];
IF edited THEN EditedStream.SetDeliverWhen[h.in, deliverWhen];
Menus.InsertMenuEntry
[menu: h.viewer.menu, line: 0, entry: MBQueue.CreateMenuEntry[q: h.menuHitQueue,
name: "STOP!", proc: StopHit, clientData: h]];
ViewerOps.ComputeColumn[column: info.column];
};
CreateStreamEvaluator: PUBLIC PROC [clientProc: ReadEvalPrint.ClientProc, prompt: Rope.ROPENIL, in, out: IO.STREAM, deliverWhen: EditedStream.DeliverWhenProc ← NIL, clientData: REF ANYNIL, topLevel: BOOLFALSE] RETURNS [h: ReadEvalPrint.Handle] = {
h ← NEW[ReadEvalPrint.RObject ← []];
h.clientProc ← clientProc;
IF deliverWhen = NIL THEN deliverWhen ← EditedStream.IsACR;
h.deliverWhenProc ← deliverWhen;
h.prompt ← prompt;
h.in ← in;
h.out ← out;
h.clientData ← clientData;
h.topLevel ← topLevel;
};
Stop: PUBLIC ENTRY PROC [h: ReadEvalPrint.Handle] = {
ENABLE UNWIND => NULL;
IF h.in # NIL THEN h.in.Close[! RuntimeError.UNCAUGHT => CONTINUE];
h.terminateRequested ← TRUE;
};
MainLoop: PUBLIC PROC [h: ReadEvalPrint.Handle, forkAndDetach: BOOLTRUE, properties: List.AList] = {
IF forkAndDetach THEN TRUSTED { Process.Detach[FORK Base[props: properties, rep: h]]; }
ELSE Base[props: properties, rep: h];
};
Base: PROC [props: List.AList, rep: ReadEvalPrint.Handle] = {
inner: PROC = { MainLoopInternal[rep]; };
IF props # NIL THEN ProcessProps.PushPropList[props, inner]
ELSE inner[];
};
MainLoopInternal: PROC [h: ReadEvalPrint.Handle] = {
commandLine: Rope.ROPENIL;
result: Rope.ROPENIL;
rejectThisOne: BOOLFALSE;
badRope: Rope.ROPENIL; -- used to save for debugger inspection
TRUSTED{h.mainLoopProcess ← LOOPHOLE[Process.GetCurrent[], SAFE PROCESS]};
UNTIL WasTerminateRequested[h] DO
{--1--ENABLE RuntimeError.UNCAUGHT => {rejectThisOne ← FALSE; REJECT};
{--2--ENABLE {-- these catch phrases protect the input operation only
AMEvents.Debugging, AMEvents.Debugged => REJECT;
ABORTED => { -- took a breakpoint and then aborted
IF rejectThisOne OR NOT h.topLevel THEN REJECT;
GOTO HandleAborted;
};
UNWIND => IF NOT h.topLevel THEN REJECT ELSE GOTO DoLoop;
RuntimeError.UNCAUGHT => REJECT;
IF h.bulletProof THEN { -- otherwise reject
Inform: PROCEDURE = {
ENABLE RuntimeError.UNCAUGHT => GO TO localOut;
h.in.Reset[];
h.out.PutRope["\n"];
AMIO.PutSignal[h.out];
h.out.PutRope["\n"];
EXITS localOut => {};
};
IF rejectThisOne THEN REJECT;
Inform[! RuntimeError.UNCAUGHT => CONTINUE];
GOTO DoLoop;
};
}; -- end "these catch phrases protect the input operation only"
{--3--ENABLE {
IO.Error => {
IF rejectThisOne THEN REJECT;
IF ec = StreamClosed THEN {
IF h.viewer = NIL OR h.viewer.destroyed THEN GOTO Destroyed
} -- user destroyed the viewer.
ELSE IF ec = SyntaxError THEN {
h.in.Reset[ ! RuntimeError.UNCAUGHT => CONTINUE];
h.out.PutRope["Input Syntax Error" ! RuntimeError.UNCAUGHT => CONTINUE];
GOTO DoLoop;
}; -- user destroyed the viewer.
};
EditedStream.Rubout => {
IF rejectThisOne THEN REJECT;
IF h.ruboutProc # NIL THEN h.ruboutProc[h]
ELSE {
h.in.Reset[ ! RuntimeError.UNCAUGHT => CONTINUE];
h.out.PutF[IF h.readIOSignalRope # NIL THEN h.readIOSignalRope ELSE " XXX\n" ! RuntimeError.UNCAUGHT => CONTINUE];
};
GOTO DoLoop;
};
IO.EndOfStream => GOTO Destroyed;
}; -- end "3" catch phrases
IF result # NIL
THEN {
h.out.PutF[result
! RuntimeError.UNCAUGHT => {
badRope ← result;
result ← NIL;
rejectThisOne ← TRUE;
REJECT}];
h.out.PutRope["\n" ! RuntimeError.UNCAUGHT => CONTINUE];
result ← NIL;
};
IF h.in.EndOf[ ! RuntimeError.UNCAUGHT => GOTO Destroyed] THEN GOTO Destroyed;
h.out.PutF[h.prompt
! RuntimeError.UNCAUGHT => {
badRope ← result;
rejectThisOne ← TRUE;
h.prompt ← NIL;
REJECT}];
IF h.promptProc # NIL
THEN h.promptProc[h
! RuntimeError.UNCAUGHT => {
rejectThisOne ← TRUE;
REJECT}];
[] ← h.in.PeekChar[];
wait until there's stuff to read, else GetTokenRope will raise EndOfStream
{--4--
breakProc: IO.BreakProc = {
IF h.deliverWhenProc[char, NIL, h.in, NIL].activate
THEN RETURN[break] ELSE RETURN[other];
};
commandLine ← h.in.GetTokenRope[breakProc
! RuntimeError.UNCAUGHT => {
rejectThisOne ← TRUE;
REJECT;
}].token;
IF commandLine.Length[] # 1
OR NOT h.deliverWhenProc[commandLine.Fetch[0], NIL, h.in, NIL].activate
THEN
commandLine
← Rope.Concat[
commandLine,
h.in.GetTokenRope[breakProc
! RuntimeError.UNCAUGHT => {
rejectThisOne ← TRUE;
REJECT;
}].token];
break char (e.g. CR) included; won't raise EndOfStream
}; -- end 4
}; -- end 3
EXITS
DoLoop => LOOP;
HandleAborted => {
h.in.Reset[
! RuntimeError.UNCAUGHT => CONTINUE];
h.out.PutF[IF h.readABORTEDRope # NIL THEN h.readABORTEDRope ELSE " aborted\n"
! RuntimeError.UNCAUGHT => CONTINUE];
LOOP;
};
}; -- end 2
}; -- end of input block (1)
TRUSTED {
result ← h.clientProc[h, commandLine !
ABORTED => {
IF NOT h.topLevel THEN REJECT
ELSE {
result ← IF h.evalABORTEDRope # NIL THEN h.evalABORTEDRope ELSE " ...Aborted\n";
CONTINUE;
};
};
UNWIND => {
IF NOT h.topLevel THEN REJECT
ELSE {
result ← IF h.evalUNWINDRope # NIL THEN h.evalUNWINDRope ELSE " ...Unwound\n";
CONTINUE;
};
};
];
};
ENDLOOP;
EXITS
Destroyed => NULL;
}; -- end MainLoopInternal
Implementation private procedures
WasTerminateRequested: ENTRY PROC [h: ReadEvalPrint.Handle] RETURNS [BOOL] = {
ENABLE UNWIND => NULL;
RETURN[h.terminateRequested];
};
StopHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: ReadEvalPrint.Handle ← NARROW[clientData, ReadEvalPrint.Handle];
in: IO.STREAM ← h.in;
Process.Abort[h.mainLoopProcess];
};
ViewerEvent: ViewerEvents.EventProc = TRUSTED {
SELECT event FROM
destroy => {
prop: REF ← ViewerOps.FetchProp[viewer, $ReadEvalPrint];
IF prop # NIL THEN {
Stop[NARROW[prop, ReadEvalPrint.Handle]];
};
};
ENDCASE;
};
Init: PROC = {
[] ← ViewerEvents.RegisterEventProc[proc: ViewerEvent, event: destroy, filter: $Typescript];
};
Initialization
Init[];
END.