EDIFParsing:
CEDAR
PROGRAM
IMPORTS Atom, BasicTime, CoreOps2, EDIFAndCorePrivate, HashTable, IO, Rope
EXPORTS EDIFAndCore, EDIFAndCorePrivate
= {OPEN CO2: CoreOps2, EDIFAndCore, EDIFAndCorePrivate;
Error: PUBLIC ERROR [msg: ROPE] = CODE;
Warning: PUBLIC SIGNAL [msg: ROPE] = CODE;
EDIFFromStream:
PUBLIC
PROC [from:
IO.
STREAM]
RETURNS [ew: EDIFWhole] = {
me: ROPE = "EDIF file";
lexed: LORA = LexEDIF[from];
stdTail: StdTail ← [];
ec: EDIFConversion =
NEW [EDIFConversionPrivate ← [
rawDesigns: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase],
rawLibraries: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase],
ew: NIL]];
AddRaw:
PROC [table: HashTable.Table, part:
LORA, kind:
ATOM] = {
me: ROPE = IO.PutFR["%g in EDIF file", [atom[kind]]];
atom: ATOM;
CheckLength[part, me, 2];
CheckHead[part, kind, me];
atom ← ToATOM[part.rest.first, me.Concat[" has bad name"]];
[] ← table.Insert[Atom.GetPName[atom], part];
};
DefineLibrary:
PROC [key, value:
REF
ANY]
RETURNS [quit:
BOOLEAN ←
FALSE]
--HashTable.EachPairAction-- = {
name: ROPE = NARROW[key];
lexed: LORA = NARROW[value];
[] ← GetLibrary[ec, name, "ERROR"];
};
DefineDesign:
PROC [key, value:
REF
ANY]
RETURNS [quit:
BOOLEAN ←
FALSE]
--HashTable.EachPairAction-- = {
name: ROPE = NARROW[key];
lexed: LORA = NARROW[value];
[] ← GetDesign[ec, name, "ERROR"];
};
CheckLength[lexed, me, 3];
CheckHead[lexed, $edif, me];
ec.ew ← ew ←
NEW [EDIFWholePrivate ← [
name: [Atom.GetPName[ToATOM[lexed.rest.first, "Bad EDIFFileName"]]],
status: ParseStatus[lexed.rest.rest.first, me],
designs: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase],
libraries: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase],
externalLibraries: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase]
]];
IF ew.status.version # unspecifiedVersion AND ew.status.version # [1, 0, 0] THEN Warning[IO.PutFR["Seeing version %g.%g.%g stuff when version 1.0.0 expected", [integer[ew.status.version.major]], [integer[ew.status.version.medium]], [integer[ew.status.version.minor]] ]];
FOR rest:
LORA ← lexed.rest.rest.rest, rest.rest
WHILE rest #
NIL
DO
innerMe: ROPE = Rope.Cat["statement in ", me];
part: LORA = ToLORA[lexed.first, me.Cat[" contains a non-statement"]];
key: ATOM;
CheckLength[part, innerMe, 1];
key ← ToATOM[part.first, innerMe.Cat[" doesn't begin with a keyword"]];
SELECT LcA[key]
FROM
$design => AddRaw[ec.rawDesigns, part, $design];
$library => AddRaw[ec.rawLibraries, part, $library];
$external => {
FOR pr:
LORA ← part.rest, pr.rest
WHILE pr #
NIL
DO
libName: ATOM = ToATOM[pr.first, "bad external library"];
[] ← ew.externalLibraries.Insert[Atom.GetPName[libName], $T];
ENDLOOP;
};
$comment => {
c: Comment = ParseComment[part, me];
[ew.comments, stdTail.c] ← CAppend[ew.comments, stdTail.c, c];
};
$userdata => {
ue: UserExtension = ParseUserExtension[part, me];
[ew.ues, stdTail.ues] ← UEAppend[ew.ues, stdTail.ues, ue];
};
ENDCASE => Error[IO.PutFR["%g statement found in %g", [atom[key]], [rope[me]] ]];
ENDLOOP;
[] ← ec.rawLibraries.Pairs[DefineLibrary];
[] ← ec.rawDesigns.Pairs[DefineDesign];
};
GetLibrary:
PROC [ec: EDIFConversion, name, bitch:
ROPE]
RETURNS [l: Library] = {
raw: LORA;
l ← NARROW[ec.ew.libraries.Fetch[name].value];
IF l # NIL THEN RETURN;
raw ← NARROW[ec.rawLibraries.Fetch[name].value];
IF raw = NIL THEN Error[bitch];
l ← ParseLibrary[ec, name, raw];
IF NOT ec.ew.libraries.Insert[name, l] THEN ERROR;
};
GetDesign:
PROC [ec: EDIFConversion, name, bitch:
ROPE]
RETURNS [d: Design] = {
raw: LORA;
d ← NARROW[ec.ew.designs.Fetch[name].value];
IF d # NIL THEN RETURN;
raw ← NARROW[ec.rawDesigns.Fetch[name].value];
IF raw = NIL THEN Error[bitch];
d ← ParseDesign[ec, name, raw];
IF NOT ec.ew.designs.Insert[name, d] THEN ERROR;
};
ParseLibrary:
PROC [ec: EDIFConversion, name:
ROPE, lexed:
LORA]
RETURNS [l: Library] = {
me: ROPE = Rope.Cat["Library ", name];
stdTail: StdTail ← [];
CheckLength[lexed, me, 2];
CheckHead[lexed, $library, me];
l ←
NEW[LibraryPrivate ← [
name: [name],
cells: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase]
]];
FOR lexed ← lexed.rest.rest, lexed.rest
WHILE lexed #
NIL
DO
innerMe: ROPE = Rope.Cat["statement in ", me];
part: LORA = ToLORA[lexed.first, me.Cat[" contains a non-statement"]];
key: ATOM;
CheckLength[part, innerMe, 1];
key ← ToATOM[part.first, innerMe.Cat[" doesn't begin with a keyword"]];
SELECT LcA[key]
FROM
$status => {
IF l.status # NIL THEN Error[Rope.Cat["Multiple status specifications in ", me]];
l.status ← ParseStatus[part, me];
};
$technology => {
IF l.technology # NIL THEN Error[Rope.Cat["Multiple technologies in ", me]];
l.technology ← ParseTechnology[ec, part, me];
};
$cell => {
ct: CoreCellType = ParseCell[ec, part, me];
cellTypeName: ROPE = CO2.GetCellTypeName[ct];
IF
NOT l.cells.Insert[cellTypeName, ct]
THEN Error[IO.PutFR["Multiple definitions of cell %g in library %g", [rope[cellTypeName]], [rope[name]] ]]
};
$comment => {
c: Comment = ParseComment[part, me];
[l.comments, stdTail.c] ← CAppend[l.comments, stdTail.c, c];
};
$userdata => {
ue: UserExtension = ParseUserExtension[part, me];
[l.ues, stdTail.ues] ← UEAppend[l.ues, stdTail.ues, ue];
};
ENDCASE => Error[IO.PutFR["%g statement found in %g", [atom[key]], [rope[me]] ]];
ENDLOOP;
ec ← ec;
};
ParseDesign:
PROC [ec: EDIFConversion, name:
ROPE, lexed:
LORA]
RETURNS [d: Design] = {
me: ROPE = Rope.Cat["design ", name];
stdTail: StdTail ← [];
q: Qual;
lib: Library;
root: CellType;
CheckLength[lexed, me, 3];
CheckHead[lexed, $design, me];
q ← ToQual[lexed.rest.rest.first, me.Concat[" has bad root cell"]];
lib ← GetLibrary[ec, q.outer, me.Cat[" wants non-existant library ", q.outer]];
root ← NARROW[lib.cells.Fetch[q.inner].value];
IF root = NIL THEN Error[IO.PutFR["%g wants non-existant cell %g from library %g", [rope[me]], [rope[q.inner]], [rope[q.outer]] ]];
d ←
NEW[DesignPrivate ← [
name: [name],
rootCellType: root]];
FOR lexed ← lexed.rest.rest.rest, lexed.rest
WHILE lexed #
NIL
DO
innerMe: ROPE = Rope.Cat["statement in ", me];
part: LORA = ToLORA[lexed.first, me.Cat[" contains a non-statement"]];
key: ATOM;
CheckLength[part, innerMe, 1];
key ← ToATOM[part.first, innerMe.Cat[" doesn't begin with a keyword"]];
SELECT LcA[key]
FROM
$comment => {
c: Comment = ParseComment[part, me];
[d.comments, stdTail.c] ← CAppend[d.comments, stdTail.c, c];
};
$userdata => {
ue: UserExtension = ParseUserExtension[part, me];
[d.ues, stdTail.ues] ← UEAppend[d.ues, stdTail.ues, ue];
};
ENDCASE => Error[IO.PutFR["%g statement found in %g", [atom[key]], [rope[me]] ]];
ENDLOOP;
ec ← ec;
};
ParseStatus:
PUBLIC
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [status: Status] = {
me: ROPE = Rope.Cat["Status block for ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
writtenTail: WrittenList ← NIL;
stdTail: StdTail ← [];
CheckLength[lexed, me, 1];
CheckHead[lexed, $status, me];
status ← NEW[StatusPrivate ← []];
FOR lexed ← lexed.rest, lexed.rest
WHILE lexed #
NIL
DO
innerMe: ROPE = Rope.Cat["statement in ", me];
part: LORA = ToLORA[lexed.first, me.Cat[" contains a non-statement"]];
key: ATOM;
CheckLength[part, innerMe, 1];
key ← ToATOM[part.first, innerMe.Cat[" doesn't begin with a keyword"]];
SELECT LcA[key]
FROM
$edifversion => {
IF status.version # unspecifiedVersion THEN Error[Rope.Cat["Multiple version specifications in ", me]];
status.version ← ParseVersion[part, me];
};
$ediflevel => {
IF status.level # unspecifiedLevel THEN Error[Rope.Cat["Multiple level specifications in ", me]];
status.level ← ParseLevel[part, me];
};
$written => {
w: Written = ParseWritten[part, me];
[status.writtens, writtenTail] ← WAppend[status.writtens, writtenTail, w];
};
$comment => {
c: Comment = ParseComment[part, me];
[status.comments, stdTail.c] ← CAppend[status.comments, stdTail.c, c];
};
$userdata => {
ue: UserExtension = ParseUserExtension[part, me];
[status.ues, stdTail.ues] ← UEAppend[status.ues, stdTail.ues, ue];
};
ENDCASE => Error[IO.PutFR["%g statement found in %g", [atom[key]], [rope[me]] ]];
ENDLOOP;
lexedRA ← lexedRA;
};
ParseVersion:
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [v: Version] = {
me: ROPE = Rope.Cat["version in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
CheckLength[lexed, me, 4, 4];
CheckHead[lexed, $edifversion, me];
v.major ← ToINT[LFetch[lexed, 1], me.Concat[" contains bad major version"]];
v.medium ← ToINT[LFetch[lexed, 2], me.Concat[" contains bad medium version"]];
v.minor ← ToINT[LFetch[lexed, 3], me.Concat[" contains bad minor version"]];
lexedRA ← lexedRA;
};
ParseLevel:
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [l: Level] = {
me: ROPE = Rope.Cat["level in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
CheckLength[lexed, me, 2, 2];
CheckHead[lexed, $ediflevel, me];
l ← ToINT[LFetch[lexed, 1], me.Concat[" contains bad number"], 0, 2];
lexedRA ← lexedRA;
};
WAppend:
PROC [oldHead, oldTail: WrittenList, x: Written]
RETURNS [newHead, newTail: WrittenList] = {
this: WrittenList = LIST[x];
IF oldHead =
NIL
THEN newHead ← newTail ← this
ELSE {newHead ← oldHead; oldTail.rest ← newTail ← this};
};
ParseWritten:
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [w: Written] = {
me: ROPE = Rope.Cat["written in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
accTail: AccountingList ← NIL;
stdTail: StdTail ← [];
CheckLength[lexed, me, 1];
CheckHead[lexed, $written, me];
w ← NEW[WrittenPrivate ← []];
FOR lexed ← lexed.rest, lexed.rest
WHILE lexed #
NIL
DO
innerMe: ROPE = Rope.Cat["statement in ", me];
part: LORA = ToLORA[lexed.first, me.Cat[" contains a non-statement"]];
key: ATOM;
CheckLength[part, innerMe, 1];
key ← ToATOM[part.first, innerMe.Cat[" doesn't begin with a keyword"]];
SELECT LcA[key]
FROM
$timestamp => {
IF w.time # BasicTime.nullGMT THEN Error[Rope.Cat["Multiple timestamps in ", me]];
w.time ← ParseTime[part, me];
};
$accounting => {
a: Accounting = ParseAccounting[part, me];
[w.accountings, accTail] ← AccAppend[w.accountings, accTail, a];
};
$comment => {
c: Comment = ParseComment[part, me];
[w.comments, stdTail.c] ← CAppend[w.comments, stdTail.c, c];
};
$userdata => {
ue: UserExtension = ParseUserExtension[part, me];
[w.ues, stdTail.ues] ← UEAppend[w.ues, stdTail.ues, ue];
};
ENDCASE => Error[IO.PutFR["%g statement found in %g", [atom[key]], [rope[me]] ]];
ENDLOOP;
lexedRA ← lexedRA;
};
ParseTime:
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [time: BasicTime.
GMT] = {
me: ROPE = Rope.Cat["timestamp in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
ut: BasicTime.Unpacked ← [zone: 0, dst: no];
CheckLength[lexed, me, 7, 7];
CheckHead[lexed, $timestamp, me];
ut.year ← ToINT[LFetch[lexed, 1], me.Concat[" contains bad year"], 1900, 2000];
ut.month ← VAL[CARDINAL[ToINT[LFetch[lexed, 2], me.Concat[" contains bad month"], 1, 12]-1]];
ut.day ← ToINT[LFetch[lexed, 3], me.Concat[" contains bad day"], 1, 31];
ut.hour ← ToINT[LFetch[lexed, 4], me.Concat[" contains bad hour"], 0, 23];
ut.minute ← ToINT[LFetch[lexed, 5], me.Concat[" contains bad minute"], 0, 59];
ut.second ← ToINT[LFetch[lexed, 6], me.Concat[" contains bad second"], 0, 59];
time ← BasicTime.Pack[ut];
lexedRA ← lexedRA;
};
AccAppend:
PROC [oldHead, oldTail: AccountingList, x: Accounting]
RETURNS [newHead, newTail: AccountingList] = {
this: AccountingList = LIST[x];
IF oldHead =
NIL
THEN newHead ← newTail ← this
ELSE {newHead ← oldHead; oldTail.rest ← newTail ← this};
};
ParseAccounting:
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [a: Accounting] = {
me: ROPE = Rope.Cat["accounting in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
CheckLength[lexed, me, 1, 2];
CheckHead[lexed, $accounting, me];
a ←
NEW[AccountingPrivate ← [
name: ToATOM[lexed.rest.first, me.Concat[" has malformed name"]]
]];
FOR lexed ← lexed.rest.rest, lexed.rest
WHILE lexed #
NIL
DO
part: ROPE = ToROPE[lexed.first, me.Cat[" contains a non-string"]];
a.data ← part;
ENDLOOP;
lexedRA ← lexedRA;
};
CAppend:
PUBLIC
PROC [oldHead, oldTail: Comments, x: Comment]
RETURNS [newHead, newTail: Comments] = {
this: Comments = LIST[x];
IF oldHead =
NIL
THEN newHead ← newTail ← this
ELSE {newHead ← oldHead; oldTail.rest ← newTail ← this};
};
ParseComment:
PUBLIC
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [comment: Comment] = {
me: ROPE = Rope.Cat["comment in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
tail: RopeList ← NIL;
CheckLength[lexed, me, 1];
CheckHead[lexed, $comment, me];
FOR lexed ← lexed, lexed.rest
WHILE lexed #
NIL
DO
innerMe: ROPE = Rope.Cat["form in ", me];
c: ROPE = ToROPE[lexed.first, me.Cat[" contains a non-string"]];
[comment, tail] ← RAppend[comment, tail, c];
ENDLOOP;
lexedRA ← lexedRA;
};
RAppend:
PROC [oldHead, oldTail: RopeList, x:
ROPE]
RETURNS [newHead, newTail: RopeList] = {
this: RopeList = LIST[x];
IF oldHead =
NIL
THEN newHead ← newTail ← this
ELSE {newHead ← oldHead; oldTail.rest ← newTail ← this};
};
UEAppend:
PUBLIC
PROC [oldHead, oldTail:
LIST
OF UserExtension, x: UserExtension]
RETURNS [newHead, newTail:
LIST
OF UserExtension] = {
this: LIST OF UserExtension = LIST[x];
IF oldHead =
NIL
THEN newHead ← newTail ← this
ELSE {newHead ← oldHead; oldTail.rest ← newTail ← this};
};
ParseUserExtension:
PUBLIC
PROC [lexedRA:
REF
ANY, context:
ROPE]
RETURNS [ue: UserExtension] = {
me: ROPE = Rope.Cat["user extension in ", context];
lexed: LORA ← ToLORA[lexedRA, me.Concat[" not a statement"]];
name: ATOM;
tail: LORA ← NIL;
CheckLength[lexed, me, 2];
CheckHead[lexed, $userdata, me];
name ← ToATOM[lexed.rest.first, me.Concat[" doesn't have a proper name"]];
ue ← [name: [Atom.GetPName[name]], stuff: lexed.rest.rest];
};
CheckLength:
PUBLIC
PROC [lora:
LORA, context:
ROPE, min:
INT ← 0, max:
INT ←
LAST[
INT]] = {
lim: INT = IF max = LAST[INT] THEN min ELSE (max+1);
SELECT Length[lora, lim]
FROM
< min => Error[IO.PutFR["Too few forms in %g", [rope[context]] ]];
> max => Error[IO.PutFR["Too many forms in %g", [rope[context]] ]];
ENDCASE => NULL;
};
CheckHead:
PUBLIC
PROC [lexed:
LORA, keyword:
ATOM, context:
ROPE] = {
msg: ROPE = IO.PutFR["%g does not begin with %g", [rope[context]], [atom[keyword]]];
IF LcA[ToATOM[lexed.first, msg]] # keyword THEN Error[msg];
};
Length:
PUBLIC
PROC [lora:
LORA, max:
INT ←
LAST[
INT]]
RETURNS [length:
INT] = {
length ← 0;
FOR lora ← lora, lora.rest
WHILE lora #
NIL
AND length < max
DO
length ← length + 1;
ENDLOOP;
length ← length;
};
LFetch:
PUBLIC
PROC [lora:
LORA, index:
NAT]
RETURNS [ra:
REF
ANY] = {
WHILE index # 0
DO
lora ← lora.rest;
index ← index - 1;
ENDLOOP;
ra ← lora.first;
};
ToQual:
PUBLIC
PROC [ra:
REF
ANY, bitch:
ROPE]
RETURNS [q: Qual] = {
WITH ra
SELECT
FROM
lexed:
LORA => {
CheckLength[lexed, bitch, 3, 3];
CheckHead[lexed, $qualify, bitch];
q.outer ← ToROPE[lexed.rest.first, bitch];
q.inner ← ToROPE[lexed.rest.rest.first, bitch];
};
ENDCASE => Error[bitch];
};
ToATOM:
PUBLIC
PROC [ra:
REF
ANY, bitch:
ROPE]
RETURNS [a:
ATOM] = {
WITH ra
SELECT
FROM
x: ATOM => a ← x;
ENDCASE => Error[bitch];
};
ToLORA:
PUBLIC
PROC [ra:
REF
ANY, bitch:
ROPE]
RETURNS [lora:
LORA] = {
WITH ra
SELECT
FROM
x: LORA => lora ← x;
ENDCASE => Error[bitch];
};
ToROPE:
PUBLIC
PROC [ra:
REF
ANY, bitch:
ROPE]
RETURNS [r:
ROPE] = {
WITH ra
SELECT
FROM
x: ROPE => r ← x;
ENDCASE => Error[bitch];
};
ToINT:
PUBLIC
PROC [ra:
REF
ANY, bitch:
ROPE, min:
INT ←
FIRST[
INT], max:
INT ←
LAST[
INT]]
RETURNS [i:
INT] = {
WITH ra
SELECT
FROM
x: REF INT => IF (i ← x^) NOT IN [min .. max] THEN Error[bitch];
ENDCASE => Error[bitch];
};
}.