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: BOOL ← FALSE;
WHILE
NOT endOfStream
DO
endOfStream ← PlayAction[f, clientData, notifyProc];
ENDLOOP;
};
PlayAction:
PUBLIC
PROC [f:
IO.
STREAM, clientData:
REF
ANY, notifyProc: SlackProcess.EventProc]
RETURNS [endOfStream:
BOOL ←
FALSE] = {
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: BOOL ← FALSE;
[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: BOOL ← FALSE;
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: BOOL ← FALSE;
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: BOOL ← FALSE;
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.