GGSessionLogImpl.mesa
Last edited by Bier on August 20, 1985 3:08:55 pm PDT.
Contents: Routines for saving TIP Table atoms in a file to aid in interface evaluation and for playback.
DIRECTORY
Atom, FS, GGError, GGInterfaceTypes, GGModelTypes, GGSessionLog, GGUserInput, IO, Rope;
GGSessionLogImpl:
CEDAR
PROGRAM
IMPORTS Atom, FS, GGError, GGUserInput, IO, Rope
EXPORTS GGSessionLog =
BEGIN
CharClass: TYPE = IO.CharClass;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Point: TYPE = GGModelTypes.Point;
globalStream: IO.STREAM;
SetStream:
PUBLIC
PROC [stream:
IO.
STREAM] = {
globalStream ← stream;
};
EnterAction:
PUBLIC
PROC [point: Point, action:
LIST
OF
REF
ANY, mouseEvent:
BOOL] = {
IF mouseEvent THEN globalStream.PutF["* [%g, %g] ", [real[point[1]]], [real[point[2]]] ];
FOR actionList:
LIST
OF
REF
ANY ← action, actionList.rest
UNTIL actionList =
NIL
DO
WITH actionList.first SELECT FROM
atom: ATOM => globalStream.PutF["%g", [rope[Atom.GetPName[atom]]]];
n: REF INT => globalStream.PutF["%g", [integer[n^]]];
r: REF REAL => globalStream.PutF["%g", [real[r^]]];
ENDCASE => ERROR;
IF actionList.rest = NIL THEN globalStream.PutChar[IO.CR]
ELSE globalStream.PutChar[IO.SP];
ENDLOOP;
};
OpenFile:
PROC [name: Rope.
ROPE]
RETURNS [f:
IO.
STREAM, success:
BOOL] = {
success ← TRUE;
Two possiblilities
1) File doesn't exist. Print error message.
2) File does exist. File it in. Succeed.
f ←
FS.StreamOpen[name
!
FS.Error => {
IF error.group = user
THEN {
success ← FALSE;
GGError.Append["Picture file problem: ", oneLiner];
GGError.Append[error.explanation, oneLiner];
}
ELSE ERROR;
CONTINUE}];
};
PlaybackFromFile:
PUBLIC
PROC [name: Rope.
ROPE, gargoyleData: GargoyleData] = {
wdir, fullName: Rope.ROPE;
success: BOOL;
f: IO.STREAM;
wdir ← gargoyleData.originalWorkingDirectory;
success ← TRUE;
[fullName,,] ←
FS.ExpandName[name, wdir
!
FS.Error =>
IF error.group = user
THEN {
success ← FALSE;
CONTINUE;
}
];
IF NOT success THEN RETURN;
[f, success] ← OpenFile[fullName];
IF NOT success THEN RETURN;
WHILE success DO
success ← PlayAction[f, gargoyleData];
ENDLOOP;
};
AppendAtom:
PROC [atom:
ATOM, list:
LIST
OF
REF
ANY]
RETURNS [newList:
LIST
OF
REF
ANY] = {
l: LIST OF REF ANY ← list;
IF l = NIL THEN RETURN[LIST[atom]];
UNTIL l.rest = NIL DO l ← l.rest ENDLOOP;
l.rest ← CONS[atom, NIL];
newList ← list;
};
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;
};
PlayAction:
PROC [f:
IO.
STREAM, gargoyleData: GargoyleData]
RETURNS [success:
BOOL] = {
c: CHAR;
mouseEvent: BOOL;
point: Point;
good: BOOL;
token: REF ANY;
action: LIST OF REF ANY;
success ← TRUE;
c ←
IO.GetChar[f
!IO.EndOfStream => {success ← FALSE; CONTINUE}];
IF NOT success THEN RETURN;
IF c = '*
THEN {
good ← ReadHorizontalBlank[f];
IF NOT good THEN ERROR;
point ← ReadPoint[f];
mouseEvent ← TRUE
}
ELSE {
mouseEvent ← FALSE;
IO.Backup[f, c];
};
token ← $Start;
UNTIL token = $EndOfLine
DO
[token] ← ReadSpacesAndAtomOrNum[f];
IF token = $EndOfLine THEN LOOP
ELSE action ← AppendToken[token, action]
ENDLOOP;
GGUserInput.PlayAction[point, action, mouseEvent, gargoyleData];
};
ReadPoint:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [point: Point] = {
Assumes the next rope on the stream will be of the form "[<real1>,<real2>]".
ReadRope[f, "["];
point[1] ← ReadBlankAndReal[f];
ReadRope[f, ","];
point[2] ← ReadBlankAndReal[f];
ReadRope[f, "]"];
};
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.
realRope: Rope.ROPE;
end: BOOL ← FALSE;
[realRope,
----] ←
IO.GetTokenRope[f, RealBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {r ← 0.0; RETURN};
IF Rope.Find[realRope, ".", 0, FALSE] = -1 THEN realRope ← Rope.Concat[realRope, ".0"];
r ← IO.GetReal[IO.RIS[realRope]];
};
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];
};
ReadBlankAndNAT:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [n:
NAT] = {
A convenience function. Equivalent to ReadBlank[f]; n ← ReadNAT[f];
ReadBlank[f];
n ← ReadNAT[f];
};
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
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};
};
HorizontalBlankProc:
SAFE
PROC [char:
CHAR]
RETURNS [CharClass] = {
SELECT char
FROM
IO.TAB, IO.SP => RETURN [other];
ENDCASE => RETURN [break];
};
IsDigit:
PROC [c:
CHAR]
RETURNS [
BOOL] = {
RETURN[c IN ['0..'9]];
};
ReadSpacesAndAtomOrNum:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [token:
REF
ANY] = {
word: Rope.ROPE;
good: BOOL;
int: INT;
real: REAL;
good ← ReadHorizontalBlank[f];
IF
NOT good
THEN {
token ← $EndOfLine;
RETURN;
};
word ← ReadWord[f];
IF IsDigit[Rope.Fetch[word, 0]]
THEN {
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];
};
}
ELSE {
token ← Atom.MakeAtom[word];
};
};
ReadBlankAndWord:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [word: Rope.
ROPE] = {
A convenience function. Equivalent to ReadBlank[f]; word ← ReadWord[f];
ReadBlank[f];
word ← ReadWord[f];
};
ReadBlankAndRope:
PUBLIC
PROC [f:
IO.
STREAM, rope: Rope.
ROPE] = {
ReadBlank[f];
ReadRope[f, rope];
};
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];
};
ReadWord:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [word: Rope.
ROPE] = {
Reads a rope until <SPACE>, <CR>, <COMMA> or <TAB> are encountered. Used to read in a rope which is data. ie the name of a coordinate system from a 3d file.
[word,
----] ←
IO.GetTokenRope[f, WordBreakProc
!IO.EndOfStream => {word ← NIL; CONTINUE}];
};
WordBreakProc:
SAFE
PROC [char:
CHAR]
RETURNS [
IO.CharClass] =
CHECKED {
SELECT char
FROM
IO.TAB => RETURN [break];
IO.CR =>RETURN [break];
IO.SP => RETURN [break];
', => RETURN [break];
'] => RETURN [break];
') => RETURN [break];
ENDCASE => RETURN [other];
};
ReadNAT:
PUBLIC
PROC [f:
IO.
STREAM]
RETURNS [n:
NAT] = {
Reads digits up to the next ], <CR>, <SPACE>. Leaves these terminators on the stream.
end: BOOL ← FALSE;
intRope: Rope.ROPE;
[intRope,
----] ←
IO.GetTokenRope[f, NATBreakProc
!IO.EndOfStream => {end ← TRUE; CONTINUE}];
IF end THEN {n ← 1; RETURN};
n ← IO.GetInt[IO.RIS[intRope]];
};
NATBreakProc:
SAFE
PROC [char:
CHAR]
RETURNS [
IO.CharClass] =
TRUSTED {
SELECT char
FROM
'], IO.CR, IO.SP, '., ', => RETURN [break];
ENDCASE => RETURN [other];
};
RopeNotOnTop: PUBLIC SIGNAL [position: NAT, wasThere: Rope.ROPE, notThere: Rope.ROPE] = CODE;
ReadRope:
PUBLIC
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 RopeNotOnTop 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 RopeNotOnTop [IO.GetIndex[f], NIL, rope];
IF
NOT c = Rope.Fetch[rope,i-1]
THEN
SIGNAL RopeNotOnTop [IO.GetIndex[f], Rope.FromChar[c], rope];
ENDLOOP;
};
END.