SessionLogImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on April 27, 1987 10:47:48 pm PDT
Contents: Routines recording and playing back a record of an interactive session. SessionLog is intended to be used with SlackProcess in systems built according to the new user interface architecture (see SlackProcessDoc and AtomButtonsDoc).
DIRECTORY
Atom, Convert, Vector2, IO, RefText, Rope, SessionLog, SlackProcess;
SessionLogImpl: CEDAR PROGRAM
IMPORTS Atom, Convert, RefText, IO, Rope
EXPORTS SessionLog
=
BEGIN
Recording
EnterAction: PUBLIC PROC [f: IO.STREAM, action: LIST OF REF ANY] = {
FOR actionList: LIST OF REF ANY ← action, actionList.rest UNTIL actionList = NIL DO
WITH actionList.first SELECT FROM
atom: ATOM => f.PutF["%g", [rope[Atom.GetPName[atom]]]];
n: REF INT => f.PutF["%g", [integer[n^]]];
r: REF REAL => f.PutF["%g", [real[r^]]];
c: REF CHAR => f.PutF["'%g", [character[c^]]];
cd: REF CARD => f.PutF["%g", [cardinal[cd^]]];
rope: Rope.ROPE => f.PutF["\"%g\"", [rope[rope]]];
refText: REF TEXT => f.PutF["\"%g\"", [text[refText]]];
refPoint: REF Vector2.VEC => f.PutF["[%g, %g]", [real[refPoint.x]], [real[refPoint.y]]];
ENDCASE => ERROR;
IF actionList.rest = NIL THEN f.PutChar[IO.CR]
ELSE f.PutChar[IO.SP];
ENDLOOP;
};
Playing Back
PlayScript: PUBLIC PROC [f: IO.STREAM, clientData: REF ANY, notifyProc: SlackProcess.EventProc] = {
endOfStream: BOOLFALSE;
WHILE NOT endOfStream DO
endOfStream ← PlayAction[f, clientData, notifyProc];
ENDLOOP;
};
PlayAction: PUBLIC PROC [f: IO.STREAM, clientData: REF ANY, notifyProc: SlackProcess.EventProc] RETURNS [endOfStream: BOOLFALSE] = {
c: CHAR;
mouseEvent: BOOL;
point: Vector2.VEC;
good: BOOL;
token: REF ANY;
action: LIST OF REF ANY;
ReadBlank[f];
For compatibility with the old style of script.
c ← IO.GetChar[f
!IO.EndOfStream => {endOfStream ← TRUE; CONTINUE}];
IF endOfStream THEN RETURN;
IF c = '* THEN {
atom: ATOM;
refPoint: REF Vector2.VEC;
good ← ReadHorizontalBlank[f];
IF NOT good THEN ERROR;
point ← ReadPoint[f];
refPoint ← NEW[Vector2.VEC ← point];
token ← ReadSpacesAndToken[f];
atom ← NARROW[token];
UNTIL token = $EndOfLine DO
token ← ReadSpacesAndToken[f];
ENDLOOP;
action ← LIST[atom, refPoint];
}
End of compatibility.
ELSE {
mouseEvent ← FALSE;
IO.Backup[f, c];
token ← $Start;
UNTIL token = $EndOfLine DO
token ← ReadSpacesAndToken[f];
IF token = $EndOfLine THEN LOOP;
action ← AppendToken[token, action]
ENDLOOP;
};
notifyProc[action, clientData];
};
ReadSpacesAndToken: PUBLIC PROC [f: IO.STREAM] RETURNS [token: REF ANY] = {
word: Rope.ROPE;
good: BOOL;
int: INT;
real: REAL;
firstChar: CHAR;
good ← ReadHorizontalBlank[f];
IF NOT good THEN {
token ← $EndOfLine;
RETURN;
};
firstChar ← IO.PeekChar[f];
SELECT TRUE FROM
firstChar = '" => {
token ← f.GetRopeLiteral[];
};
firstChar = '' => {
[] ← f.GetChar[];
token ← NEW[CHAR ← f.GetChar[]];
};
firstChar = '[ => {
point: Vector2.VEC ← ReadPoint[f];
token ← NEW[Vector2.VEC ← point];
};
IsDigitOrOp[firstChar] => {
word ← ReadBlankAndWord[f];
IF Rope.Find[word, "."] = -1 THEN { -- an integer
int ← IO.GetInt[IO.RIS[word]];
token ← NEW[INT ← int];
}
ELSE {
real ← IO.GetReal[IO.RIS[word]];
token ← NEW[REAL ← real];
};
};
ENDCASE => {
word ← ReadBlankAndWord[f];
token ← Atom.MakeAtom[word];
};
};
ReadLine: PROC [f: IO.STREAM] RETURNS [line: Rope.ROPE] = {
Reads a rope UNTIL <CR> is encountered.
LineBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = CHECKED {
SELECT char FROM
IO.CR =>RETURN [break];
ENDCASE => RETURN [other];
};
end: BOOLFALSE;
[line, ----] ← IO.GetTokenRope[f, LineBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {line ← NIL; RETURN};
};
ReadSpaceAndChar: PROC [f: IO.STREAM] RETURNS [token: REF ANY] = {
c: CHAR;
good: BOOL;
c ← IO.GetChar[f !IO.EndOfStream => ERROR];
c ← IO.GetChar[f !IO.EndOfStream => ERROR];
good ← ReadHorizontalBlank[f];
IF good THEN ERROR;
token ← NEW[CHAR ← c];
};
AppendToken: PROC [token: REF ANY, list: LIST OF REF ANY] RETURNS [newList: LIST OF REF ANY] = {
l: LIST OF REF ANY ← list;
IF l = NIL THEN RETURN[LIST[token]];
UNTIL l.rest = NIL DO l ← l.rest ENDLOOP;
l.rest ← CONS[token, NIL];
newList ← list;
};
IsDigitOrOp: PROC [c: CHAR] RETURNS [BOOL] = {
RETURN[c IN ['0..'9] OR c = '- OR c = '+];
};
Parsing
ReadBlank: PUBLIC PROC [f: IO.STREAM] = {
Reads, <SPACE>'s, <CR>'s, and <TAB>'s until something else is encountered. Doesn't mind if no white space characters are found. Treats comments as white space.
[] ← IO.SkipWhitespace[f, TRUE];
};
ReadHorizontalBlank: PUBLIC PROC [f: IO.STREAM] RETURNS [good: BOOL] = {
Reads <SPACE>'s, and <TABS>'s until something else is encountered. Returns good = FALSE if a CR is encountered before anything else
HorizontalBlankProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = TRUSTED {
SELECT char FROM
IO.TAB, IO.SP => RETURN [other];
ENDCASE => RETURN [break];
};
whiteSpace: Rope.ROPE;
c: CHAR;
end: BOOLFALSE;
good ← TRUE;
[whiteSpace, ----] ← IO.GetTokenRope[f, HorizontalBlankProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {good ← FALSE; RETURN};
c ← Rope.Fetch[whiteSpace, 0];
SELECT c FROM
IO.CR => {good ← FALSE; RETURN};
IO.TAB, IO.SP => {good ← TRUE; RETURN};
ENDCASE => {good ← TRUE; IO.Backup[f, c]; RETURN};
};
ReadBlankAndReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
A convenience function. Equivalent to ReadBlank[f]; r ← ReadReal[f];
ReadBlank[f];
r ← ReadReal[f];
};
ReadReal: PUBLIC PROC [f: IO.STREAM] RETURNS [r: REAL] = {
Reads digits up to the next ), ], <CR>, <SPACE> or <COMMA>. Leaves these terminators on the stream.
RealBreakProc: SAFE PROC [char: CHAR] RETURNS [IO.CharClass] = TRUSTED {
SELECT char FROM
'), '], ', => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
ENDCASE => RETURN [other];
};
realText, buffer: REF TEXT;
end: BOOLFALSE;
buffer ← RefText.ObtainScratch[50];
[realText, ----] ← IO.GetToken[f, RealBreakProc, buffer
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {r ← 0.0; RETURN};
IF RefText.Find[realText, ".", 0, FALSE] = -1 THEN realText ← RefText.Append[realText, ".0"];
r ← Convert.RealFromRope[RefText.TrustTextAsRope[realText]];
RefText.ReleaseScratch[buffer];
};
ReadPoint: PUBLIC PROC [f: IO.STREAM] RETURNS [point: Vector2.VEC] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>]".
ReadBlank[f];
ReadRope[f, "["];
point.x ← ReadBlankAndReal[f];
ReadRope[f, ","];
point.y ← ReadBlankAndReal[f];
ReadRope[f, "]"];
};
SyntaxError: PUBLIC SIGNAL [position: NAT, wasThere: Rope.ROPE, notThere: Rope.ROPE] = CODE;
ReadRope: PROC [f: IO.STREAM, rope: Rope.ROPE] = {
Removes the given rope from the top of the stream. Used to remove formatting words and phrases from 3d files. We are not interested in these strings but only in the data in between them.
Signals SyntaxError if some other rope is on top.
c: CHAR;
endofstream: BOOLFALSE;
FOR i: INT IN[1..Rope.Length[rope]] DO
c ← IO.GetChar[f
! IO.EndOfStream => {endofstream ← TRUE; CONTINUE}];
IF endofstream THEN
SIGNAL SyntaxError [IO.GetIndex[f], NIL, rope];
IF NOT c = Rope.Fetch[rope,i-1] THEN
SIGNAL SyntaxError [IO.GetIndex[f], Rope.FromChar[c], rope];
ENDLOOP;
};
ReadBlankAndWord: PUBLIC PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
ReadBlank[f];
word ← ReadWord[f];
};
ReadWord: PROC [f: IO.STREAM] RETURNS [word: Rope.ROPE] = {
Used to read in a rope which is data.
WordBreakProc: PROC [char: CHAR] RETURNS [IO.CharClass] = {
SELECT char FROM
IO.TAB => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
', => RETURN [break];
'] => RETURN [break];
') => RETURN [break];
ENDCASE => RETURN [other];
};
[word, ----] ← IO.GetTokenRope[f, WordBreakProc
!IO.EndOfStream => {word ← NIL; CONTINUE}];
};
END.