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.
ROPE ←
NIL, info: ViewerClasses.ViewerRec ← [], edited:
BOOL ←
TRUE, deliverWhen:
IO.DeliverWhenProc ← IsACR, clientData:
REF
ANY ←
NIL]
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.
ROPE ←
NIL, in, out:
IO.
STREAM, deliverWhen:
IO.DeliverWhenProc ←
NIL, clientData:
REF
ANY ←
NIL]
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:
BOOL ←
TRUE] = {
IF forkAndDetach THEN TRUSTED { Process.Detach[FORK MainLoopInternal[h]]; }
ELSE MainLoopInternal[h];
};
MainLoopInternal:
PROC [h: ReadEvalPrint.Handle] = {
commandLine: Rope.ROPE ← NIL;
result: Rope.ROPE ← NIL;
rejectThisOne: BOOL ← FALSE;
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];
};
}
}}; -- 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;
};
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.STREAM ← NARROW[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.