ReadEvalPrint.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:02 am
This interface attempts to abstract some of the notion of a Read-Eval-Print loop. In particular, the common case of a viewer based Read-Eval-Print loop is fully supported.
DIRECTORY
IO USING [DeliverWhenProc, STREAM],
MBQueue USING [Queue],
Rope USING [ROPE],
ViewerClasses USING [Viewer, ViewerRec];
ReadEvalPrint: CEDAR DEFINITIONS =
BEGIN
The major goal of this package is to provide the (rather complex) catch phrases needed to isolate mortal programmers from the many signals that can arise during user interaction. These catch phrases are hard to get right and it is hoped that those whose applications fit the read-eval-print model closely enough can use this package as is. Those with more complex requirements can use the implementation of this package as a starting point for modifications.
Handle: TYPE = REF RObject;
RObject: TYPE = MONITORED RECORD [
viewer: ViewerClasses.Viewer ← NIL,
in: IO.STREAMNIL,
out: IO.STREAMNIL,
menuHitQueue: MBQueue.Queue,
terminateRequested: BOOLFALSE,
clientProc: ClientProc ← NIL,
deliverWhenProc: IO.DeliverWhenProc ← NIL,
prompt: Rope.ROPENIL,
promptProc: PROC [Handle] ← NIL,
bulletProof: BOOLTRUE,
clientData: REF ANYNIL,
ruboutProc: PROC [Handle] ← NIL,
readUserAbortedRope: Rope.ROPENIL, -- " XXX\n"
readABORTEDRope: Rope.ROPENIL, -- " aborted\n"
readIOSignalRope: Rope.ROPENIL, -- " XXX\n"
evalABORTEDRope: Rope.ROPENIL, -- " ...Aborted\n"
evalUserAbortedRope: Rope.ROPENIL, -- " ...UserAbort {msg}\n", msg is from the signal
evalUNWINDRope: Rope.ROPENIL -- " ...Unwound\n"
];
Advanced features:
bulletProof = FALSE disables an ANY catch phrase protecting the Read phase of the loop. This should only be interesting during debugging of a complex DeliverWhenProc.
menuHitQueue is used to serialize any mouse actions on buttons in the caption area of the viewer (if the viewer exists). The package provides only a STOP button, whose only action is to set UserAbort in the in stream. The client may wish to add more buttons for other functions. Look at the implementation of ReadEvalPrint to see how to do this.
If a promptProc is specified, it will be called right after the prompt rope is printed.
If a ruboutPrompt is specified, it will be called if IO.Signal[ec: Rubout] is raised during the read phase.
If any of the 'read' or 'eval' ropes are not NIL, then they will be used when the given signals are caught during the read or eval phases. All of these ropes are printed using IO.PutF, so the client may register PFStarCodeProcs.
ClientProc: TYPE = PROC [h: Handle, command: Rope.ROPE] RETURNS [result: Rope.ROPENIL];
The ClientProc is expected to implement the Eval and Print parts of the read-eval-print loop. The ClientProc will be called with each "line" of input (as defined by the DeliverWhenProc, see below). Inside the ClientProc, the client can make arbitrary use of the streams h.in and h.out and may change the prompt and clientData fields of the handle. The package's call to ClientProc is protected only by ctch phrases for ABORTED and UNWIND. Thus other signals raised by ClientProc will go through to the debugger (or to catch phrases in the caller of MainLoop - see below).
CreateViewerEvaluator: PROC [clientProc: ClientProc, prompt: Rope.ROPENIL, info: ViewerClasses.ViewerRec ← [], edited: BOOLTRUE, deliverWhen: IO.DeliverWhenProc ← IsACR, clientData: REF ANYNIL] RETURNS [Handle];
A viewer of the specified sort is created, edited (or not) IO streams are attached to it. Also see comments for CreateStreamEvaluator.
CreateStreamEvaluator: PROC [clientProc: ClientProc, prompt: Rope.ROPENIL, in, out: IO.STREAM, deliverWhen: IO.DeliverWhenProc ← NIL, clientData: REF ANYNIL] RETURNS [Handle];
Creates a Read-Eval-Print loop using the given STREAMs. Otherwise, the evaluator created is like the one above. In this case, Handle.viewer will be NIL. If the client wants an edited stream, it is the client's responsibility to provide one. If deliverWhen is NIL, then the Read phase will wait for the first character to be available from the input stream, then collect additional characters until in.CharsAvail[] = FALSE. If deliverWhen is not NIL, the reader will activate when deliverWhen[char] = TRUE. This facility is intended for reading line at a time from a disk file. As above, the loop will stop if in.Endof[] = TRUE. The prompt rope is printed using IO.PutF, so PFStarCodeProcs may be used, but the character '% must be handled specially
MainLoop: PROC [h: Handle, forkAndDetach: BOOLTRUE];
This procedure is the main loop of the read-eval-print. The main loop is fully protected against signals and errors arising during the Read phase, but only against ABORTED and UNWIND during the eval and print phases. The evaluator will stop when Stop is called (see below), or when the input stream reports EOF. A client may wish to protect the call to MainLoop by catch phrases in order to protect calls to ClientProc from signals other than ABORTED and UNWIND. MainLoop may be called recursivly from a ClientProc. If forkAndDetach = TRUE, then the call to MainLoop will return almost right away.
Stop: PROC [h: Handle];
Cause a Read-Eval-Print loop to shut down. clientProc will not be called again, the MainLoop process will disappear, but the viewer (if any) will not be destroyed and the streams will not be closed. Stop may be called either from inside the ClientProc or from the debugger (if the client called MainLoop directly, rather than by using CreateViewerEvaluator, then a Client catch phrase above MainLoop may call Stop.)
IsACR: IO.DeliverWhenProc;
The definition of IsACR is RETURN[char = CR]. The reader will read to the next CR and return the characters read as a rope, including the CR. The principle for a DeliverWhenProc is that MainLoop will call ClientProc whenever DeliverWhenProc returns TRUE, passing it all the characters typed.
END.