ReadEvalPrintImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
A comman read-eval-print loop for use with viewers.
Original version by P. Rovner March 30, 1983 3:34 pm
L. Stewart, January 17, 1984 11:38 am
Russ Atkinson, November 5, 1984 9:29:30 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:
ROPE ←
NIL, info: ViewerClasses.ViewerRec ← [], edited:
BOOL ←
TRUE, deliverWhen: EditedStream.DeliverWhenProc ←
NIL, clientData:
REF ←
NIL, topLevel:
BOOL ←
TRUE]
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:
ROPE ←
NIL, in, out:
STREAM, deliverWhen: EditedStream.DeliverWhenProc ←
NIL, clientData:
REF ←
NIL, topLevel:
BOOL ←
FALSE]
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:
BOOL ←
TRUE, 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: ROPE ← NIL;
result: ROPE ← NIL;
rejectThisOne: BOOL ← FALSE;
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:
BOOL ←
FALSE] = {
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;
commandLine ← IO.GetTokenRope[in, breakProc].token;
IF commandLine.Length[] <= 1 THEN RETURN;
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: BOOL ← FALSE;
syntaxError: BOOL ← FALSE;
rubout: BOOL ← FALSE;
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;
}; -- end MainLoopInternal
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.
January 16, 1984 5:01 pm, Stewart, last line of CM or LOAD file not end with a CR
January 17, 1984 11:38 am, Stewart, add bold and not bold IO.Values to prompt PutF