ReadEvalPrintImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Original version by P. Rovner March 30, 1983 3:34 pm
Russ Atkinson (RRA) November 26, 1985 12:49:08 pm PST
DIRECTORY
EditedStream USING [DeliverWhenProc, IsACR, Rubout, SetDeliverWhen],
IO USING [BreakProc, CharsAvail, Close, EndOf, EndOfStream, Error, Flush, GetChar, GetTokenRope, PutF, PutRope, Reset, STREAM],
List USING [AList],
MBQueue USING [Create, CreateMenuEntry, Queue],
Menus USING [InsertMenuEntry, MenuProc],
Process USING [Abort, Detach, GetCurrent, InvalidProcess],
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 EditedStream, IO, MBQueue, Menus, Process, ProcessProps, Rope, RuntimeError, TypeScript, ViewerEvents, ViewerIO, ViewerOps
EXPORTS ReadEvalPrint
= BEGIN
Handle: TYPE = ReadEvalPrint.Handle;
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
CreateViewerEvaluator: PUBLIC PROC [clientProc: ReadEvalPrint.ClientProc, prompt: ROPENIL, info: ViewerClasses.ViewerRec ← [], edited: BOOLTRUE, deliverWhen: EditedStream.DeliverWhenProc ← NIL, clientData: REFNIL, topLevel: BOOLTRUE] RETURNS [h: 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: ROPENIL, in, out: STREAM, deliverWhen: EditedStream.DeliverWhenProc ← NIL, clientData: REFNIL, topLevel: BOOLFALSE] RETURNS [h: 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 PROC [h: Handle] = {
This eventually makes the process associated with the handle terminate.
h.terminateRequested ← TRUE;
};
MainLoop: PUBLIC PROC [h: Handle, forkAndDetach: BOOLTRUE, properties: List.AList] = {
inner: PROC = { MainLoopInternal[h]; };
IF forkAndDetach THEN TRUSTED {
We can be recursive here, since at the next level we do not recurse.
Process.Detach[FORK MainLoop[h, FALSE, properties]];
RETURN;
};
Stuff the given properties on the current process.
IF properties # NIL THEN ProcessProps.PushPropList[properties, inner] ELSE inner[];
};
MainLoopInternal: PROC [h: Handle] = {
commandLine: ROPENIL;
result: ROPENIL;
rejectThisOne: BOOLFALSE;
in: STREAM ← h.in;
out: STREAM ← h.out;
breakProc: IO.BreakProc = {
IF h.deliverWhenProc[char, NIL, in, NIL].activate
THEN RETURN[break] ELSE RETURN[other];
};
DoRead: PROC RETURNS [destroyed: BOOLFALSE] = {
destroyed ← IO.EndOf[in ! RuntimeError.UNCAUGHT => {destroyed ← TRUE; CONTINUE}];
IF NOT destroyed THEN {
IO.PutF[out, h.prompt, [rope["b"]], [rope["B"]] ];
IF h.promptProc # NIL THEN h.promptProc[h];
GetLine[ ! IO.EndOfStream => CONTINUE];
};
};
GetLine: PROC = {
Get a command line.
commandLine ← NIL;
NIL out the command line even if we get an error
commandLine ← IO.GetTokenRope[in, breakProc].token;
IF commandLine.Length[] # 0 THEN
IF NOT h.deliverWhenProc[commandLine.Fetch[0], NIL, in, NIL].activate THEN
commandLine ← Rope.Concat[commandLine, IO.GetTokenRope[in, breakProc].token];
};
TRUSTED {h.mainLoopProcess ← LOOPHOLE[Process.GetCurrent[], SAFE PROCESS]};
UNTIL h.terminateRequested DO
aborted: BOOLFALSE;
syntaxError: BOOLFALSE;
rubout: BOOLFALSE;
Just in case the user alters these with intent.
in ← h.in;
out ← h.out;
IF in = NIL OR out = NIL THEN RETURN;
Try to read a `line' for the user. We try very hard to protect against errors, and to report the non-fatal ones. It is always fatal for the input stream to not be readable, for example. It is fatal to be aborted if not at topLevel. Rubouts should never be fatal.
IF DoRead[ !
ABORTED => {aborted ← TRUE; CONTINUE};
IO.Error => {
IF ec = StreamClosed THEN EXIT;
IF h.viewer = NIL OR h.viewer.destroyed THEN EXIT;
IF ec = SyntaxError THEN {syntaxError ← TRUE; CONTINUE};
};
IO.EndOfStream => {
IF h.viewer = NIL OR h.viewer.destroyed THEN EXIT;
IF stream = in THEN EXIT;
};
EditedStream.Rubout => {rubout ← TRUE; CONTINUE};
] THEN EXIT;
{
Here we handle the actual work of the world, as well as rubout and syntaxError reporting. We try to guard against errors when dealing with rubout and syntaxError reporting, but the user must not be overly protected against anything except ABORTED.
ENABLE ABORTED => {aborted ← TRUE; GO TO out};
SELECT TRUE FROM
rubout => {
ENABLE RuntimeError.UNCAUGHT => GO TO out;
IF h.ruboutProc # NIL
THEN h.ruboutProc[h]
ELSE {
msg: ROPE ← " -- <del>\n";
IF h.readIOSignalRope # NIL THEN msg ← h.readIOSignalRope;
EatIt[in];
IO.PutRope[out, msg];
};
};
syntaxError => {
ENABLE RuntimeError.UNCAUGHT => GO TO out;
EatIt[in];
IO.PutRope[out, " -- Syntax error!\n"];
};
commandLine # NIL AND NOT aborted => {
result ← h.clientProc[h, commandLine];
IF result # NIL THEN {
IO.PutF[out, result];
We use PutF to handle % codes registered by the client.
result ← NIL;
IO.PutRope[out, "\n"];
};
};
ENDCASE => {};
EXITS out => {};
};
IF aborted THEN {
msg: ROPE ← " -- Aborted.\n";
IF h.evalABORTEDRope # NIL THEN msg ← h.evalABORTEDRope;
IO.PutRope[out, msg ! RuntimeError.UNCAUGHT => CONTINUE];
IO.Flush[out ! RuntimeError.UNCAUGHT => CONTINUE];
EatIt[in ! ABORTED => CONTINUE];
IF NOT h.topLevel THEN {
IF in # NIL THEN IO.Close[in
! RuntimeError.UNCAUGHT => CONTINUE; IO.Error => CONTINUE];
EXIT;
};
};
ENDLOOP;
};
Implementation private procedures
StopHit: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
h: Handle ← NARROW[clientData, Handle];
Process.Abort[h.mainLoopProcess ! Process.InvalidProcess => CONTINUE];
};
EatIt: PROC [st: STREAM] = {
This little helper routine eats all characters from an input stream until there are no more available. If the stream is NIL or any IO error occurs, then we just exit.
IF st # NIL THEN {
ENABLE {
IO.Error => GO TO done;
IO.EndOfStream => GO TO done;
RuntimeError.UNCAUGHT => GO TO done};
IO.Reset[st];
WHILE IO.CharsAvail[st] > 0 DO
[] ← IO.GetChar[st];
IO.Reset[st];
ENDLOOP;
EXITS done => {};
};
};
ViewerEvent: ViewerEvents.EventProc = TRUSTED {
SELECT event FROM
destroy => {
prop: REF ← ViewerOps.FetchProp[viewer, $ReadEvalPrint];
IF prop # NIL THEN {
Stop[NARROW[prop, Handle]];
};
};
ENDCASE;
};
Init: PROC = {
[] ← ViewerEvents.RegisterEventProc[proc: ViewerEvent, event: destroy, filter: $Typescript];
};
Initialization
Init[];
END.