GraphFile.mesa, Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, November 7, 1985 11:30:23 pm PST
DIRECTORY
Atom USING [GetPropFromList],
Basics USING [BYTE, LowHalf],
BasicTime USING [GMT, nullGMT],
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, EntityGroupRec, EntityList, EntityRec, FontIndex, GRAPH, LastEntityColor, NestedEntities, NestedEntitiesList, NestedEntitiesRec, NumberOfColors, NumberOfFonts, ROPE, SegmentDataList, TargetSpec, Text, TextRec, Texts, ValueList, Viewer, XY],
GraphCleanUp USING [CleanUpVL],
GraphFileKeys,
GraphOps USING [AddCurve, CreateGraph, SetXValues];
GraphPrivate USING [EntityGroupList, EntityHashSize, GraphHandle, GraphProc, Lock, MakeTable, PaintAll, PaintTails, ShowChart, Unlock],
GraphUtil USING [FullName, HandleFromViewer, HandleNotNil, InitSegAll, InitSegEnd, LengthOfSDL, LengthOfVL, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle],
IO USING [Close, EndOf, Error, Flush, GetChar, GetIndex, int, PutBlock, PutChar, PutFR, rope, STREAM],
List USING [DReverse, Kill],
RefText USING [New, ObtainScratch, ReleaseScratch],
Rope USING [Equal, Fetch, Find, FromRefText, IsEmpty, Length, Substr, ToRefText],
ViewerClasses USING [Viewer],
ViewerOps USING [DestroyViewer, PaintViewer];
GraphFile: CEDAR PROGRAM
IMPORTS Atom, Basics, Commander, CommandTool, Convert, FileNames, FS, IO, List, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps
EXPORTS GraphPrivate = {
OPEN Graph, GraphFileKeys, GraphPrivate, GraphUtil;
ReadBeyondEOF: SIGNAL[message: ROPENIL];
SyntaxError: SIGNAL[message: ROPENIL];
DataError: SIGNAL[message: ROPENIL];
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, text, illegal}; -- currently only binary is supported.
RopeList: TYPE = LIST OF ROPE;
scratch for rope
myScratch: REF TEXT ← RefText.New[256];
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
HandleFromFile: PUBLIC PROC[file: ROPENIL] RETURNS [handle: GraphHandle ← NIL, msg: ROPENIL] = {
handle ← VanillaHandle[];
Lock[handle];
handle.userEditAllowed ← FALSE;
msg ← GetGraph[handle, file, FALSE];
handle.userEditAllowed ← TRUE;
Unlock[handle];
}; -- HandleFromFile
GetGraph: PUBLIC PROC [handle: GraphHandle ← NIL, file: ROPENIL] RETURNS [msg: ROPENIL] = {
assume handle is already locked.
if no problem then return msg=nil.
IF HandleNotNil[handle] THEN {
s: STREAMNIL;
ok: BOOLTRUE;
IF file.IsEmpty[] THEN RETURN["No file specified."];
s ← FS. StreamOpen[file ! FS.Error => {msg ← error.explanation; ok ← FALSE; CONTINUE} ];
IF ok THEN {
ENABLE {
SyntaxError => {
msg ← IO.PutFR["Syntax error: %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
};
ReadBeyondEOF => {
msg ← IO.PutFR["Attempting to read beyond end of file for %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
};
DataError => {
msg ← IO.PutFR["Error: %g at [%g].",
IO.rope[message],
IO.int[s.GetIndex[]]
];
};
IO.EndOfStream => {
msg ← End of file reached;
};
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[]]
];
};
ABORTED => {
msg ← "Graph aborted. ... ";
};
CONTINUE;
};
IF handle.controller # NIL THEN IF handle.controller.table # NIL THEN ViewerOps.DestroyViewer[handle.controller.table];
handle.userEditAllowed ← FALSE;
msg ← SELECT CheckFormat[s] FROM
old => ReadPlotFile[handle, s],
binary => ReadGraphFile[handle, s],
text => ProcessesTextFile[handle, s],
ENDCASE => "Illegal graph file.";
handle.userEditAllowed ← TRUE;
IF handle.controller # NIL THEN MakeTable[handle];
};
IF s # NIL THEN s.Close[];
};
}; -- GetGraph
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, FALSE]]];
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, FALSE]]];
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, FALSE]]];
AppendTexts[graph.texts, CONS[title, CONS[time, CONS[file, NIL]]]];
AppendTexts[allTexts, CONS[title, CONS[time, CONS[file, NIL]]]];
bounds, nNamesMax, entityHash, and entityGroupList.
graph.bounds ← [GetReal[s], GetReal[s], GetReal[s], GetReal[s]];
nNamesMax ← GetCardinal[s];
xEntity ← NEW[EntityRec ← [name: "X", id: NewEntityId[handle, 0, FALSE]]];
entityHash[xEntity.id MOD EntityHashSize] ← CONS[xEntity, entityHash[xEntity.id MOD EntityHashSize]];
entityGroup ← NEW[EntityGroupRec ← [
x: xEntity,
ys: NEW[NestedEntitiesRec ← []],
id: NewGroupId[handle, 0, FALSE] -- should update length later.
]];
xEntity.group ← entityGroup;
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, FALSE]
]];
entityHash[index] ← CONS[entity, entityHash[index]];
entityList ← CONS[entity, entityList];
ENDLOOP;
entityGroup.ys.entityList ← ReverseEntityList[entityList, FALSE];
AppendEntityList[graph.entityList, ReverseEntityList[entityList, FALSE]];
IF chart.viewer = NIL THEN {
ShowChart[handle]; -- will unlock handle right before viewer is opened up.
Lock[handle];
}
ELSE PaintAll[handle];
add tails
entityGroup.length ← GetCardinal[s];
FOR i: INT IN [1..entityGroup.length] DO
x2 ← GetReal[s];
vl: ValueList ← 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.
GraphCleanUp.CleanUpEL[entityList, FALSE];
nNamesMax ← nNamesMax MOD LastEntityColor;
lastEntityColor ← IF nNamesMax = 0 THEN LastEntityColor ELSE nNamesMax;
handle will be unlocked.
}; -- 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]; -- graph.fileName is already initialized above.
TextsKey => {
allTexts ← GetTexts[s];
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.
Lock[handle];
};
handle shall be unlocked by caller of this proc.
}; -- ReadGraphFile
GraphFileProc: TYPE = PROC [s: STREAM, graph: GRAPH];
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], FALSE]}; -- 7
EndOfRecordKey => EXIT;
ENDCASE => SIGNAL SyntaxError["Text"];
ENDLOOP;
}; -- GetText
GetCarets: GraphFileProc = {
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: GraphFileProc = {
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: GraphFileProc = {
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: GraphFileProc = {
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: GraphFileProc = {
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: GraphFileProc = {
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: GraphFileProc = {
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
IF handle.entityGroupList = NIL THEN handle.entityGroupList ← egl
ELSE FOR t2 ← handle.entityGroupList, t2.rest UNTIL t2 = NIL DO
IF t2.rest = NIL THEN t2.rest ← egl;
ENDLOOP;
cleanup t1
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[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], FALSE]; -- 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[valueList, 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
ProcessesTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPENIL] = {
key: ROPE ← s.GetID[ ! IO.EndOfStream => SIGNAL ReadBeyondEOF["first id in text file"]];
IF key.Equal["graph", FALSE] THEN ReadTextFile[handle, s]
ELSE {
s.SetIndex[0];
ReadSimpleTextFile[handle, s];
};
}; -- ReadTextFile
ReadTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPENIL] = {
read more complicated graph file in text format.
msg ← "Not implemented yet."
}; -- ReadGraphData
ReadSimpleTextFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPENIL] = {
buffer: REF TEXT ← RefText.Scratch[512];
tokenKind: IO.TokenKind ← tokenROPE;
token: REF TEXT;
charsSkipped: INT;
error: TokenError;
get, ok: BOOLTRUE;
key: ROPENIL;
nNames, specifiedN: INT ← 0;
nNamesSpeicified: BOOLFALSE;
names, lastName: LIST OF ROPENIL;
xList: ValueList ← NIL;
lvl, tlvl: LIST OF ValueList ← NIL;
r: REAL;
int: INT ← 0;
keyRope, tokenRope: ROPE;
key: {namesKey, nNamesKey, dataKey, noneKey} ← noneKey;
xmin, xmax, ymin, ymax: REAL;
iRow, iCol: INT ← 0; -- last row/col read so far.
InitData: PROC [] = {
check number of names.
IF nNamesSpeicified THEN {
IF names = NIL THEN {
FOR i: INT IN [0..nNamesSpeicified) DO
names ← CONS[NIL, names];
ENDLOOP;
nNames ← specifiedN;
};
ELSE IF nNames # specifiedN THEN SIGNAL DataError[
IO.PutFR["%g names are specified but %g names are listed",
IO.int[specifiedN],
IO.int[nNames]
]];
}
ELSE {
IF nNames = 0 THEN SIGNAL DataError[
"Number of curves unknown before reading data"];
};
xmin ← ymin ← Real.LargestNumber;
xmax ← ymax ← -xmin;
FOR j: INT IN [0..nNames) DO lvl ← CONS[NIL, lvl]; ENDLOOP;
tlvl ← lvl;
}; -- InitData
UNTIL key = dataKey DO
get key word: names, nNames, or data.
IF get THEN {
[token, ] ← s.GetToken[buffer: buffer];
tokenRope ← Rope.FromRefText[token];
};
get ← TRUE;
key ← SELECT TRUE FROM
tokenRope.Equal["nNames", FALSE] => nNamesKey,
tokenRope.Equal["data", FALSE] => dataKey,
tokenRope.Equal["names", FALSE] => namesKey,
ENDCASE => SIGNAL SyntaxError["unknown key word"];
IF key = dataKey THEN InitData[];
DO
[tokenKind, token, charsSkipped, error] ← s.GetCedarToken[buffer];
IF tokenKind = tokenEOF OR error # none THEN {
status: ROPE;
IF key = dataKey AND iCol = nNames THEN EXIT;
status ← SELECT key FROM
dataKey => IO.PutFR[
"after getting the %g-th number on the %g-th row in the table.",
IO.int[iCol], IO.int[iRow+1]],
namesKey => "getting names",
ENDCASE => "getting the number of names";
IF tokenKind = tokenEOF THEN SIGNAL ReadBeyondEOF[status]
ELSE SIGNAL DataError[Rope.Concat["token error in ", status]];
};
tokenRope ← Rope.FromRefText[token];
IF tokenKind = tokenCHAR THEN LOOP
ELSE IF tokenKind = tokenID THEN {get ← FALSE; EXIT};
ELSE SELECT key FROM
namesKey =>
IF tokenKind = tokenROPE THEN {
nl: LIST OF ROPECONS[tokenRope, NIL];
IF lastName = NIL THEN lastName ← names ← nl
ELSE {lastName.rest ← nl; lastName ← nl};
nNames ← nNames + 1;
IF nNamesSpeicified AND nNames > specifiedN THEN SIGNAL DataError[
IO.PutFR["%g names are specified but %g names are listed",
IO.int[specifiedN],
IO.int[nNames]
];
}
ELSE SIGNAL SyntaxError [IO.PutFR["after getting the %g-th name", IO.int[nNames]];
};
nNamesKey => {
ENABLE Convert.Error => SIGNAL DataError["number of names"];
base: [2..36] ← SELECT tokenKind FROM
tokenDECIMAL => 10,
tokenOCTAL => 8,
tokenHEX => 16,
ENDCASE => 2;
number: INT ← Convert.IntFromRope[tokenRope, base];
IF names # NIL AND nNames # number THEN SIGNAL DataError[
IO.PutFR["%g names are specified but %g names are listed",
IO.int[number],
IO.int[nNames]
];
specifiedN ← number;
nNamesSpecified ← TRUE;
EXIT;
};
ENDCASE => { -- dataKey
ENABLE Convert.Error => SIGNAL DataError[IO.PutFR[
"parsing the %g-th number on the %g-th row",
IO.int[iCol], IO.int[iRow+1]];
IF iCol = nNames THEN {iCol ← 1; iRow ← iRow + 1}
ELSE iCol ← iCol + 1;
r ← Convert.RealFromRope[tokenRope];
IF iCol = 1 THEN {
xList ← CONS[r, xList];
IF iRow = 0 THEN xmin ← r
ELSE xmax ← r;
}
ELSE {
tlvl.first ← CONS[r, tlvl.first];
ymax ← MAX[ymax, r];
ymin ← MIN[ymin, r];
tlvl ← tlvl.rest;
};
};
ENDLOOP;
ENDLOOP;
IF ymax = ymin THEN {
IF ymax = 0.0 THEN { ymax ← 1.0; ymin ← -1.0 }
ELSE { ymax ← ymax + ABS[ymax]; ymin ← ymin - ABS[ymin]; };
};
IF xmax = xmin THEN {
IF xmax = 0.0 THEN { xmax ← 1.0; xmin ← -1.0 }
ELSE { xmax ← xmax + ABS[xmax]; xmin ← xmin - ABS[xmin]; };
};
handle ← GraphOps.CreateGraph[
bounds: [xmin, ymin, xmax, ymax],
fileName: fullName
];
[] ← GraphOps.AddText[handle: handle,
id: 0, rope: title, place: [0.5, 1.1], fontIndex: 2, justifX: center];
[] ← GraphOps.SetXValues[handle, xList];
iCurve ← nNames;
TRUSTED {
lvl ← LOOPHOLE[List.DReverse[LOOPHOLE[lvl]]];
names ← LOOPHOLE[List.DReverse[LOOPHOLE[names]]];
};
tlvl ← lvl;
FOR lastName ← names, lastName.rest UNTIL lastName = NIL DO
[] ← GraphOps.AddCurve[handle, iCurve, tlvl.first, lastName.first, iCurve];
iCurve ← iCurve - 1;
tlvl ← tlvl.rest;
ENDLOOP;
TRUSTED {
List.Kill[LOOPHOLE[lvl]];
List.Kill[LOOPHOLE[names]];
};
out.PutF["done.\n"];
}; -- ReadSimpleGraphData
SaveGraph: PUBLIC PROC[viewer: Viewer, file: ROPENIL, plottedOnly: BOOLTRUE] RETURNS [msg: ROPENIL] = {
IF file.IsEmpty[] THEN msg ← "Please select a file name."
ELSE IF file.Find["/", 1] > 1 OR file.Find["]"] > 1 THEN msg ← "Can not write a remote file."
ELSE {
handle: GraphHandle ← HandleFromViewer[viewer];
newName: ROPE;
[msg, newName] ← WriteGraphFile[handle, file, plottedOnly];
IF plottedOnly THEN IF msg = NIL AND handle.chart.viewer # NIL THEN {
handle.chart.viewer.name ← newName;
ViewerOps.PaintViewer[handle.chart.viewer, caption, FALSE, NIL];
};
};
}; -- SaveGraph
WriteGraphFile: PUBLIC PROC [handle: GraphHandle ← NIL, file: ROPENIL, plottedOnly: BOOLTRUE] RETURNS[msg, newName: ROPENIL] = {
IF handle = NIL THEN RETURN["handle = NIL IN WriteGraphFile"]
ELSE IF file.IsEmpty THEN RETURN ["No file name"]
ELSE { OPEN handle;
ok: BOOLTRUE;
s: STREAM;
nVector: CARDINAL ← 0;
s ← FS. StreamOpen[fileName: file, accessOptions: $create, keep: 99
! FS.Error => {msg ← error.explanation; ok ← FALSE; CONTINUE} ];
IF ok THEN {
ENABLE IO.Error => {
msg ← "Write Error.";
ok ← FALSE;
CONTINUE };
PutRope[s, "binary graph"]; -- format id
PutTexts[s, IF plottedOnly THEN graph.texts ELSE handle.allTexts];
PutCarets[s, graph];
PutByte[s, ShowSlopeKey]; PutBool[s, graph.showSlope];
PutTargets[s, graph];
PutGrids[s, graph];
PutDivisions[s, graph];
PutBounds[s, graph];
PutColors[s, graph];
PutFonts[s, graph];
PutEntityGroupList[s, handle, plottedOnly];
PutByte[s, EndOfRecordKey];
};
IF s # NIL THEN {
s.Flush[];
s.Close[];
newName ← NARROW[Atom.GetPropFromList[s.propList, $Name]]
};
};
}; -- WriteGraphFile
PutTexts: PROC [s: STREAM, texts: Texts] = {
PutByte[s, TextsKey];
FOR ts: Texts ← texts, ts.rest UNTIL ts = NIL DO
text: Text ← ts.first;
PutByte[s, TextKey];
PutByte[s, NameKey]; PutRope[s, text.text];
PutByte[s, PlaceKey]; PutReal[s, text.place.x]; PutReal[s, text.place.y];
PutByte[s, FontIndexKey]; PutByte[s, text.fontIndex];
PutByte[s, ColorIndexKey]; PutByte[s, text.colorIndex];
PutByte[s, RotationKey]; PutReal[s, text.rotation];
PutByte[s, JustificationsKey];
PutByte[s, XKey]; PutByte[s,
SELECT text.justifX FROM left => 1, center => 2, ENDCASE => 3];
PutByte[s, YKey]; PutByte[s,
SELECT text.justifY FROM top => 1, center => 2, ENDCASE => 3];
PutByte[s, EndOfRecordKey];
PutByte[s, TextIdKey]; PutInt[s, text.id];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfListKey];
}; -- PutTexts
PutCarets: GraphFileProc = {
PutByte[s, CaretsKey];
FOR index: CaretIndex IN CaretIndex DO
PutByte[s, SELECT index FROM
primary => PrimaryKey, secondary => SecondaryKey, ENDCASE => TextCaretKey];
PutByte[s, PlaceKey];
PutReal[s, graph.caret[index].place.x];
PutReal[s, graph.caret[index].place.y];
PutByte[s, OnKey];
PutBool[s, graph.caret[index].on];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutCarets
PutTargets: GraphFileProc = {
PutByte[s, TargetsKey];
FOR xy: XY IN XY DO OPEN graph.target[xy];
PutByte[s, IF xy = x THEN XKey ELSE YKey];
PutByte[s, ValueKey]; PutReal[s, value];
PutByte[s, WidthKey]; PutReal[s, width];
PutByte[s, ColorIndexKey]; PutByte[s, colorIndex];
PutByte[s, OnKey]; PutBool[s, on];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutTargets
PutGrids: GraphFileProc = {
PutByte[s, GridsKey];
FOR xy: XY IN XY DO
PutByte[s, IF xy = x THEN XKey ELSE YKey];
PutByte[s, OnKey]; PutBool[s, graph.grids[xy]];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutGrids
PutDivisions: GraphFileProc = {
PutByte[s, DivisionsKey];
FOR xy: XY IN XY DO
PutByte[s, IF xy = x THEN XKey ELSE YKey];
PutByte[s, AutoKey]; PutBool[s, graph.auto[divisions]];
PutByte[s, DivisionsKey]; PutByte[s, graph.division[xy]];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutDivisions
PutBounds: GraphFileProc = {
PutByte[s, BoundsKey];
FOR xy: XY IN XY DO OPEN graph.bounds;
PutByte[s, IF xy = x THEN XKey ELSE YKey];
PutByte[s, AutoKey]; PutBool[s, graph.auto[bounds]];
PutByte[s, MaxKey]; PutReal[s, IF xy = x THEN xmax ELSE ymax];
PutByte[s, MinKey]; PutReal[s, IF xy = x THEN xmin ELSE ymin];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutBounds
PutColors: GraphFileProc = {
PutByte[s, ColorsKey];
FOR i: ColorIndex IN ColorIndex DO OPEN graph.color[i];
PutByte[s, i+1];
PutByte[s, RKey]; PutReal[s, R];
PutByte[s, GKey]; PutReal[s, G];
PutByte[s, BKey]; PutReal[s, B];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutColors
PutFonts: GraphFileProc = {
PutByte[s, FontsKey];
FOR i: FontIndex IN FontIndex DO OPEN graph.font[i];
PutByte[s, i+1];
PutByte[s, NameKey]; PutRope[s, family];
PutByte[s, BoldKey]; PutBool[s, bold];
PutByte[s, ItalicKey]; PutBool[s, italic];
PutByte[s, VFontSizeKey]; PutInt[s, vFontSize];
PutByte[s, PFontScaleKey]; PutReal[s, pFontScale];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfRecordKey];
}; -- PutFonts
PutEntityGroupList: PROC [s: STREAM, handle: GraphHandle, plottedOnly: BOOLTRUE] = {
PutByte[s, EntityGroupListKey];
FOR egl: EntityGroupList ← handle.entityGroupList, egl.rest UNTIL egl = NIL DO
eg: EntityGroup ← egl.first;
PutByte[s, EntityGroupKey];
PutByte[s, NameKey]; PutRope[s, eg.name];
PutEntity[s, eg.x, plottedOnly];
IF plottedOnly THEN PutPlottedNE[s, handle.graph.entityList, eg]
ELSE PutAllNE[s, eg.ys];
PutByte[s, GroupIdKey]; PutInt[s, eg.id];
PutByte[s, LengthKey]; PutInt[s, eg.length];
PutCrossSections[s, eg];
PutByte[s, EndOfRecordKey];
ENDLOOP;
PutByte[s, EndOfListKey];
}; -- PutEntityGroupList
PutEntity: PROC [s: STREAM, entity: Entity, plottedOnly: BOOLTRUE] = {
PutByte[s, EntityKey];
PutByte[s, NameKey];
PutRope[s, IF plottedOnly THEN FullName[entity] ELSE entity.name];
PutByte[s, CommentKey];
PutRope[s, IF plottedOnly THEN "" ELSE entity.comment];
PutByte[s, ColorIndexKey]; PutByte[s, entity.colorIndex];
PutByte[s, MarkKey]; PutByte[s, SELECT entity.mark FROM
none => NoneKey, round => RoundKey, square => SquareKey,
diamond => DiamondKey, cross => CrossKey, dollar => DollarKey,
ENDCASE => PercentKey];
PutByte[s, WidthKey]; PutReal[s, entity.width];
IF plottedOnly THEN PutValuesForSDL[s, entity.segments]
ELSE PutValuesForVL[s, entity.oldValues];
PutByte[s, EntityIdKey]; PutInt[s, entity.id];
PutByte[s, EndOfRecordKey];
}; -- PutEntity
PutAllEL: PROC [s: STREAM, entityList: EntityList] = {
PutByte[s, EntityListKey];
FOR el: EntityList ← entityList, el.rest UNTIL el = NIL DO
PutEntity[s, el.first, FALSE];
ENDLOOP;
PutByte[s, EndOfListKey];
}; -- PutAllEL
PutPlottedEL: PROC [s: STREAM, el: EntityList, group: EntityGroup] = {
PutByte[s, EntityListKey];
FOR el: EntityList ← entityList, el.rest UNTIL el = NIL DO
IF el.first.group = group THEN PutEntity[s, el.first, plottedOnly];
ENDLOOP;
PutByte[s, EndOfListKey];
}; -- PutPlottedEL
PutPlottedNE: PROC [s: STREAM, plottedEL: EntityList, group: EntityGroup] = {
PutByte[s, NestedEntitiesKey];
PutByte[s, NameKey]; PutRope[s, ne.name];
PutByte[s, CommentKey]; PutRope[s, ne.comment];
PutPlottedEL[s, plottedEL, group];
PutNEL[s, NIL];
PutByte[s, EndOfRecordKey];
}; -- PutPlottedNE
PutAllNE: PROC [s: STREAM, ne: NestedEntities] = {
PutByte[s, NestedEntitiesKey];
PutByte[s, NameKey]; PutRope[s, ne.name];
PutByte[s, CommentKey]; PutRope[s, ne.comment];
PutAllEL[s, ne.entityList];
PutNEL[s, ne.children];
PutByte[s, EndOfRecordKey];
}; -- PutAllNE
PutNEL: PROC [s: STREAM, nestedEL: NestedEntitiesList] = {
PutByte[s, NELKey];
FOR nel: NestedEntitiesList ← nestedEL, nel.rest UNTIL nel = NIL DO
PutAllNE[s, nel.first];
ENDLOOP;
PutByte[s, EndOfListKey];
}; -- PutNEL
PutValuesForVL: PROC [s: STREAM, valueList: ValueList] = {
PutByte[s, ValuesKey];
PutInt[s, LengthOfVL[valueList]];
FOR vl: ValueList ← valueList, vl.rest UNTIL vl = NIL DO
PutReal[s, vl.first];
ENDLOOP;
}; -- PutValuesForVL
PutValuesForSDL: PROC [s: STREAM, segmentDataList: SegmentDataList] = {
PutByte[s, ValuesKey];
PutInt[s, LengthOfSDL[segmentDataList]];
FOR sdl: SegmentDataList ← segmentDataList, sdl.rest UNTIL sdl = NIL DO
PutReal[s, sdl.first.end];
ENDLOOP;
}; -- PutValuesForSDL
PutCrossSections: PROC [s: STREAM, eg: EntityGroup] = {
VLL: TYPE = LIST OF ValueList;
vllR, vll, tvll: VLLNIL;
GetVLfromSDL: PROC [segmentDataList: SegmentDataList] RETURNS [vl: ValueList ← NIL] = {
FOR sdl: SegmentDataList ← segmentDataList, sdl.rest UNTIL sdl = NIL DO
vl ← CONS[sdl.first.end];
ENDLOOP;
[vl, ] ← ReverseValueList[vl];
}; -- GetVLfromSDL
GetVLsFromNE: PROC [ne: NestedEntities, old: VLL] RETURNS [new: VLLNIL] = {
result is in reverse order !!
new ← old;
FOR el: EntityList ← ne.entityList, el.rest UNTIL el = NIL DO
new ← CONS[
IF el.first.segments = NIL THEN el.first.oldValues
ELSE GetVLfromSDL[el.first.segments],
new]
ENDLOOP;
new ← GetVLsFromNEL[ne.children, new];
}; -- GetVLsFromNE
GetVLsFromNEL: PROC [nestedEntitiesList: NestedEntitiesList, old: VLL] RETURNS [new: VLLNIL] = { -- result is in reverse order !!
new ← old;
FOR nel: NestedEntitiesList ← nestedEntitiesList, nel.rest UNTIL nel = NIL DO
new ← GetVLsFromNE[nel.first, new];
ENDLOOP;
}; -- GetVLsFromNEL
CleanUpVLL: PROC [old: VLL] RETURNS [VLL] = {
WHILE old # NIL DO
next: VLL ← old.rest;
old.first ← NIL;
old.rest ← NIL;
old ← next;
ENDLOOP;
RETURN[NIL];
}; -- CleanUpVLL
vllR ← CONS[GetVLfromSDL[eg.x.segments], NIL]; -- list of vl in reverse order.
vllR ← GetVLsFromNE[eg.ys, vllR];
copy to vll in the correct order.
FOR tvll ← vllR, tvll.rest UNTIL tvll = NIL DO
vll ← CONS[tvll.first, vll];
ENDLOOP;
clean up the reversed one.
vllR ← CleanUpVLL[vllR];
start to work.
PutByte[s, CrossSectionsKey];
WHILE vll # NIL DO
IF vll.first = NIL THEN EXIT;
PutByte[s, TailsKey]; -- keyword before each set of tails
FOR tvll ← vll, tvll.rest UNTIL tvll = NIL DO
vl: ValueList ← tvll.first;
PutReal[s, vl.first];
tvll.first ← vl.rest;
ENDLOOP;
ENDLOOP;
PutByte[s, EndOfListKey];
clean up.
vll ← CleanUpVLL[vll];
}; -- PutCrossSections
WriteDataFile: PUBLIC PROC [graph: GRAPHNIL, file: ROPENIL] RETURNS [msg: ROPENIL] = {
write text formatted graph file
}; -- WriteDataFile
CheckFormat: PROC [stream: STREAM] RETURNS[format: FileFormat ← illegal] = {
rope: ROPE;
firstChar: CHAR ← stream.PeekChar[];
IF firstChar = 0C THEN {
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 ← text;
}; -- CheckFormat
reading routines
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
writing routines
PutRope: PROC [stream: STREAM, rope: ROPENIL] = {
length: CARDINAL ← Basics.LowHalf[rope.Length[]];
PutCardinal[stream, length];
stream.PutBlock[Rope.ToRefText[rope], 0, length];
}; -- PutRope
PutTime: PROC [stream: STREAM, time: BasicTime.GMT ← BasicTime.nullGMT] = {
PutLongWord[stream, LOOPHOLE[time, FourChars]];
}; -- PutTime
PutBool: PROC[stream: STREAM, bool: BOOLTRUE] = {
PutByte[stream, IF bool THEN TrueKey ELSE FalseKey];
}; -- PutBool
PutByte: PROC [stream: STREAM, byte: BYTE ← 0] = {
stream.PutChar[LOOPHOLE[byte, CHAR]];
}; -- PutByte
PutInt: PROC [stream: STREAM, int: INT ← 0] = {
PutLongWord[stream, LOOPHOLE[int, FourChars]];
}; -- PutInt
PutReal: PROC [stream: STREAM, real: REAL ← 0] = {
PutLongWord[stream, LOOPHOLE[real, FourChars]];
}; -- PutReal
PutCardinal: PROC [stream: STREAM, word: CARDINAL ← 0] = {
pair: CharPair ← LOOPHOLE[word, CharPair];
stream.PutChar[pair.high];
stream.PutChar[pair.low];
}; -- PutCardinal
PutLongWord: PROC [stream: STREAM, long: FourChars] = INLINE {
stream.PutChar[long.lh];
stream.PutChar[long.ll];
stream.PutChar[long.hh];
stream.PutChar[long.hl];
}; -- PutLongCard
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.