GraphRead.mesa, Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, November 17, 1985 1:03:04 am PST
DIRECTORY
Atom USING [GetPropFromList],
Basics USING [BYTE],
BasicTime USING [GMT],
Commander USING [CommandProc, Register],
CommandTool USING [ParseToList],
Convert USING [CardFromRope, Error, RopeFromTime],
FileNames USING [CurrentWorkingDirectory],
FS USING [EnumerateForNames, NameProc, Error, StreamOpen],
Graph USING [CaretIndex, CaretSpec, ColorIndex, Entity, EntityGroup, EntityGroupList, EntityGroupRec, EntityHashSize, EntityList, EntityRec, FontIndex, GRAPH, GraphHandle, GraphProc, HashIndex, LastEntityColor, NestedEntities, NestedEntitiesList, NestedEntitiesRec, NumberOfColors, NumberOfFonts, ROPE, SegmentDataList, TargetSpec, Text, TextRec, Texts, ValueList, Viewer, XY],
GraphCleanUp USING [CleanUpEL, CleanUpVL],
GraphFileKeys,
GraphOps USING [Lock, Unlock],
GraphPrivate USING [GraphStreamProc, MakeTable, PaintAll, PaintTails, ReadAsciiFile, RopeList, ShowChart],
GraphUtil USING [AppendEGL, AppendEntityList, AppendTexts, HandleNotNil, InitSegAll, InitSegEnd, NewEntityId, NewGroupId, NewTextId, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle],
IO USING [Close, EndOf, EndOfStream, Error, GetChar, GetIndex, int, PeekChar, PutFR, rope, STREAM],
List USING [DReverse, Kill],
RefText USING [New, ObtainScratch, ReleaseScratch],
Rope USING [Equal, Fetch, Find, FromRefText, IsEmpty, Length, Substr],
ViewerOps USING [DestroyViewer];
GraphRead:
CEDAR
PROGRAM
IMPORTS Atom, Commander, CommandTool, Convert, FileNames, FS, IO, List, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps
EXPORTS GraphPrivate = {
OPEN Graph, GraphFileKeys, GraphOps, GraphPrivate, GraphUtil;
ReadBeyondEOF: PUBLIC SIGNAL[message: ROPE ← NIL] = CODE;
SyntaxError: PUBLIC SIGNAL[message: ROPE ← NIL] = CODE;
DataError: PUBLIC SIGNAL[message: ROPE ← NIL] = CODE;
STREAM: TYPE = IO.STREAM;
BYTE: TYPE = Basics.BYTE;
CharPair: TYPE = MACHINE DEPENDENT RECORD[high, low: CHAR];
FourChars: TYPE = MACHINE DEPENDENT RECORD[lh, ll, hh, hl: CHAR];
FileFormat: TYPE = {old, binary, ascii, illegal}; -- currently only binary is supported.
HandleFromFile:
PUBLIC
PROC[file:
ROPE ←
NIL]
RETURNS [handle: GraphHandle ←
NIL, msg:
ROPE ←
NIL] = {
handle ← VanillaHandle[];
Lock[handle];
handle.userEditAllowed ← FALSE;
msg ← GetGraph[handle, file];
handle.userEditAllowed ← TRUE;
Unlock[handle];
}; -- HandleFromFile
GetGraph:
PUBLIC
PROC [handle: GraphHandle, file:
ROPE]
RETURNS [msg:
ROPE ←
NIL] = {
assume handle is already locked.
if no problem then return msg=nil.
IF file.IsEmpty[] THEN RETURN["No file specified."];
IF HandleNotNil[handle]
THEN {
s: STREAM ← NIL;
s ← FS. StreamOpen[file ! FS.Error => {msg ← error.explanation; CONTINUE} ];
IF msg =
NIL
THEN {
ENABLE {
SyntaxError => {
msg ←
IO.PutFR["Syntax error: %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
CONTINUE;
};
ReadBeyondEOF => {
msg ←
IO.PutFR["Attempting to read beyond end of file for %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
CONTINUE;
};
DataError => {
msg ←
IO.PutFR["Error: %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
CONTINUE;
};
IO.EndOfStream => {
msg ← "End of file reached";
CONTINUE;
};
IO.Error => {
msg ←
IO.PutFR["%g at [%g].",
IO.rope[
SELECT ec
FROM
SyntaxError => "Syntax error",
Overflow => "Overflow in input conversion"
ENDCASE => IO.PutFR["IO Error # %g", IO.int[LOOPHOLE[ec, CARDINAL]]]],
IO.int[s.GetIndex[]]
];
CONTINUE;
};
ABORTED => {
msg ← "Graph aborted. ... ";
CONTINUE;
};
};
IF handle.controller #
NIL
THEN
IF handle.controller.table #
NIL
THEN {
ViewerOps.DestroyViewer[handle.controller.table];
handle.controller.table ← NIL;
};
handle.userEditAllowed ← FALSE;
msg ←
SELECT CheckFormat[s]
FROM
old => ReadPlotFile[handle, s],
binary => ReadGraphFile[handle, s],
ascii => ReadAsciiFile[handle, s],
ENDCASE => "Illegal graph file.";
handle.userEditAllowed ← TRUE;
IF handle.controller #
NIL
THEN MakeTable[handle,
IF handle.controller.tableVisible THEN 0 ELSE 1000, 0];
};
IF s # NIL THEN s.Close[];
};
}; -- GetGraph
ReadPlotFile:
PROC[handle: GraphHandle, s:
STREAM]
RETURNS [msg:
ROPE ←
NIL] = {
OPEN handle;
assume handle is already locked.
nNamesMax: INT;
xEntity: Entity ← NIL;
entityGroup: EntityGroup ← NIL;
entityList: EntityList ← NIL;
v1, v2: ValueList ← NIL;
x1, x2: REAL ← 0.0;
texts: title, time, and file name.
title: Text ←
NEW[TextRec ← [
text: GetRope[s],
place: [0.5, 1.1], colorIndex: 15, fontIndex: 2, justifX: center, justifY: bottom,
id: NewTextId[handle, 0]]];
time: Text ←
NEW[TextRec ← [
text: Convert.RopeFromTime[GetTime[s]],
place: [1.0, -0.35], colorIndex: 15, fontIndex: 1, justifX: right, justifY: bottom,
id: NewTextId[handle, 1]]];
file: Text ←
NEW[TextRec ← [
text: (graph.fileName ← NARROW[Atom.GetPropFromList[s.propList, $Name]]),
place: [0, -0.35], colorIndex: 15, fontIndex: 1, justifX: left, justifY: bottom,
id: NewTextId[handle, 2]]];
graph.texts ← AppendTexts[graph.texts, CONS[title, CONS[time, CONS[file, NIL]]]];
allTexts ← AppendTexts[allTexts, CONS[title, CONS[time, CONS[file, NIL]]]];
bounds, nNamesMax, entityHash, and entityGroupList.
graph.bounds ← [GetReal[s], GetReal[s], GetReal[s], GetReal[s]];
graph.auto[bounds] ← FALSE;
nNamesMax ← GetCardinal[s];
xEntity ← NEW[EntityRec ← [name: "X", id: NewEntityId[handle, 0]]];
entityHash[xEntity.id MOD EntityHashSize] ← CONS[xEntity, entityHash[xEntity.id MOD EntityHashSize]];
entityGroup ←
NEW[EntityGroupRec ← [
x: xEntity,
ys: NEW[NestedEntitiesRec ← []],
id: NewGroupId[handle, 0] -- should update length later.
]];
xEntity.group ← entityGroup;
entityGroupList ← AppendEGL[entityGroupList, CONS[entityGroup, NIL]];
allocate EntityRec for each curve, construct the entity list entityGroupList.ys and graph.entityList, and create the graph viewer to show the axes, title and time.
FOR i:
INT
IN [1..nNamesMax]
DO
index: HashIndex ← i MOD EntityHashSize;
entity: Entity ←
NEW[EntityRec ← [
name: GetRope[s],
colorIndex: ((i-1) MOD LastEntityColor) + 1,
group: entityGroup,
id: NewEntityId[handle, i]
]];
entityHash[index] ← CONS[entity, entityHash[index]];
entityList ← CONS[entity, entityList];
ENDLOOP;
entityGroup.ys.entityList ← ReverseEntityList[entityList, FALSE];
graph.entityList ← AppendEntityList[graph.entityList, ReverseEntityList[entityList, FALSE]];
IF chart.viewer =
NIL
THEN {
ShowChart[handle]; -- will unlock handle right before viewer is opened up. No more (11/13/85).
Lock[handle];
}
ELSE PaintAll[handle];
add tails
entityGroup.length ← GetCardinal[s];
FOR i:
INT
IN [1..entityGroup.length]
DO
vl: ValueList;
x2 ← GetReal[s];
vl ← CONS[x2, NIL];
IF xEntity.lastValue = NIL THEN xEntity.oldValues ← xEntity.lastValue ← vl
ELSE {xEntity.lastValue.rest ← vl; xEntity.lastValue ← vl};
v2 ← NIL;
FOR el: EntityList ← ReverseEntityList[entityList,
FALSE], el.rest
UNTIL el =
NIL
DO
y2: REAL ← GetReal[s];
yEntity: Entity ← el.first;
v2 ← CONS[y2, v2];
vl ← CONS[y2, NIL];
IF yEntity.lastValue = NIL THEN yEntity.oldValues ← yEntity.lastValue ← vl
ELSE {yEntity.lastValue.rest ← vl; yEntity.lastValue ← vl};
ENDLOOP;
v2 ← ReverseValueList[v2];
IF i > 1 THEN PaintTails[handle, paint, v1, v2, x1, x2]; -- v1 and v2 are in reversed order.
v1 ← GraphCleanUp.CleanUpVL[v1];
v1 ← v2;
x1 ← x2;
ENDLOOP;
v1 ← NIL;
v2 ← GraphCleanUp.CleanUpVL[v2];
correct order of oldValues for xEntity and each entity on graph.entityList
xEntity.oldValues ← ReverseValueList[xEntity.oldValues];
FOR el: EntityList ← graph.entityList, el.rest UNTIL el = NIL DO
el.first.oldValues ← ReverseValueList[el.first.oldValues];
ENDLOOP;
initialize each segment data
InitSegEnd[xEntity];
FOR el: EntityList ← entityList, el.rest
UNTIL el =
NIL
DO
InitSegAll[el.first];
ENDLOOP;
lastTextId ← 1;
lastEntityId ← nNamesMax;
lastEntityGroupId is 0.
entityList ← GraphCleanUp.CleanUpEL[entityList, FALSE];
nNamesMax ← nNamesMax MOD LastEntityColor;
lastEntityColor ← IF nNamesMax = 0 THEN LastEntityColor ELSE nNamesMax;
handle should be unlocked by caller.
}; -- ReadPlotFile
ReadGraphFile:
PROC [handle: GraphHandle, s:
STREAM]
RETURNS [msg:
ROPE ←
NIL] = {
OPEN handle;
assume handle is already locked.
The format could have been more compact. But it is easier to allow for upward/backward compatibility this way.
graph.fileName ← NARROW[Atom.GetPropFromList[s.propList, $Name]];
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
NameKey => [] ← GetRope[s]; -- graph.fileName is already initialized above.
TextsKey => {
allTexts ← GetTexts[s, handle];
graph.texts ← ReverseTexts[allTexts, FALSE];
graph.texts ← ReverseTexts[graph.texts, TRUE];
};
CaretsKey => GetCarets[s, graph];
ShowSlopeKey => graph.showSlope ← GetBool[s];
TargetsKey => GetTargets[s, graph];
GridsKey => GetGrids[s, graph];
DivisionsKey => GetDivisions[s, graph];
BoundsKey => GetBounds[s, graph];
AutoKey => graph.auto ← [GetBool[s], GetBool[s]];
ColorsKey => GetColors[s, graph];
FontsKey => GetFonts[s, graph];
EntityGroupListKey => GetEntityGroupList[s, handle];
EndOfRecordKey => EXIT; -- later we can read more than one graphs at a time.
ENDCASE => SIGNAL SyntaxError["Graph"];
ENDLOOP;
initialize segments for x entities.
FOR egltemp: EntityGroupList ← entityGroupList, egltemp.rest
UNTIL egltemp =
NIL
DO
InitSegEnd[egltemp.first.x];
ENDLOOP;
draw some of the curves.
InitSomeCurves[handle];
IF handle.chart.viewer #
NIL
THEN {
UseMyColors[handle];
UseMyFonts[handle];
PaintAll[handle];
}
ELSE {
ShowChart[handle]; -- will unlock handle. no more, 11/13/85.
Lock[handle];
};
handle should be unlocked by caller of this proc.
}; -- ReadGraphFile
GetTexts:
PROC [s:
STREAM, handle: GraphHandle]
RETURNS [texts: Texts ←
NIL] = {
reads in graph.texts in the reversed order.
DO
SELECT GetByte[s]
FROM
EndOfListKey => EXIT;
TextKey => texts ← CONS[GetText[s, handle], texts]; -- 1
ENDCASE => SIGNAL SyntaxError["Texts"];
ENDLOOP;
texts ← ReverseTexts[texts, TRUE];
}; -- GetTexts
GetText:
PROC [s:
STREAM, handle: GraphHandle]
RETURNS [text: Text ←
NIL] = {
text ← NEW[TextRec ← []];
DO
SELECT GetByte[s]
FROM
NameKey => text.text ← GetRope[s]; -- 1
PlaceKey => text.place ← [GetReal[s], GetReal[s]]; -- 2
FontIndexKey => text.fontIndex ← GetByte[s]; -- 3
ColorIndexKey => text.colorIndex ← GetByte[s]; -- 4
RotationKey => text.rotation ← GetReal[s]; -- 5
JustificationsKey => {
-- 6
WHILE
NOT s.EndOf[]
DO
byte: BYTE;
SELECT GetByte[s]
FROM
XKey => {
-- 1
byte ← GetByte[s];
IF byte
IN [LeftKey..RightKey]
THEN text.justifX ←
SELECT byte
FROM
-- 1, 2, 3.
LeftKey => left, CenterKey => center, ENDCASE => right
ELSE SIGNAL SyntaxError["X Justification"];
};
YKey => {
-- 2
byte ← GetByte[s];
IF byte
IN [TopKey..BottomKey]
THEN text.justifY ←
SELECT byte
FROM
-- 1, 2, 3.
TopKey => top, CenterKey => center, ENDCASE => bottom
ELSE SIGNAL SyntaxError["Y Justification"];
};
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["Justifications"];
ENDLOOP;
};
TextIdKey => {text.id ← NewTextId[handle, GetInt[s]]}; -- 7
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["Text"];
ENDLOOP;
}; -- GetText
GetCarets: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [PrimaryKey..TextCaretKey] => {
-- 1, 2, 3.
index: CaretIndex ←
SELECT byte
FROM
PrimaryKey => primary, SecondaryKey => secondary, ENDCASE => text;
GetCaret[s, graph.caret[index]];
};
ENDCASE => SIGNAL SyntaxError["Carets"];
ENDLOOP;
}; -- GetCarets
GetCaret:
PROC [s:
STREAM, spec: CaretSpec] = {
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
PlaceKey => spec.place ← [GetReal[s], GetReal[s]]; -- 2
OnKey => spec.on ← GetBool[s]; -- 12
ENDCASE => SIGNAL SyntaxError["Caret"];
ENDLOOP;
}; -- GetCaret
GetTargets: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [XKey..YKey] => {
-- 1, 2.
xy: XY ← IF byte = XKey THEN x ELSE y;
GetTarget[s, graph.target[xy]];
};
ENDCASE => SIGNAL SyntaxError["Targets"];
ENDLOOP;
}; -- GetTargets
GetTarget:
PROC [s:
STREAM, spec: TargetSpec] = {
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
ValueKey => spec.value ← GetReal[s]; -- 1
WidthKey => spec.width ← GetReal[s]; -- 2
ColorIndexKey => spec.colorIndex ← GetByte[s]; -- 4, need proctection.
OnKey => spec.on ← GetBool[s]; -- 12
ENDCASE => SIGNAL SyntaxError["Target"];
ENDLOOP;
}; -- GetTarget
GetGrids: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [XKey..YKey] => {
-- 1, 2
xy: XY ← IF byte = XKey THEN x ELSE y;
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
OnKey => graph.grids[xy] ← GetBool[s]; -- 12
ENDCASE => SIGNAL SyntaxError["Grid"];
ENDLOOP;
};
ENDCASE => SIGNAL SyntaxError["Grids"];
ENDLOOP;
}; -- GetGrids
GetDivisions: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [XKey..YKey] => {
-- 1, 2
xy: XY ← IF byte = XKey THEN x ELSE y;
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
AutoKey => graph.auto[divisions] ← GetBool[s]; -- 9
DivisionsKey => graph.division[xy] ← GetByte[s]; -- 7
ENDCASE => SIGNAL SyntaxError["Division"];
ENDLOOP;
};
ENDCASE => SIGNAL SyntaxError["Divisions"];
ENDLOOP;
}; -- GetDivisions
GetBounds: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [XKey..YKey] => {
-- 1, 2
xy: XY ← IF byte = XKey THEN x ELSE y;
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
AutoKey => graph.auto[bounds] ← GetBool[s]; -- 9
MaxKey =>
IF xy = x
THEN graph.bounds.xmax ← GetReal[s]
-- 1
ELSE graph.bounds.ymax ← GetReal[s];
MinKey =>
IF xy = x
THEN graph.bounds.xmin ← GetReal[s]
-- 2
ELSE graph.bounds.ymin ← GetReal[s];
ENDCASE => SIGNAL SyntaxError["Bound"];
ENDLOOP;
};
ENDCASE => SIGNAL SyntaxError["Bounds"];
ENDLOOP;
}; -- GetBounds
GetColors: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [1..NumberOfColors] => {
index: ColorIndex ← byte - 1;
GetColor[s, graph, index];
};
ENDCASE => SIGNAL SyntaxError["Colors"];
ENDLOOP;
}; -- GetColors
GetColor:
PROC [s:
STREAM, graph:
GRAPH, index: ColorIndex] = {
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
RKey => graph.color[index].R ← GetReal[s]; -- 1
GKey => graph.color[index].G ← GetReal[s]; -- 2
BKey => graph.color[index].B ← GetReal[s]; -- 3
ENDCASE => SIGNAL SyntaxError["Color"];
ENDLOOP;
}; -- GetColor
GetFonts: GraphStreamProc = {
byte: BYTE;
WHILE
NOT s.EndOf[]
DO
SELECT (byte ← GetByte[s])
FROM
EndOfRecordKey => EXIT;
IN [1..NumberOfFonts] => {
index: FontIndex ← byte - 1;
GetFont[s, graph, index];
};
ENDCASE => SIGNAL SyntaxError["Fonts"];
ENDLOOP;
}; -- GetFonts
GetFont: PROC [s: STREAM, graph: GRAPH, index: FontIndex] = {
WHILE
NOT s.EndOf[]
DO
SELECT GetByte[s]
FROM
EndOfRecordKey => EXIT;
NameKey => graph.font[index].family ← GetRope[s]; -- 1
BoldKey => graph.font[index].bold ← GetBool[s]; -- 2
ItalicKey => graph.font[index].italic ← GetBool[s]; -- 3
VFontSizeKey => graph.font[index].vFontSize ← GetInt[s]; -- 4
PFontScaleKey => graph.font[index].pFontScale ← GetReal[s]; -- 5
ENDCASE => SIGNAL SyntaxError["Font"];
ENDLOOP;
}; -- GetFonts
GetEntityGroupList:
PROC [s:
STREAM, handle: GraphHandle] = {
OPEN handle;
egl, t1, t2: EntityGroupList ← NIL;
DO
SELECT GetByte[s]
FROM
EndOfListKey => EXIT;
EntityGroupKey => t1 ← CONS[GetEntityGroup[s, handle], t1]; -- 4
ENDCASE => SIGNAL SyntaxError["EntityGroupList"];
ENDLOOP;
reverse it.
FOR t2 ← t1, t2.rest
UNTIL t2 =
NIL
DO
egl ← CONS[t2.first, egl];
ENDLOOP;
append it
entityGroupList ← AppendEGL[entityGroupList, egl];
cleanup t1
TRUSTED {List.Kill[LOOPHOLE[t1]]};
}; -- GetEntityGroupList
GetEntityGroup:
PROC [s:
STREAM, handle: GraphHandle]
RETURNS [eg: EntityGroup] = {
eg ← NEW[EntityGroupRec ← []];
DO
SELECT GetByte[s]
FROM
NameKey => eg.name ← GetRope[s]; -- 1
EntityKey => eg.x ← GetEntity[s, handle, eg, NIL]; -- 2
NestedEntitiesKey => eg.ys ← GetNestedEntities[s, handle, eg, NIL]; -- 5
GroupIdKey => eg.id ← NewGroupId[handle, GetInt[s]]; -- 6
LengthKey => eg.length ← GetInt[s]; -- 7
CrossSectionsKey => GetCrossSections[s, eg];
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["EntityGroup"];
ENDLOOP;
GetEntityList:
PROC [s:
STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities]
RETURNS [el: EntityList ←
NIL] = {
-- without getting the values, for now.
DO
SELECT GetByte[s]
FROM
EndOfListKey => EXIT;
EntityKey => el ← CONS[GetEntity[s, handle, group, father], el]; -- 2
ENDCASE => SyntaxError["EntityList"];
ENDLOOP;
el ← ReverseEntityList[el, TRUE]; -- resume the order.
}; -- GetEntityList
GetEntity:
PROC [s:
STREAM, handle: GraphHandle, eg: EntityGroup, father: NestedEntities ←
NIL]
RETURNS [entity: Entity ←
NIL] = {
hashIndex: [0..EntityHashSize);
entity ← NEW[EntityRec ← [group: eg, parent: father]];
DO
SELECT GetByte[s]
FROM
NameKey => entity.name ← GetRope[s]; -- 1
CommentKey => entity.comment ← GetRope[s]; -- 11
ColorIndexKey => entity.colorIndex ← GetByte[s]; -- 4
MarkKey => {
-- 3
byte: BYTE ← GetByte[s];
IF byte
IN [NoneKey..PercentKey]
THEN
-- 1 .. 7
entity.mark ←
SELECT byte
FROM
NoneKey => none, RoundKey => round, SquareKey => square,
DiamondKey => diamond, CrossKey => cross, DollarKey => dollar,
ENDCASE => percent
ELSE SIGNAL SyntaxError["Mark"];
};
WidthKey => entity.width ← GetReal[s]; -- 2
ValuesKey => [entity.oldValues, entity.lastValue] ← GetValueList[s]; -- 5
EntityIdKey => entity.id ← NewEntityId[handle, GetInt[s]]; -- 7
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["Entity"];
ENDLOOP;
hashIndex ← entity.id MOD EntityHashSize;
handle.entityHash[hashIndex] ← CONS[entity, handle.entityHash[hashIndex]];
}; -- GetEntity
GetValueList:
PROC [s:
STREAM]
RETURNS [vl, last: ValueList ←
NIL] = {
length: INT ← GetInt[s];
FOR i:
INT
IN [0..length)
DO
vl ← CONS[GetReal[s], vl];
ENDLOOP;
[vl, last] ← ReverseValueList[vl, TRUE];
GetNestedEntitiesList:
PROC [s:
STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities ←
NIL]
RETURNS [nel: NestedEntitiesList ←
NIL] = {
tnel: NestedEntitiesList ← NIL;
DO
SELECT GetByte[s]
FROM
EndOfListKey => EXIT;
NestedEntitiesKey => tnel ←
CONS[
-- 5
GetNestedEntities[s, handle, group, father], tnel];
ENDCASE => SyntaxError["NestedEntitiesList"];
ENDLOOP;
reverse it.
FOR ttnel: NestedEntitiesList ← tnel, ttnel.rest
UNTIL ttnel =
NIL
DO
nel ← CONS[ttnel.first, nel];
ENDLOOP;
clean up.
WHILE tnel #
NIL
DO
next: NestedEntitiesList ← tnel.rest;
tnel.first ← NIL;
tnel.rest ← NIL;
tnel ← next;
ENDLOOP;
}; -- GetNestedEntitiesList
GetNestedEntities:
PROC [s:
STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities ←
NIL]
RETURNS [ne: NestedEntities] = {
ne ← NEW[NestedEntitiesRec ← [parent: father]];
DO
SELECT GetByte[s]
FROM
NameKey => ne.name ← GetRope[s]; -- 1
CommentKey => ne.comment ← GetRope[s]; -- 11
EntityListKey => ne.entityList ← GetEntityList[s, handle, group, father]; -- 3
NELKey => ne.children ← GetNestedEntitiesList[s, handle, group, ne]; -- 4
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["NestedEntities"];
ENDLOOP;
}; -- GetNestedEntities
GetCrossSections:
PROC [s:
STREAM, group: EntityGroup] = {
AddValuesToEL:
PROC [entityList: EntityList] = {
FOR el: EntityList ← entityList, el.rest
UNTIL el =
NIL
DO
vl: ValueList ← CONS[GetReal[s], NIL];
el.first.lastValue.rest ← vl;
el.first.lastValue ← vl;
ENDLOOP;
}; -- AddValuesToEL
AddValuesToNEL:
PROC [nestedEntityList: NestedEntitiesList] = {
FOR nel: NestedEntitiesList ← nestedEntityList, nel.rest
UNTIL nel =
NIL
DO
AddValuesToEL[nel.first.entityList];
AddValuesToNEL[nel.first.children];
ENDLOOP;
}; -- AddValuesToNEL
DO
-- error if eof is hit inside the loop;
SELECT GetByte[s]
FROM
EndOfListKey => EXIT;
TailsKey => {
-- 8
vl: ValueList ← CONS[GetReal[s], NIL];
group.x.lastValue.rest ← vl;
group.x.lastValue ← vl;
AddValuesToEL[group.ys.entityList];
AddValuesToNEL[group.ys.children];
};
ENDCASE => SyntaxError["CrossSection"];
ENDLOOP;
}; -- GetCrossSections
InitSomeCurves: GraphProc = {
OPEN handle;
InitSomeEntity:
PROC [ne: NestedEntities, graf:
GRAPH, count:
INT]
RETURNS [newCount:
INT] = {
newCount ← count;
FOR el: EntityList ← ne.entityList, el.rest
UNTIL el = NIL OR newCount >= LastEntityColor DO
entity: Entity ← el.first;
InitSegAll[entity];
graf.entityList ← CONS[entity, graf.entityList];
newCount ← newCount + 1;
ENDLOOP;
FOR nel: NestedEntitiesList ← ne.children, nel.rest
UNTIL nel = NIL OR newCount >= LastEntityColor DO
newCount ← InitSomeEntity[nel.first, graf, newCount];
ENDLOOP;
}; -- InitSomeEntity
curveCount: CARDINAL ← 0;
FOR egl: EntityGroupList ← entityGroupList, egl.rest
UNTIL egl =
NIL
OR curveCount >= LastEntityColor
DO
curveCount ← InitSomeEntity[egl.first.ys, graph, curveCount];
ENDLOOP;
graph.entityList ← ReverseEntityList[graph.entityList, TRUE];
}; -- InitSomeCurves
CheckFormat:
PROC [stream:
STREAM]
RETURNS[format: FileFormat ← illegal] = {
firstChar: CHAR ← stream.PeekChar[];
IF firstChar = 0C
THEN {
rope: ROPE;
rope ← GetRope[stream! ReadBeyondEOF => {rope ← NIL; CONTINUE}];
IF
NOT rope.IsEmpty[]
THEN
SELECT
TRUE
FROM
rope.Equal["* binary"] => format ← old;
rope.Equal["binary graph"] => format ← binary;
ENDCASE; -- illegal
}
ELSE format ← ascii;
}; -- CheckFormat
reading routines
scratch for GetRope
myScratch: REF TEXT ← RefText.New[256];
GetRope:
PROC [stream:
STREAM]
RETURNS [rope:
ROPE] = {
length: NAT = GetCardinal[stream];
text: REF TEXT ← IF length > 256 THEN RefText.ObtainScratch[length] ELSE myScratch;
FOR i:
NAT
IN [0..length)
DO
IF stream.EndOf[]
THEN
SIGNAL ReadBeyondEOF[
IO.PutFR["%gth character of a rope of length %g.", IO.int[i+1], IO.int[length]]];
text[i] ← stream.GetChar[];
ENDLOOP;
text.length ← length;
rope ← Rope.FromRefText[text];
IF length > 256 THEN RefText.ReleaseScratch[text];
}; -- GetRope
GetTime:
PROC [stream:
STREAM]
RETURNS [BasicTime.
GMT] = {
RETURN[LOOPHOLE[GetLongWord[stream, " a BasicTime.GMT"], BasicTime.GMT]];
}; -- GetTime
GetBool:
PROC [stream:
STREAM]
RETURNS [bool:
BOOL ←
FALSE] = {
SELECT GetByte[stream]
FROM
TrueKey => bool ← TRUE;
FalseKey => bool ← FALSE;
ENDCASE => SIGNAL SyntaxError["Boolean"];
}; -- GetBool
GetByte:
PROC [stream:
STREAM]
RETURNS [
BYTE] = {
RETURN[LOOPHOLE[stream.GetChar[], BYTE]];
}; -- GetByte
GetInt:
PROC [stream:
STREAM]
RETURNS [
INT] = {
RETURN[LOOPHOLE[GetLongWord[stream, " an INT"], INT]];
}; -- GetInt
GetReal:
PROC [stream:
STREAM]
RETURNS [
REAL] = {
RETURN[LOOPHOLE[GetLongWord[stream, " a real number"], REAL]];
}; -- GetReal
GetCardinal:
PROC [stream:
STREAM]
RETURNS [
CARDINAL] = {
pair: CharPair;
pair.high ← MyGetChar[stream, "1st byte of a cardinal"];
pair.low ← MyGetChar[stream, "2nd byte of a cardinal"];
RETURN[LOOPHOLE[pair, CARDINAL]];
}; -- GetCardinal
GetLongWord:
PROC [stream:
STREAM, info:
ROPE]
RETURNS [chars: FourChars] = {
chars.lh ← MyGetChar[stream, "1st byte of", info];
chars.ll ← MyGetChar[stream, "2nd byte of", info];
chars.hh ← MyGetChar[stream, "3rd byte of", info];
chars.hl ← MyGetChar[stream, "4th byte of", info];
}; -- GetLongWord
MyGetChar:
PROC [stream:
STREAM, info1, info2:
ROPE ←
NIL]
RETURNS [
CHAR] = {
IF stream.EndOf[]
THEN
SIGNAL ReadBeyondEOF[
IO.PutFR["%g%g.", IO.rope[info1], IO.rope[info2]]];
RETURN[stream.GetChar[]];
}; -- MyGetChar
GraphFiles: Commander.CommandProc = {
argList: RopeList;
length: NAT;
msg ← NIL;
[argList, length] ← CommandTool.ParseToList[cmd];
IF length <= 0 THEN msg ← "To review graph files, type: Graph <files>"
ELSE {
wDir: ROPE ← FileNames.CurrentWorkingDirectory[];
nGraphs: NAT ← 0;
maxGraphs: NAT ← 12;
allVersions: BOOL ← FALSE;
FOR arg: RopeList ← argList, arg.rest
UNTIL arg =
NIL
OR msg #
NIL
DO
IF arg.first.Fetch[0] = '-
THEN {
IF arg.first.Length[] >= 2
THEN
SELECT arg.first.Fetch[1]
FROM
'A, 'a => allVersions ← TRUE;
'H, 'h => allVersions ← FALSE;
IN ['0..'9] => {
max:
NAT ← Convert.CardFromRope[arg.first.Substr[1]
! Convert.Error => max ← maxGraphs]; -- don't change it
maxGraphs ← max;
};
ENDCASE; -- simply ignors illegal switches
}
ELSE {
fileList: RopeList ← FileListFrom[arg.first, wDir, allVersions];
IF fileList = NIL THEN msg ← "No such file."
ELSE
FOR file: RopeList ← fileList, file.rest
UNTIL file =
NIL
OR msg #
NIL
DO
IF nGraphs >= maxGraphs
THEN msg ←
IO.PutFR[
"Note: max. %g plots for each command. You may change it by a switch.",
IO.int[maxGraphs]]
ELSE [, msg] ← HandleFromFile[file: file.first];
nGraphs ← nGraphs + 1;
ENDLOOP;
};
ENDLOOP;
};
IF msg = NIL THEN msg ← "done.";
}; -- GraphFiles
FileListFrom:
PROC [pattern, wDir:
ROPE ←
NIL, allVersions:
BOOL ←
FALSE]
RETURNS [fileList: RopeList ←
NIL] = {
root, lastRoot: ROPE ← NIL;
LinkIt:
FS.NameProc
--
PROC [fullFName]
RETURNS [continue:
BOOL] -- = {
excl: INT ← fullFName.Find["!"];
continue ← TRUE;
IF fullFName.Substr[excl-6, 6].Equal[".press", FALSE] THEN RETURN;
IF ~allVersions
THEN {
root ← fullFName.Substr[0, excl];
IF root.Equal[lastRoot,
FALSE]
THEN {
fileList.first ← fullFName;
RETURN;
};
lastRoot ← root;
};
fileList ← CONS[fullFName, fileList];
}; -- LinkIt
FS.EnumerateForNames[pattern, LinkIt, wDir];
TRUSTED {fileList ← LOOPHOLE[List.DReverse[LOOPHOLE[fileList]]]};
}; -- FileListFrom
Commander.Register["Graph", GraphFiles, "Graph <graph file list>, reviews the graphs in the files. See GraphDoc.tioga for more information."];
and for historical reasons
Commander.Register["Plot", GraphFiles, "Plot <graph file list>, reviews the graphs in the files. See GraphDoc.tioga for more information."];
}.
CHANGE
LOG.
SChen, created at October 9, 1985 6:21:05 pm PDT.