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, April 15, 1983 11:38 am
DIRECTORY
AMEvents USING [Debugged, Debugging],
IO USING [Close, ChangeDeliverWhen, CharProc, CharsAvail, DeliverWhenProc, EndOf, EndOfStream, Error, GetSequence, NewLine, PeekChar, PutF, PutRope, PutSignal, Reset, ResetUserAbort, rope, SetUserAbort, Signal, STREAM, SyntaxError, UserAborted],
MBQueue USING [Create, CreateMenuEntry, Queue],
Menus USING [InsertMenuEntry, MenuProc],
Process USING [Detach],
ReadEvalPrint,
Rope USING [ROPE],
TypeScript USING [Create],
ViewerClasses USING [Viewer, ViewerRec],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerIO USING [CreateViewerStreams, NotAllowed],
ViewerOps USING [AddProp, ComputeColumn, FetchProp];
ReadEvalPrintImpl: CEDAR MONITOR
IMPORTS AMEvents, IO, MBQueue, Menus, Process, TypeScript, ViewerEvents, ViewerIO, ViewerOps
EXPORTS ReadEvalPrint
SHARES ViewerIO =
BEGIN
CreateViewerEvaluator: PUBLIC PROC [clientProc: ReadEvalPrint.ClientProc, prompt: Rope.ROPENIL, info: ViewerClasses.ViewerRec ← [], edited: BOOLTRUE, deliverWhen: IO.DeliverWhenProc ← IsACR, clientData: REF ANYNIL] RETURNS [h: ReadEvalPrint.Handle] = {
h ← NEW[ReadEvalPrint.RObject ← []];
h.clientProc ← clientProc;
h.deliverWhenProc ← NIL;
h.menuHitQueue ← MBQueue.Create[];
h.prompt ← prompt;
h.viewer ← TypeScript.Create[info: info, paint: FALSE];
h.clientData ← clientData;
ViewerOps.AddProp[h.viewer, $ReadEvalPrint, h];
[in: h.in, out: h.out] ← ViewerIO.CreateViewerStreams[name: NIL, viewer: h.viewer, editedStream: edited];
[] ← h.in.ChangeDeliverWhen[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: IO.DeliverWhenProc ← NIL, clientData: REF ANYNIL] RETURNS [h: ReadEvalPrint.Handle] = {
h ← NEW[ReadEvalPrint.RObject ← []];
h.clientProc ← clientProc;
h.deliverWhenProc ← deliverWhen;
h.prompt ← prompt;
h.in ← in;
h.out ← out;
h.clientData ← clientData;
};
Stop: PUBLIC PROC [h: ReadEvalPrint.Handle] = {
SetTerminateRequested[h];
};
MainLoop: PUBLIC PROC [h: ReadEvalPrint.Handle, forkAndDetach: BOOLTRUE] = {
IF forkAndDetach THEN TRUSTED { Process.Detach[FORK MainLoopInternal[h]]; }
ELSE MainLoopInternal[h];
};
MainLoopInternal: PROC [h: ReadEvalPrint.Handle] = {
commandLine: Rope.ROPENIL;
result: Rope.ROPENIL;
rejectThisOne: BOOLFALSE;
badRope: Rope.ROPE ← NIL; -- used to save for debugger inspection
UNTIL WasTerminateRequested[h] DO
{ ENABLE ANY => {
rejectThisOne ← FALSE;
REJECT;
};
{ENABLE {-- these catch phrases protect the input operation only
AMEvents.Debugging, AMEvents.Debugged => REJECT;
IO.UserAborted => { -- user typed ctrl-del
s: IO.STREAM ← h.in;
IF rejectThisOne THEN REJECT;
WITH abortee SELECT FROM
a: IO.STREAM => s ← a;
ENDCASE;
s.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.PutF[IF h.readUserAbortedRope # NIL THEN h.readUserAbortedRope ELSE " XXX\n" ! ANY => CONTINUE];
GOTO DoLoop;
};
ABORTED => { -- took a breakpoint and then aborted
IF rejectThisOne THEN REJECT;
h.in.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.PutF[IF h.readABORTEDRope # NIL THEN h.readABORTEDRope ELSE " aborted\n" ! ANY => CONTINUE];
GOTO DoLoop;
};
UNWIND => GOTO DoLoop;
ANY => IF h.bulletProof THEN { -- otherwise reject
Inform: PROCEDURE = {
h.in.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.NewLine[ ! ANY => CONTINUE];
h.out.PutSignal[ ! ANY => CONTINUE];
h.out.NewLine[ ! ANY => CONTINUE];
};
IF rejectThisOne THEN REJECT;
Inform[! ANY => CONTINUE];
GOTO DoLoop;
};
};
{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.
};
IO.Signal => IF ec = Rubout THEN {
IF rejectThisOne THEN REJECT;
IF h.ruboutProc # NIL THEN h.ruboutProc[h]
ELSE {
h.in.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.PutF[IF h.readIOSignalRope # NIL THEN h.readIOSignalRope ELSE " XXX\n" ! ANY => CONTINUE];
};
GOTO DoLoop;
};
ViewerIO.NotAllowed => {
IF rejectThisOne THEN REJECT;
h.in.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.PutRope[" ViewerIO.NotAllowed => XXX\n" ! ANY => CONTINUE];
GOTO DoLoop;
};
IO.SyntaxError => {
IF rejectThisOne THEN REJECT;
h.in.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
h.out.PutF["Syntax Error: %g", IO.rope[msg] ! ANY => CONTINUE];
GOTO DoLoop;
};
IO.EndOfStream => GOTO Destroyed;
};
MyCharProc: IO.CharProc = {
b: BOOL ← h.deliverWhenProc[char: char, stream: h.in ! ANY => {
rejectThisOne ← TRUE;
REJECT;
}];
RETURN[quit: b, include: TRUE];
};
AvailCharProc: IO.CharProc = {
RETURN[quit: NOT h.in.CharsAvail[], include: TRUE];
};
h.out.NewLine[ ! ANY => CONTINUE];
IF result # NIL THEN h.out.PutF[result ! ANY => {
badRope ← result;
result ← NIL;
rejectThisOne ← TRUE;
REJECT}];
h.out.NewLine[ ! ANY => CONTINUE];
IF h.in.EndOf[! ANY => GOTO Destroyed] THEN GOTO Destroyed;
h.out.PutF[h.prompt ! ANY => {
badRope ← result;
rejectThisOne ← TRUE;
h.prompt ← NIL;
REJECT}];
IF h.promptProc # NIL THEN h.promptProc[h ! ANY => {
rejectThisOne ← TRUE;
REJECT}];
IF h.deliverWhenProc # NIL THEN commandLine ← h.in.GetSequence[MyCharProc]
ELSE {
[] ← h.in.PeekChar[]; -- wait DeliverWhenProc to activate
commandLine ← h.in.GetSequence[AvailCharProc];
};
}
EXITS
DoLoop => LOOP;
}}; -- end of input block
TRUSTED {
result ← h.clientProc[h, commandLine !
ABORTED => {result ← IF h.evalABORTEDRope # NIL THEN h.evalABORTEDRope ELSE " ...Aborted\n"; CONTINUE};
IO.UserAborted => {
s: IO.STREAM ← h.in;
WITH abortee SELECT FROM
a: IO.STREAM => s ← a;
ENDCASE;
s.ResetUserAbort[ ! ANY => CONTINUE];
h.in.Reset[ ! ANY => CONTINUE];
result ← NIL;
IF h.evalUserAbortedRope # NIL THEN result ← h.evalUserAbortedRope
ELSE h.out.PutF[" ...UserAbort %g\n", IO.rope[msg] ! ANY => CONTINUE];
CONTINUE;
};
UNWIND => {result ← IF h.evalUNWINDRope # NIL THEN h.evalUNWINDRope ELSE " ...Unwound\n"; CONTINUE};
];
};
ENDLOOP;
EXITS
Destroyed => NULL;
};
IsACR: PUBLIC IO.DeliverWhenProc = { RETURN[char = '\n]; };
Implementation private procedures
SetTerminateRequested: ENTRY PROC [h: ReadEvalPrint.Handle] = {
ENABLE UNWIND => NULL;
IF h.in # NIL THEN h.in.Close[! ANY => CONTINUE];
h.terminateRequested ← TRUE;
};
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]
in: IO.STREAMNARROW[clientData, ReadEvalPrint.Handle].in;
in.SetUserAbort[];
};
ViewerEvent: ViewerEvents.EventProc = TRUSTED {
SELECT event FROM
destroy => {
prop: REF ← ViewerOps.FetchProp[viewer, $ReadEvalPrint];
IF prop # NIL THEN {
SetTerminateRequested[NARROW[prop, ReadEvalPrint.Handle]];
};
};
ENDCASE;
};
Init: PROC = {
[] ← ViewerEvents.RegisterEventProc[proc: ViewerEvent, event: destroy, filter: $Typescript];
};
Initialization
Init[];
END.