GraphRead.mesa, Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, November 20, 1985 2:16:46 pm 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],
GraphFileKeys,
GraphOps USING [AddCrossSection, AddText, EnlistEntity, Lock, NewGraph, Unlock],
GraphPrivate USING [BYTE, CharPair, FourChars, GraphStreamProc, MakeTable, PaintAll, ReadAsciiFile, RopeList, ShowChart, STREAM],
GraphUtil USING [AppendEGL, HandleNotNil, InitSegAll, InitSegEnd, NewEntityId, NewGroupId, NewTextId, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle],
IO USING [Close, EndOf, EndOfStream, Error, GetChar, GetIndex, int, PeekChar, PutFR, rope],
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, GraphOps, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps
EXPORTS GraphPrivate = {
OPEN Graph, GraphFileKeys, GraphOps, GraphPrivate, GraphUtil;
ReadBeyondEOF: PUBLIC SIGNAL[message: ROPENIL] = CODE;
SyntaxError: PUBLIC SIGNAL[message: ROPENIL] = CODE;
DataError: PUBLIC SIGNAL[message: ROPENIL] = CODE;
FileFormat: TYPE = {old, binary, ascii, illegal};
HandleFromFile: PUBLIC PROC[file: ROPENIL] RETURNS [handle: GraphHandle ← NIL, msg: ROPENIL] = {
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: ROPENIL] = {
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: STREAMNIL;
s ← FS. StreamOpen[file ! FS.Error => {msg ← error.explanation; CONTINUE} ];
IF msg = NIL THEN {
ENABLE {
SyntaxError => {
msg ← IO.PutFR["Syntax errorat [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]];
CONTINUE;
};
ReadBeyondEOF => {
msg ← IO.PutFR["End of file at [%g], reading %g.",
IO.int[s.GetIndex[]], IO.rope[message]];
CONTINUE;
};
DataError => {
msg ← IO.PutFR["Error at [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]];
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
IF handle.controller.table = 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: ROPENIL] = { OPEN handle;
assume handle is already locked.
groupId, valuesLength, nCurves: INT;
title, time, and file name.
title: ROPE ← GetRope[s];
time: ROPE ← Convert.RopeFromTime[GetTime[s]];
file: ROPENARROW[Atom.GetPropFromList[s.propList, $Name]];
create graph with bounds
[handle, groupId] ← NewGraph[
fileName: file,
groupName: NIL,
comment: file,
xName: "X",
autoBounds: FALSE,
bounds: [GetReal[s], GetReal[s], GetReal[s], GetReal[s]],
oldGraph: handle
];
add texts
[] ← GraphOps.AddText[handle: handle,
rope: title, place: [0.5, 1.1], fontIndex: 2, justifX: center, justifY: bottom];
[] ← GraphOps.AddText[handle: handle,
rope: time, place: [1.0, -0.35], fontIndex: 1, justifX: right, justifY: bottom];
[] ← GraphOps.AddText[handle: handle,
rope: file, place: [0, -0.35], fontIndex: 1, justifX: left, justifY: bottom];
enlist curves
nCurves ← GetCardinal[s];
FOR i: INT IN [1..nCurves] DO
[] ← EnlistEntity[handle: handle, groupId: groupId, name: GetRope[s],
colorIndex: ((i-1) MOD LastEntityColor) + 1];
ENDLOOP;
add cross-sections
valuesLength ← GetCardinal[s];
FOR i: INT IN [1..valuesLength] DO
x: REAL ← GetReal[s];
yvalues: ValueList ← NIL;
FOR j: INT IN [1..nCurves] DO
yvalues ← CONS[GetReal[s], yvalues];
ENDLOOP;
AddCrossSection[handle, x, yvalues, groupId];
ENDLOOP;
}; -- ReadPlotFile
ReadPlotFile: PROC[handle: GraphHandle, s: STREAM] RETURNS [msg: ROPENIL] = { 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 ← [comment: graph.fileName]],
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: ROPENIL] = {
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 ShowChart[handle]
ELSE {
UseMyColors[handle];
UseMyFonts[handle];
PaintAll[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: XYIF 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: XYIF 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: XYIF 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: XYIF 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;
}; -- GetEntityGroup
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];
}; -- GetValueList
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 TEXTIF 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: BOOLFALSE] = {
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: ROPENIL] 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: BOOLFALSE;
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: ROPENIL, allVersions: BOOLFALSE] RETURNS [fileList: RopeList ← NIL] = {
root, lastRoot: ROPENIL;
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.