LichenFromExtImpl2.Mesa
Last tweaked by Mike Spreitzer on August 18, 1988 12:02:52 pm PDT
DIRECTORY AbSets, BasicTime, BiRels, IntStuff, IO, LichenDataOps, LichenDataStructure, LichenFromExtPrivate, LichenIntBasics, RefText, Rope, SetBasics;
LichenFromExtImpl2: CEDAR MONITOR
LOCKS dr USING dr: DesignReading
IMPORTS AbSets, BasicTime, BiRels, IO, LichenDataOps, LichenDataStructure, LichenFromExtPrivate, LichenIntBasics, RefText, Rope, SetBasics
EXPORTS LichenFromExtPrivate
=
BEGIN OPEN LIB:LichenIntBasics, LIB, LichenDataStructure, LichenDataOps, LichenFromExtPrivate, Sets:AbSets;
GetOSn: PROC [dr: DesignReading, ct: CellType, s: Source] RETURNS [name: SteppyName] ~ {
raw: ROPE ~ GetName[s];
name ← ParseSteppyName[raw];
IF name.steps.rest#NIL THEN ERROR;
name ← MapFirst[dr, ct, name];
RETURN};
GetName: PROC [s: Source] RETURNS [name: ROPE] = {
from: IO.STREAM = s.stream;
[] ← from.SkipWhitespace[];
SELECT from.PeekChar[] FROM
'" => name ← from.GetRopeLiteral[];
ENDCASE => name ← from.GetTokenRope[TokenBreak].token;
};
EndLine: PROC [from: IO.STREAM, buffer: REFTEXT] = {
IF NOT from.EndOf[] THEN [] ← from.GetLine[buffer];
};
ReadTech: PROC [s: Source, reader: Reader, cr: CellReading] = {
ct: CellType = cr.ct;
from: IO.STREAM = s.stream;
techname: ROPE ← GetName[s];
EndLine[from, cr.dr.buffer];
RETURN};
ReadTimestamp: PROC [s: Source, reader: Reader, cr: CellReading] = {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
unixTime: INT ~ from.GetInt[];
time: BasicTime.GMT ~ BasicTime.Update[unixOrigin, unixTime];
EndLine[from, cr.dr.buffer];
RETURN};
unixOrigin: BasicTime.GMT ← BasicTime.Pack[[
year: 1970,
month: January,
day: 1,
hour: 0,
minute: 0,
second: 0,
zone: 0--GMT, I hope--,
dst: no]];
ReadVersion: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
version: ROPE ~ GetName[s];
deriver: ROPE ~ "UCB's Magic .extractor";
EndLine[from, cr.dr.buffer];
RETURN};
ReadScale: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
rscale: INT ~ from.GetInt[];
cscale: INT ~ from.GetInt[];
lscale: INT ~ from.GetInt[];
IF cr.scalingDefined THEN Warn[s, "More than one scale statment"];
cr.scalingDefined ← TRUE;
cr.rScale ← rscale * cr.rScale;
cr.cScale ← cscale * cr.cScale;
cr.lUnits ← lscale * cr.lUnits;
EndLine[from, cr.dr.buffer];
RETURN};
ReadResistClasses: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
n: INT ← 0;
IF cr.resistClasses # undefinedINT THEN Warn[s, "More than one resistclasses statment"];
DO
token: ROPE = from.GetTokenRope[TokenBreak].token;
IF token.Equal["\n"] THEN EXIT;
n ← n + 1;
ENDLOOP;
cr.resistClasses ← n;
RETURN};
keepCruft: BOOLFALSE;
ReadNode: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
fullName: SteppyName ~ GetOSn[cr.dr, ct, s];
R: INT ~ from.GetInt[];
C: INT ~ from.GetInt[];
x: INT ~ from.GetInt[];
y: INT ~ from.GetInt[];
ok: BOOL ~ SkipNTokens[from, cr.resistClasses*2, cr.dr.buffer];
attrs: LORA ~ ReadAttrs[s];
nv: Wire ← FetchWire[ct, fullName];
<<IF fullName.steps.rest#NIL THEN ERROR;>>
IF nv = NIL THEN nv ← CreateWire[ct, OneSteppy[fullName], FALSE, TRUE, TRUE];
CTIncludesPoint[ct, [x, y]];
EndLine[from, cr.dr.buffer];
RETURN};
ReadAttrs: PROC [s: Source, zeroNIL: BOOLFALSE] RETURNS [allTogetherNow: LORANIL] = {
from: IO.STREAM = s.stream;
toke: ROPENIL;
IF zeroNIL THEN {
[] ← from.SkipWhitespace[];
IF from.PeekChar[] = '0 THEN {
IF from.GetChar[] # '0 THEN ERROR;
RETURN};
};
{DO
attr: ROPENIL;
toke ← from.GetTokenRope[AttrBreak !IO.EndOfStream => EXIT].token;
IF toke.Equal[","] THEN {Warn[s, "Extra comma"]; LOOP};
IF NOT toke.Equal["\""] THEN GOTO Return;
from.Backup['"];
attr ← from.GetRopeLiteral[ !IO.Error, IO.EndOfStream => {Warn[s, "not a rope literal"]; CONTINUE}];
IF attr # NIL THEN allTogetherNow ← CONS[attr, allTogetherNow];
toke ← from.GetTokenRope[AttrBreak !IO.EndOfStream => EXIT].token;
IF NOT toke.Equal[","] THEN GOTO Return;
ENDLOOP;
EXITS
Return => {
FOR i: INT DECREASING IN [0 .. toke.Length[]) DO
s.stream.Backup[toke.Fetch[i]];
ENDLOOP;
};
}};
AttrBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] =
{cc ← SELECT char FROM ',, '\n, '" => break, ENDCASE => sepr};
ReadEquiv: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
name1: SteppyName ~ GetOSn[cr.dr, ct, s];
name2: SteppyName ~ GetOSn[cr.dr, ct, s];
v1: Wire ~ FetchWire[ct, name1];
v2: Wire ~ FetchWire[ct, name2];
IF name1.steps.rest#NIL OR name2.steps.rest#NIL THEN ERROR;
SELECT TRUE FROM
v1=v2 => IF v1=NIL THEN ERROR;
v1=NIL => KnowVertexName[ct, v2, name1, FALSE];
v2=NIL => KnowVertexName[ct, v1, name2, FALSE];
ENDCASE => ERROR;
EndLine[from, cr.dr.buffer];
RETURN};
ReadFet: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
type: ROPE ~ GetName[s];
xl: INT ~ from.GetInt[];
yl: INT ~ from.GetInt[];
xh: INT ~ from.GetInt[];
yh: INT ~ from.GetInt[];
area: INT ~ from.GetInt[];
perim: INT ~ from.GetInt[];
sub: ROPE ~ GetName[s];
GATE: FetTerminal ~ GetFetTerminal[s];
T1: FetTerminal ~ GetFetTerminal[s];
T2: FetTerminal ~ GetFetTerminal[s];
sct: CellType;
tv: CellInstance;
offset: Int2;
DoTerm: PROC [portName: ROPE, ft: FetTerminal] ~ {
path: Path ~ ParsePath[s, cr.dr, ct, ft.name, TRUE];
w: Wire ~ PathGet[cr.dr, ct, path, TRUE];
p: Port ~ FetchPort[sct, OSn[portName]];
Connect[cr.dr.d, w, p, tv];
RETURN};
[sct, offset] ← GetFetType[cr.dr, type, [[xl, xh], [yl, yh]], area, perim, T1.length+T2.length];
tv ← Instantiate[sct, ct, FALSE, [], offset, OneOSn[IO.PutFR["Q%g#", IO.int[cr.fetCount ← cr.fetCount + 1]]]];
DoTerm["gate", GATE];
DoTerm["ch1", T1];
DoTerm["ch2", T2];
EndLine[from, cr.dr.buffer];
RETURN};
GetFetTerminal: PROC [s: Source] RETURNS [ft: FetTerminal] ~ {
from: IO.STREAM ~ s.stream;
ft.name ← GetName[s];
ft.length ← from.GetInt[];
{attrs: LORA ← ReadAttrs[s, TRUE];
RETURN}};
GetFetType: PROC [dr: DesignReading, className: ROPE, innerGate: Range2, area, perim, sumChannelLengths: INT] RETURNS [ct: CellType, offset: Int2] ~ {
d: Design ~ dr.d;
shape: Int2 ~ RangeShape[innerGate];
ft: FetType ~ NEW [FetTypeRep ← [className, area, perim, sumChannelLengths]];
rft: FetType;
rft ← NARROW[dr.fetTypes.ApplyA[ft].MDA];
offset ← Range2Min[innerGate];
IF rft = NIL THEN {
cellTypeName: ROPE ~ IO.PutFLR["%g[%g,%g,%g,%g,%g]", LIST[IO.rope[ft.className], IO.int[ft.area], IO.int[ft.perim], IO.int[ft.twiceLength], IO.int[shape[X]], IO.int[shape[Y]] ]];
rft ← ft;
ft.ct ← CreateCellType[d, leaf, SizeRange[shape], OneRope[cellTypeName]];
ft.ct.asTrans ← NEW [TransistorPrivate ← [
type: className,
area: area,
perimeter: perim]];
[] ← CreatePort[ft.ct, OneOSn[R["gate"]], FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE];
[] ← CreatePort[ft.ct, OneOSn[R["ch1"]], FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE];
[] ← CreatePort[ft.ct, OneOSn[R["ch2"]], FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE];
dr.fetTypes.AddNewAA[ft, rft];
};
ct ← rft.ct;
RETURN};
ReadUse: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
typeName: ROPE ~ GetName[s];
useId: ROPE ~ GetName[s];
dxdx: INT ~ from.GetInt[];
dxdy: INT ~ from.GetInt[];
dx: INT ~ from.GetInt[];
dydx: INT ~ from.GetInt[];
dydy: INT ~ from.GetInt[];
dy: INT ~ from.GetInt[];
xfm: Transform ~ XfmFrom4[dxdx, dxdy, dydx, dydy];
pu: Use ~ ParseUseDef[useId];
u: Use ~ TransformUse[xfm, pu];
type: CellType ~ EnsureType[cr, typeName, u.as, xfm, ct, u.childName];
ci: CellInstance ~ Instantiate[type, ct, TRUE, IF u.as.kind=array THEN [] ELSE xfm, [dx, dy], OneOSn[u.childName]];
EndLine[from, cr.dr.buffer]};
TransformUse: PROC [xfm: Transform, u: Use] RETURNS [Use] ~ {
WITH u.as SELECT FROM
scalar => RETURN [u];
array => {
aas: ArraySpec.array ~ [variant: array[
range: xfm.TranspRange2[range],
sep: xfm.TransformVector[sep]
]];
IF aas.sep[X]<0 OR aas.sep[Y]<0 THEN ERROR;
RETURN [[childName: u.childName, as: aas]]};
ENDCASE => ERROR};
ParseUseDef: PROC [useId: ROPE] RETURNS [u: Use] ~ {
in: IO.STREAM ~ IO.RIS[useId];
u.childName ← in.GetTokenRope[UseNameBreak].token;
IF in.EndOf[] THEN {
in.Close[];
RETURN [[u.childName, [scalar[]]]]
}
ELSE {
as: ArraySpec.array ← [array[range: ALL[[0, 0]], sep: [0, 0]]];
Get: PROC [d: Dim2] ~ {
IF in.GetChar[] # '[ THEN ERROR;
as.range[d].min ← in.GetInt[];
IF in.GetChar[] # ': THEN ERROR;
as.range[d].maxPlusOne ← in.GetInt[]+1;
IF in.GetChar[] # ': THEN ERROR;
as.sep[d] ← in.GetInt[];
IF in.GetChar[] # '] THEN ERROR;
};
Get[X];
Get[Y];
IF NOT in.EndOf[] THEN ERROR;
in.Close[];
RETURN [[u.childName, as]];
};
};
UseNameBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = {
cc ← SELECT char FROM
'[, '], ': => break,
ENDCASE => other;
};
EnsureType: PROC [cr: CellReading, typeName: ROPE, as: ArraySpec, xfm: Transform, parent: CellType, childName: ROPE] RETURNS [ct: CellType] ~ {
dr: DesignReading ~ cr.dr;
d: Design ~ dr.d;
WITH as SELECT FROM
x: ArraySpec.scalar => {
ct ← NARROW[d.ctName.ApplyA[typeName, rightToLeft].MDA];
IF ct=NIL THEN ct ← ReadCellType[d, typeName, dr];
};
x: ArraySpec.array => {
ec: ROPE ~ typeName.Cat[FmtAS[as]];
cellTypeName: ROPE ~ IO.PutFR["%g(%g.%g)", IO.rope[ec], IO.rope[NARROW[d.ctName.ApplyA[parent].MA]], IO.rope[childName]];
eltType: CellType ~ EnsureType[cr, typeName, [scalar[]], [], NIL, NIL];
size: Int2 ~ RangeShape[x.range];
ct ← CreateArray[d, eltType, size, [1, 1], BiRels.CreateSingleton[[I2V[[0, 0]], XfmV[xfm]], [int2s, xfmSpace]], OffsetsFromList[LIST[[o0: ALL[0], o1: x.sep]]], OneRope[cellTypeName]];
cr.dr.arraySpecs.AddNewAA[ct, NEW [ArraySpec.array ← x]];
IF NOT cr.newArrays.AddA[ct] THEN ERROR;
};
ENDCASE => ERROR;
};
NameElt: PROC [i: INT] RETURNS [eltName: NameStep] ~
{eltName ← NewInt[i]};
FmtAS: PROC [as: ArraySpec] RETURNS [r: ROPE] = {
r ← WITH as SELECT FROM
scalar => "scalar",
array => IO.PutFLR["[%g:%g:%g][%g:%g:%g]", LIST[
IO.int[range[X].min],
IO.int[range[X].maxPlusOne-1],
IO.int[sep[X]],
IO.int[range[Y].min],
IO.int[range[Y].maxPlusOne-1],
IO.int[sep[Y]]]],
ENDCASE => ERROR;
};
FmtShape: PROC [shape: Int2] RETURNS [r: ROPE] = {
r ← IO.PutFR["[X: %g, Y: %g]", IO.int[shape[X]], IO.int[shape[Y]]];
RETURN};
ReadMerge: PROC [s: Source, reader: Reader, cr: CellReading] ~ {
ct: CellType ~ cr.ct;
from: IO.STREAM ~ s.stream;
IF cr.firstMerge THEN {cr.firstMerge ← FALSE; TryArrayFile[cr]};
{name1: ROPE ~ GetName[s];
name2: ROPE ~ GetName[s];
path1: Path ~ ParsePath[s, cr.dr, ct, name1, TRUE];
path2: Path ~ ParsePath[s, cr.dr, ct, name2, TRUE];
IF ComparePaths[path1, cr.dr.mostRecentPathToMerge]#equal THEN {
DoMerges[s, cr];
AddMerge[cr.dr, path1];
};
AddMerge[cr.dr, path2];
cr.dr.mostRecentPathToMerge ← path2;
EndLine[from, cr.dr.buffer];
RETURN}};
AddMerge: PROC [dr: DesignReading, path: Path] ~ {
arrayPrefix: Path ~ [IF path.cells#NIL AND path.cells.rest#NIL
THEN WITH path.cells.first SELECT FROM
x: ROPE => WITH path.cells.rest.first SELECT FROM
r: REF Range2 => LIST[x, r],
ENDCASE => NIL,
ENDCASE => NIL
ELSE NIL];
apv: Sets.Value ~ arrayPrefix.PathV;
rs: REF Set--of Path--NARROW[dr.toMerge.Apply[apv].MDA];
IF rs=NIL THEN dr.toMerge.AddNewPair[[apv, AV[rs ← Sets.CreateHashSet[paths].Refify]]];
IF NOT rs^.AddElt[PathV[path]] THEN ERROR;
RETURN};
ArrayMerge: PUBLIC PROC [cr: CellReading, arrayInstance: CellInstance, instanceName: ROPE, path1, path2: Path, may: BOOL] RETURNS [merged: BOOL] = {
dr: DesignReading ~ cr.dr;
d: Design ~ dr.d;
act: CellType = d.CiT[arrayInstance];
a: Array = act.asArray;
et: CellType = act.EltType[];
rr1: REF Range2 = NARROW[path1.cells.first];
rr2: REF Range2 = NARROW[path2.cells.first];
r1: Range2 = rr1^;
r2: Range2 = rr2^;
size: Int2 = RangeShape[r2];
mai1: Int2 = Range2Min[r1];
mai2: Int2 = Range2Min[r2];
D: Int2 = LIB.Sub[mai2, mai1];
p1: Port = PortGet[dr, et, path1.PTail, may];
p2: Port = PortGet[dr, et, path2.PTail, may];
IF RangeShape[r1] # size THEN ERROR;
IF p1=NIL OR p2=NIL THEN {IF may THEN ERROR ELSE RETURN [FALSE]};
IF may THEN {
IF ABS[D[X]]>1 OR ABS[D[Y]]>1 THEN ERROR;
MakeArrayNewConnection[d, a, r1, D, p1, p2];
RETURN [TRUE]}
ELSE {
IF size # [1, 1] THEN ERROR;
merged ← ArrayEltPortsConnected[act, mai1, mai2, p1, p2];
RETURN};
};
MergeFinal: PUBLIC PROC [dr: DesignReading, ct: CellType, path1, path2: Path] = {
w1, w2, nw: Wire;
w1 ← PathGet[dr, ct, path1, TRUE];
w2 ← PathGet[dr, ct, path2, TRUE];
IF w1#w2 THEN nw ← MergeNets[dr.d, w1, w2, FALSE].merged;
RETURN};
ParsePath: PUBLIC PROC [s: Source, dr: DesignReading, from: CellType, asRope: ROPE, map: BOOL] RETURNS [Path] = {
in: IO.STREAM = IO.RIS[asRope];
cells: TList ← [];
GetRange: PROC RETURNS [x: Range] = {
x.maxPlusOne ← (x.min ← in.GetInt[]) + 1;
SELECT in.PeekChar[] FROM
': => {
IF in.GetChar[] # ': THEN ERROR;
x.maxPlusOne ← in.GetInt[] + 1;
};
',, '] => NULL;
ENDCASE => ERROR;
};
DO
toke: ROPE ~ in.GetTokenRope[PathNameBreak].token;
ras: REF ArraySpec.array = NARROW[dr.arraySpecs.ApplyA[from].MDA];
next: CellType ← NIL;
SELECT TRUE FROM
toke.Equal["/"] => LOOP;
toke.Equal["["] => {
xd: BOOL = ras.range[X].min # ras.range[X].maxPlusOne-1;
yd: BOOL = ras.range[Y].min # ras.range[Y].maxPlusOne-1;
a: Array ~ from.asArray;
r2: Range2 ← ras.range;
twoD: BOOLFALSE;
IF NOT (xd OR yd) THEN ERROR;
IF yd THEN r2[Y] ← GetRange[] ELSE r2[X] ← GetRange[];
{sep: ROPE ~ in.GetTokenRope[PathNameBreak].token;
SELECT TRUE FROM
sep.Equal["]"] => NULL;
sep.Equal[","] => {
twoD ← TRUE;
r2[X] ← GetRange[];
{eb: ROPE ~ in.GetTokenRope[PathNameBreak].token;
IF NOT eb.Equal["]"] THEN ERROR}};
ENDCASE => ERROR;
IF twoD # (xd AND yd) THEN ERROR;
IF a.basePeriod # ALL[1] THEN ERROR;
IF twoD THEN r2 ← VXfm[a.fXfm.Apply[I2V[ALL[0]]].Val].TranspRange2[r2];
cells ← cells.Append[NEW [Range2 ← r2]];
next ← from.EltType[]}};
toke.Equal["]"] => ERROR;
toke.Equal[":"] => ERROR;
toke.Equal[","] => ERROR;
in.EndOf[] => {
wireName: SteppyName ~ IF map THEN MapName[dr, from, toke] ELSE OSn[toke];
IF from.FetchWire[wireName]=NIL THEN ERROR;
in.Close[];
RETURN [[cells.head, wireName]]};
ENDCASE => {
cellName: ROPE ~ toke;
cells ← cells.Append[cellName];
next ← dr.d.CiT[from.FetchSubcell[OSn[cellName]]]};
IF next=NIL THEN ERROR;
from ← next; ENDLOOP;
};
PathNameBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = {
cc ← SELECT char FROM
'[, '], ':, '/, ', => break,
ENDCASE => other;
};
PathGet: PUBLIC PROC [dr: DesignReading, from: CellType, path: Path, mayAdd: BOOL] RETURNS [w: Wire] = {
d: Design ~ dr.d;
IF path.cells#NIL THEN {
name: ROPE ~ NARROW[path.cells.first];
ci: CellInstance ~ from.FetchSubcell[OSn[name]];
childPort: Port ~ PortGet[dr, d.CiT[ci], path.PTail, mayAdd];
IF childPort # NIL THEN {
w ← ConndWire[ci, childPort];
IF w=NIL THEN ERROR;
RETURN};
RETURN}
ELSE {
w ← from.FetchWire[path.wireName];
RETURN};
};
PortGet: PROC [dr: DesignReading, from: CellType, path: Path, mayAdd: BOOL] RETURNS [port: Port] = {
d: Design ~ dr.d;
IF path.cells=NIL THEN {
w: Wire ~ from.FetchWire[path.wireName];
port ← PortForWire[from, w, mayAdd]}
ELSE WITH path.cells.first SELECT FROM
x: REF Range2 => {
ras: REF ArraySpec.array = NARROW[dr.arraySpecs.ApplyA[from].MDA];
a: Array = from.asArray;
eltPort: Port = PortGet[dr, from.EltType[], path.PTail, mayAdd];
IF eltPort=NIL THEN port ← NIL ELSE {
index: Int2 = LIB.Sub[Range2Min[x^], Range2Min[ras.range]];
IF NOT Range2IsSingleton[x^] THEN ERROR;
port ← GetArrayPortForPort[from, index, eltPort, mayAdd, TRUE, TRUE];
};
};
x: ROPE => {
ci: CellInstance ~ from.FetchSubcell[OSn[x]];
childPort: Port ~ PortGet[dr, d.CiT[ci], path.PTail, mayAdd];
IF childPort = NIL THEN port ← NIL ELSE {
w: Wire ~ ConndWire[ci, childPort];
IF w=NIL THEN ERROR;
port ← PortForWire[from, w, mayAdd];
};
};
ENDCASE => ERROR;
port ← port};
ReadCap: PROC [s: Source, reader: Reader, cr: CellReading] = {
ct: CellType = cr.ct;
from: IO.STREAM = s.stream;
EndLine[from, cr.dr.buffer];
};
SkipNTokens: PROC [from: IO.STREAM, n: INT, buffer: REFTEXT] RETURNS [ok: BOOL] = {
WHILE n > 0 DO
token: REFTEXT = from.GetToken[TokenBreak, buffer].token;
IF RefText.Equal[token, "\n"] THEN RETURN [FALSE];
n ← n - 1;
ENDLOOP;
ok ← TRUE;
};
Warn: PROC [s: Source, msg: ROPE, v1, v2, v3, v4, v5: IO.Value ← [null[]]] = {
IF s.name # NIL THEN msg ← IO.PutFR["At %g[%g]: %g", [rope[s.name]], [integer[s.stream.GetIndex[]]], [rope[msg]]];
SIGNAL Warning[IO.PutFR[msg, v1, v2, v3, v4, v5]];
RETURN};
Start: PROC = {
Register["tech", ReadTech];
Register["timestamp", ReadTimestamp];
Register["version", ReadVersion];
Register["scale", ReadScale];
Register["resistclasses", ReadResistClasses];
Register["node", ReadNode];
Register["equiv", ReadEquiv];
Register["fet", ReadFet];
Register["use", ReadUse];
Register["merge", ReadMerge];
Register["adjust", ReadAdjust];
Register["cap", ReadCap];
RETURN};
Start[];
END.