File MPCParser.Mesa
November 1979 by MN
Updated: August 25, 1980 5:46 PM
Updated by TRS to conform to new parser/interp December 5, 1980 5:19 PM
Last Edited by: McCreight, January 28, 1985 6:22:45 pm PST
This version is different from the MPC79 version in that calls to
interpreter procedures pass pointers to descriptor records for the
various objects. Also, the bounding boxes of projects are allowed
for in the .mpc document
Top level syntax is:
Chips: A B C; or Chips: all; (default is all)
Layers: diffusion poly; or Layers: all; (default is all)
OutputDir: [Ivy]<MPC79>; or OutputDir: [Ivy]; (default is local disk)
InputFile: MPC79.mpc;
Only "InputFile" is mandatory and must come last
lower case keywords are recognized
DIRECTORY
Ascii, Atom, Convert, FS, IO, IODefs, List, MPCDefs, ParserErrorDefs, PartitionDefs, RefText, MPCParserDefs, Rope;
MPCParser: CEDAR PROGRAM
IMPORTS
Ascii, Atom, Convert, FS, IO, IODefs, List, MPCDefs, RefText, Rope
EXPORTS MPCParserDefs =
BEGIN
OPEN MPCDefs;
Types and global stuff
Line: REF TEXTNEW[TEXT[1000]];
LineCount: CARDINAL;
Index: CARDINAL;
Token: TYPE = RECORD [left, right: CARDINAL];
nullToken: Token = [1, 0];
DictEntry: TYPE = RECORD [name: Rope.ROPE, value: PROC];
CurrentDict: LIST OF DictEntry ← NIL;
End: BOOLFALSE;
Params: ARRAY [0..10) OF Token ← ALL[[0, 0]];
NParams: CARDINAL ← 0;
ErrorCount: CARDINAL;
Execute: BOOL;
Top level parameters
OutputDirectory: Rope.ROPE;
InStream: IO.STREAM;
InStreamCreated: BOOL;
MPC-level descriptor
mpcDescriptor: REF MPCDescriptor;
Wafer level parameters
waferDescriptor: REF WaferDescriptor;
Chip level parameters
chipDescriptor: REF ChipDescriptor;
Project level parameters
projectDescriptor: REF ProjectDescriptor;
Scan: PUBLIC PROC [s: IO.STREAM, execute: BOOL]
RETURNS [errorCount: CARDINAL] =
Scan MPC document on stream s, executing it if requested
BEGIN
gotone, known: BOOL;
token: Token;
proc: PROC;
InStream ← s;
InStreamCreated ← FALSE;
Execute ← execute;
ErrorCount ← 0; --do it here because initscanner called at end
InitScanner[];
UNTIL End DO
IF InStream.EndOf[] --force END
THEN BEGIN Line.length ← Index ← 0; Line ← RefText.AppendRope[Line, "end"]; END
ELSE ReadLine[];
DO
[gotone, token] ← ReadToken[];
IF ~gotone THEN EXIT;
[known, proc] ← LookUp[token];
IF ~known THEN
BEGIN Error["Unknown keyword", token]; Index ← Line.length; END
ELSE BEGIN IF proc # Comment THEN ScanParams[]; proc[]; END;
ENDLOOP;
ENDLOOP;
IF InStreamCreated THEN InStream.Close[];
RETURN[ErrorCount];
END;
InitScanner: PROC =
BEGIN
LineCount ← 0;
Line.length ← 0;
Index ← 0;
mpcDescriptor ← NEW[MPCDescriptor];
waferDescriptor ← NIL;
chipDescriptor ← NIL;
projectDescriptor ← NIL;
CurrentDict ← TopDict;
End ← FALSE;
END;
ReadLine: PROC =
BEGIN
Read line up to CR ', or '; - CR is discarded, '; and ', are included
ch: CHARACTER;
Line.length ← Index ← 0;
UNTIL InStream.EndOf[] DO
ch ← InStream.GetChar[];
SELECT ch FROM
Ascii.CR => BEGIN LineCount ← LineCount + 1; EXIT; END;
Ascii.ESC => LOOP;
Ascii.ControlZ =>
BEGIN
UNTIL InStream.EndOf[] OR InStream.GetChar[] = Ascii.CR DO
ENDLOOP;
LineCount ← LineCount + 1;
EXIT;
END;
ENDCASE =>
BEGIN
IF Line.length < Line.maxLength THEN
BEGIN
Line ← RefText.AppendChar[Line, ch];
IF ch = '; OR ch = ', THEN EXIT;
END
ELSE
BEGIN
Error["Line too long", nullToken]; --boo
UNTIL InStream.EndOf[] OR (ch ← InStream.GetChar[]) = ';
DO
IF ch = Ascii.CR THEN BEGIN LineCount ← LineCount + 1; EXIT; END; ENDLOOP;
END;
END;
ENDLOOP;
END;
Error: PROC [s: Rope.ROPE, t: Token] =
BEGIN
IODefs.WriteString["*** Error in line "];
IODefs.WriteDecimal[LineCount];
IODefs.WriteString[" - "];
IODefs.WriteLine[s];
IODefs.WriteLine[Rope.FromRefText[Line]];
IF t.left <= t.right THEN
BEGIN
i: CARDINAL;
FOR i IN [0..t.left) DO
IODefs.WriteChar[IF Line[i] = Ascii.TAB THEN Ascii.TAB ELSE Ascii.SP]; ENDLOOP;
IODefs.WriteLine["^"];
END;
ErrorCount ← ErrorCount + 1;
END;
ReadToken: PROC RETURNS [GotOne: BOOL, t: Token] =
BEGIN
left: CARDINAL;
IF Index >= Line.length THEN RETURN[FALSE, nullToken];
UNTIL ~Delim[Line[Index]] DO
Index ← Index + 1;
IF Index >= Line.length THEN RETURN[FALSE, nullToken];
ENDLOOP;
left ← Index;
DO
Index ← Index + 1;
IF Index >= Line.length OR Delim[Line[Index]] THEN
RETURN[TRUE, [left, Index - 1]];
ENDLOOP;
END;
Delim: PROC [ch: CHARACTER] RETURNS [BOOL] =
BEGIN
RETURN[ch = Ascii.SP OR ch = Ascii.TAB OR ch = '; OR ch = ': OR ch = '= OR ch = ',];
END;
LookUp: PROC [t: Token] RETURNS [known: BOOL, value: PROC] =
BEGIN
FOR d: LIST OF DictEntry ← CurrentDict, d.rest WHILE d#NIL DO
IF d.first.name.Length = t.right-t.left+1 THEN
FOR i: NAT IN [t.left..t.right] DO
IF d.first.name.Fetch[i-t.left] # Ascii.Lower[Line[i]] THEN EXIT;
REPEAT
FINISHED => RETURN[TRUE, d.first.value];
ENDLOOP;
ENDLOOP;
RETURN[FALSE, NullProc];
END;
TopDict: LIST OF DictEntry ← LIST [
["chips", TChips], ["layers", TLayers], ["outputdir", TOutputDir], [
"inputfile", TInputFile],
["title", TTitle], ["date", TDate], ["account", TAccount], [
"maskset", TWafer], ["end", TEnd], ["comment", Comment]];
WaferDict: LIST OF DictEntry ← LIST [
["resolution", WResolution], ["size", WSize], ["layers", WLayers], [
"chip", WChip], ["maskset", WWafer], ["end", WEnd], ["comment", Comment]];
ChipDict: LIST OF DictEntry ← LIST [
["dimensions", CDimensions], ["positions", CPositions], ["project", CProject],
["chip", CChip], ["maskset", CWafer], ["end", CEnd], ["comment", Comment]];
ProjectDict: LIST OF DictEntry ← LIST [
["rotation", PRotation], ["translation", PTranslation], [
"boundingbox", PBoundingBox], ["layers", PLayers], ["file", PFile], [
"project", PProject], ["chip", PChip], ["maskset", PWafer], ["end", PEnd], [
"comment", Comment]];
TChips: PROC =
BEGIN
DO
IF NParams # 0 THEN
BEGIN
IF NParams = 1 THEN
BEGIN
mpcDescriptor.chips ← List.Union[LIST[Atom.MakeAtom[TokenToString[Params[0]]]], mpcDescriptor.chips];
END
ELSE Error["ChipName expected", nullToken];
END;
IF Line.length # 0 AND Line[Line.length - 1] = '; THEN EXIT;
ReadLine[];
ScanParams[];
ENDLOOP;
END;
TLayers: PROC =
BEGIN
DO
IF NParams # 0 THEN
BEGIN
IF NParams = 1 THEN
BEGIN
mpcDescriptor.layers ← List.Union[LIST[Atom.MakeAtom[TokenToString[Params[0]]]], mpcDescriptor.layers];
END
ELSE Error["LayerName expected", nullToken];
END;
IF Line.length # 0 AND Line[Line.length - 1] = '; THEN EXIT;
ReadLine[];
ScanParams[];
ENDLOOP;
END;
TOutputDir: PROC =
BEGIN OutputDirectory ← GetOneString["OutputDirectory"]; END;
TInputFile: PROC =
BEGIN
IF NParams = 1 THEN
BEGIN
fileName: Rope.ROPE ← TokenToString[Params[0]];
BEGIN
InStream ← FS.StreamOpen[fileName, $read ! FS.Error => GOTO ContinueThisFile];
InStreamCreated ← TRUE;
LineCount ← 0;
Line.length ← 0;
EXITS
ContinueThisFile => Error["Unknown file", Params[0]];
END;
END
ELSE Error["FileName expected", nullToken];
END;
TTitle: PROC = BEGIN mpcDescriptor.title ← GetOneString["Title"]; END;
TDate: PROC =
BEGIN
IF NParams = 1 THEN
BEGIN
IF TLength[Params[0]] = 6 THEN mpcDescriptor.date ← TokenToString[Params[0]]
ELSE Error["Date must have 6 chars", Params[0]];
END
ELSE Error["Date(6 chars) expected", nullToken];
END;
TAccount: PROC = BEGIN mpcDescriptor.account ← GetOneString["AccountName"]; END;
TWafer: PROC =
BEGIN
WWafer[];
IF Execute THEN
IF ~InitMPC[mpcDescriptor] THEN Error["Error in call to InitMPC", nullToken];
END;
TEnd: PROC =
BEGIN
InitScanner[]; --to clean up
End ← TRUE;
END;
WResolution: PROC =
BEGIN
IF NParams = 1 THEN
waferDescriptor.outputUnitsPerMicron ← TokenToDecimal[
Params[0] !
Convert.Error => BEGIN Error["Invalid number", Params[0]]; CONTINUE; END]
ELSE Error["Number expected", nullToken];
END;
WSize: PROC =
BEGIN [waferDescriptor.waferSizeX, waferDescriptor.waferSizeY] ← GetTwoLongNumbers[]; END;
WLayers: PROC =
BEGIN
DO
IF NParams # 0 THEN
BEGIN
IF NParams = 3 AND TLength[Params[1]] = 2 AND TLength[Params[2]] = 2 THEN
BEGIN
AddLayerDef[TokenToString[Params[0]], TokenToString[Params[1]], TokenToString[Params[2]]];
END
ELSE
Error[
"IntermediateName LayerNumber(2 chars) LayerName(2 chars) expected",
nullToken];
END;
IF Line.length # 0 AND Line[Line.length - 1] = '; THEN EXIT;
ReadLine[];
ScanParams[];
ENDLOOP;
END;
WChip: PROC =
BEGIN
CChip[];
IF Execute THEN
IF ~InitWafer[waferDescriptor] THEN
Error["Error in call to InitWafer", nullToken];
END;
nextLayerNumber: PartitionDefs.layerNumber;
WWafer: PROC =
BEGIN
waferDescriptor ← NEW[WaferDescriptor ← [inMPC: mpcDescriptor]];
nextLayerNumber ← 0;
IF NParams = 1 THEN waferDescriptor.waferName ← TokenToString[Params[0]]
ELSE Error["WaferName expected", nullToken];
CurrentDict ← WaferDict;
END;
WEnd: PROC =
BEGIN
IF Execute AND ~FinishMPC[] THEN Error["Error in FinishMPC", nullToken];
TEnd[];
END;
CDimensions: PROC =
BEGIN [chipDescriptor.chipSizeX, chipDescriptor.chipSizeY] ← GetTwoNumbers[]; END;
CPositions: PROC =
BEGIN
DO
IF NParams # 0 THEN
BEGIN
x, y: INT;
[x, y] ← GetTwoLongNumbers[];
chipDescriptor.chipPositionList ← CONS[[x: x, y: y], chipDescriptor.chipPositionList];
END;
IF Line.length # 0 AND Line[Line.length - 1] = '; THEN EXIT; --might be nice to reverse list
ReadLine[];
ScanParams[];
ENDLOOP;
END;
CProject: PROC =
BEGIN
projectDescriptor ← NEW[ProjectDescriptor ← [inChip: chipDescriptor]];
IF NParams = 1 THEN projectDescriptor.projectID ← TokenToString[Params[0]]
ELSE Error["ProjectName expected", nullToken];
IF Execute THEN
IF ~InitChip[chipDescriptor] THEN Error["Error in call to InitChip", nullToken];
CurrentDict ← ProjectDict;
END;
CChip: PROC =
BEGIN
chipDescriptor ← NEW[ChipDescriptor ←
[chipName: GetOneString["ChipName"],
inWafer: waferDescriptor]];
CurrentDict ← ChipDict;
check whether to process this chip
IF mpcDescriptor.chips = NIL
OR List.Memb[Atom.MakeAtom[chipDescriptor.chipName], mpcDescriptor.chips]
OR List.Memb[$all, mpcDescriptor.chips] THEN RETURN;
not to be processed - skip to end
UNTIL InStream.EndOf[] DO
gotone: BOOL;
token: Token;
ReadLine[];
[gotone, token] ← ReadToken[];
IF ~gotone THEN LOOP;
IF TokenEqualsString[token, "chip"] OR TokenEqualsString[token, "maskset"]
OR TokenEqualsString[token, "end"] THEN BEGIN Index ← 0; EXIT; END;
ENDLOOP;
END;
CWafer: PROC =
BEGIN
IF Execute AND ~FinishWafer[] THEN Error["Error in FinishWafer", nullToken];
WWafer[];
END;
CEnd: PROC =
BEGIN
IF Execute AND ~FinishWafer[] THEN Error["Error in FinishWafer", nullToken];
WEnd[];
END;
PRotation: PROC =
{[projectDescriptor.rotateX, projectDescriptor.rotateY] ← GetTwoLongNumbers[]};
PTranslation: PROC =
{[projectDescriptor.translateX, projectDescriptor.translateY] ← GetTwoLongNumbers[]};
PBoundingBox: PROC =
BEGIN
[projectDescriptor.centerX, projectDescriptor.centerY, projectDescriptor.width, projectDescriptor.height] ← GetFourLongNumbers[];
END;
PLayers: PROC =
BEGIN
DO
IF NParams # 0 THEN
BEGIN
IF NParams = 3 AND TLength[Params[0]] <= 4 THEN
BEGIN
AddLayerUse[TokenToString[Params[0]], TokenToString[Params[1]], TokenToReal[Params[2]]];
END
ELSE Error["CIFName IntermediateName Stretch expected", nullToken];
END;
IF Line.length # 0 AND Line[Line.length - 1] = '; THEN EXIT;
ReadLine[];
ScanParams[];
ENDLOOP;
END;
PFile: PROC =
BEGIN
IF NParams = 1 THEN projectDescriptor.cifFileName ← TokenToString[Params[0]]
ELSE Error["FileName expected", nullToken];
END;
PProject: PROC =
BEGIN
DoProject[];
projectDescriptor ← NEW[ProjectDescriptor ← [inChip: chipDescriptor]];
IF NParams = 1 THEN projectDescriptor.projectID ← TokenToString[Params[0]]
ELSE Error["ProjectName expected", nullToken];
END;
DoProject: PROC =
BEGIN
IF Execute THEN
BEGIN
ok: BOOL;
errorSummary: ARRAY ParserErrorDefs.ErrorType OF CARDINAL;
[ok, errorSummary] ← ConvertProject[projectDescriptor];
IF ~ok THEN Error["Error in call to ConvertProject", nullToken];
END;
END;
PChip: PROC = BEGIN EndChip[]; CChip[]; END;
PWafer: PROC = BEGIN EndChip[]; CWafer[]; END;
PEnd: PROC = BEGIN EndChip[]; CEnd[]; END;
EndChip: PROC =
BEGIN
DoProject[];
IF Execute THEN WriteLayerFiles[];
IF Execute AND ~FinishChip[] THEN Error["Error in FinishChip", nullToken];
END;
NullProc: PROC = BEGIN END;
Comment: PROC =
BEGIN --search for a ';
UNTIL InStream.EndOf[]
OR (Line.length # 0 AND Line[Line.length - 1] = ';) DO ReadLine[]; ENDLOOP;
Index ← Line.length;
END;
ScanParams: PROC =
BEGIN
gotone: BOOL;
token: Token;
NParams ← 0;
DO
[gotone, token] ← ReadToken[];
IF gotone THEN BEGIN Params[NParams] ← token; NParams ← NParams + 1; END
ELSE EXIT;
ENDLOOP;
END;
TokenEqualsString: PROC [t: Token, s: Rope.ROPE] RETURNS [BOOL] =
BEGIN
RETURN[Rope.Equal[s1: TokenToString[t], s2: s, case: FALSE]];
END;
TokenToString: PROC [t: Token] RETURNS [s: Rope.ROPE] =
BEGIN
tr: REF TEXT ← RefText.ObtainScratch[1000];
tr.length ← 0;
tr ← RefText.Append[to: tr, from: Line, start: t.left, len: TLength[t]];
s ← Rope.FromRefText[tr];
RefText.ReleaseScratch[tr];
END;
TLength: PROC [t: Token] RETURNS [CARDINAL] =
BEGIN RETURN[t.right - t.left + 1]; END;
TokenToDecimal: PROC [t: Token] RETURNS [INT] =
{ RETURN[Convert.IntFromRope[TokenToString[t]]] };
TokenToReal: PROC [t: Token] RETURNS [REAL] =
BEGIN
r: Rope.ROPE = TokenToString[t];
BEGIN ENABLE Convert.Error => GOTO TryIntegerStupid;
RETURN[Convert.RealFromRope[r]];
EXITS TryIntegerStupid => RETURN[Convert.IntFromRope[r]];
END;
END;
GetTwoNumbers: PROC RETURNS [n1, n2: CARDINAL] =
BEGIN
n1 ← n2 ← 0;
IF NParams = 2 THEN
BEGIN
n1 ← TokenToDecimal[
Params[0] !
Convert.Error => BEGIN Error["Invalid number", Params[0]]; CONTINUE; END];
n2 ← TokenToDecimal[
Params[1] !
Convert.Error => BEGIN Error["Invalid number", Params[1]]; CONTINUE; END];
END
ELSE Error["Two Numbers expected", nullToken];
END;
GetTwoLongNumbers: PROC RETURNS [n1, n2: INT] =
BEGIN
n1 ← n2 ← 0;
IF NParams = 2 THEN
BEGIN
n1 ← TokenToDecimal[
Params[0] !
Convert.Error => BEGIN Error["Invalid number", Params[0]]; CONTINUE; END];
n2 ← TokenToDecimal[
Params[1] !
Convert.Error => BEGIN Error["Invalid number", Params[1]]; CONTINUE; END];
END
ELSE Error["Two Numbers expected", nullToken];
END;
GetFourLongNumbers: PROC RETURNS [n1, n2, n3, n4: INT] =
BEGIN
numbers: ARRAY [0..4) OF INTALL[0];
i: CARDINAL;
IF NParams = 4 THEN
BEGIN
FOR i IN [0..4) DO
numbers[i] ← TokenToDecimal[Params[i]
! Convert.Error =>
BEGIN Error["Invalid number", Params[i]]; CONTINUE; END];
ENDLOOP;
END
ELSE Error["Four Numbers expected", nullToken];
RETURN[numbers[0], numbers[1], numbers[2], numbers[3]];
END;
GetOneString: PROC [error: Rope.ROPE] RETURNS [into: Rope.ROPE] =
BEGIN
IF NParams = 1 THEN into ← TokenToString[Params[0]]
ELSE Error[IO.PutFR["%g expected", IO.rope[error]], nullToken];
END;
AddLayerDef: PROC [intname, layerNumber, layerName: Rope.ROPE] =
BEGIN
intAtom: ATOM ← Atom.MakeAtom[intname];
IF waferDescriptor.layerFileList.GetPropFromList[intAtom] # NIL THEN
Error["Layer redefined - overwritten", nullToken];
waferDescriptor.layerFileList ← waferDescriptor.layerFileList.PutPropOnList[intAtom,
NEW[LayerFileItemRec ← [
intName: intAtom,
layerNumber: layerNumber,
layerName: layerName,
pFileLayer: nextLayerNumber,
include: mpcDescriptor.layers=NIL
OR List.Memb[intAtom, mpcDescriptor.layers]
OR List.Memb[$all, mpcDescriptor.layers]]]];
nextLayerNumber ← nextLayerNumber+1;
END;
AddLayerUse: PROC [cifname, intname: Rope.ROPE, stretch: REAL] =
BEGIN
cifNameAtom: ATOM = Atom.MakeAtom[cifname];
intNameAtom: ATOM = Atom.MakeAtom[intname];
r: REF ANY;
IF (r ← waferDescriptor.layerFileList.GetPropFromList[intNameAtom]) # NIL THEN
BEGIN
q: REF ANY;
group: LayerGroup;
WHILE (q ← NARROW[projectDescriptor.layerMap.GetPropFromList[cifNameAtom]]) = NIL DO
projectDescriptor.layerMap ← projectDescriptor.layerMap.PutPropOnList[
prop: cifNameAtom,
val: NEW[LayerGroupRec ← [cifName: cifNameAtom, maskLayers: NIL]]];
ENDLOOP;
group ← NARROW[q];
group.maskLayers ← group.maskLayers.PutPropOnList[
prop: intNameAtom,
val: NEW[LayerAssocRec ← [
fromLayer: cifNameAtom,
toLayer: NARROW[r],
stretch: stretch]]];
END
ELSE Error["Unspecified layername", Params[1]];
END;
WriteLayerFiles: PROC =
BEGIN --Write included layers for this project
FOR pl: Atom.PropList ← waferDescriptor.layerFileList, pl.rest WHILE pl#NIL DO
WITH pl.first.val SELECT FROM
lfi: LayerFileItem => IF lfi.include THEN [] ← WriteLayerFile[lfi, chipDescriptor];
ENDCASE => ERROR;
ENDLOOP;
END;
END.