DIRECTORY
CIFParser USING [Point, Path, Transformation, Registration, TMatrix],
Convert USING [IntFromRope, CardFromRope],
IO USING [STREAM, GetTokenRope, CharClass],
Real USING [Round, SqRt],
Rope USING [ROPE, Cat, Concat, Length, Fetch, FromChar];
Types
external types
ROPE: TYPE ~ Rope.ROPE;
local types
CmdType: TYPE ~ {emptyCmd, endCmd, defDeleteCmd, defStartCmd, defFinishCmd, primCmd};
PrimCmdType: TYPE ~ {polygonCmd, boxCmd, roundFlashCmd, wireCmd, layerCmd, callCmd, userExtensionCmd, commentCmd, error};
Cmd:
TYPE ~
RECORD[
type: CmdType ← primCmd,
token: ROPE ← NIL
];
PrimCmd:
TYPE ~
RECORD[
type: PrimCmdType ← error,
token: ROPE ← NIL
];
State:
TYPE ~
REF StateRec;
StateRec:
TYPE ~
RECORD[
layer: ROPE ← NIL,
inSymbol: BOOLEAN ← FALSE
];
Stream:
TYPE ~
REF StreamRec;
StreamRec: TYPE ~ RECORD[lastToken: ROPE, s: IO.STREAM];
TokenKind: TYPE ~ {upperChar, digit, semicolon, leftPar, rightPar, blank};
global variables
emptyCmdNotOK: BOOLEAN ← FALSE; --it looks as if it was forbidden AND used...
Stream Parser
Error:
PUBLIC
ERROR [msg:
ROPE, cifFile:
IO.STREAM] ~
CODE;
ClientError:
PUBLIC
ERROR [clientMsg:
ROPE] ~
CODE;
Parse:
PUBLIC
PROC [cifStream:
IO.STREAM, reg: Registration] ~ {
cifFile = {{blank} [command] semi} endCmd {blank}.
command = primCmd | defDeleteCmd | defStartCmd semi {{blank} [primCmd] semi} defFinishCmd
ENABLE ClientError => Error[Rope.Concat["client error: ", clientMsg], cifStream];
cifFile: Stream ← NEW[StreamRec ← [NIL, cifStream]];
cmd: Cmd;
state: BOOLEAN ← FALSE;
DO
cmd ← GetCmd[cifFile];
SELECT cmd.type
FROM
endCmd => {CheckEnd[cifFile, state]; RETURN};
defDeleteCmd => state ← DefDelete[cifFile, reg, state];
defStartCmd => state ← DefStart[cifFile, reg, state];
defFinishCmd => state ← DefFinish[cifFile, reg, state];
primCmd => ParsePrim[cmd.token, cifFile, reg];
emptyCmd => IF emptyCmdNotOK THEN Error["empty cmd", cifFile.s];
ENDCASE => ERROR; -- CmdType def is not in sync with Parse
GoToNextCmd[cifFile];
ENDLOOP;
};
GetCmd:
PROC [cifFile: Stream]
RETURNS [cmd: Cmd] ~ {
c: CHAR;
c ← GetChar[cifFile];
cmd.token ← Rope.FromChar[c];
SELECT c
FROM
'E => cmd.type ← endCmd;
'; => cmd.type ← emptyCmd;
'D => {
c ← GetChar[cifFile];
cmd.token ← Rope.Concat[cmd.token, Rope.FromChar[c]];
SELECT c
FROM
'D => cmd.type ← defDeleteCmd;
'S => cmd.type ← defStartCmd;
'F => cmd.type ← defFinishCmd;
ENDCASE => Error[Rope.Concat[cmd.token, " : invalid cmd "], cifFile.s];
};
ENDCASE => cmd.type ← primCmd;
};
GoToNextCmd:
PROC [cifFile: Stream] ~ {
IF GetTokenKind[cifFile.lastToken]#semicolon
THEN {
cifFile.lastToken ← GetToken[cifFile];
IF GetTokenKind[cifFile.lastToken]#semicolon THEN Error["wrong cmd syntax", cifFile.s];
};
};
Cmd Procs
CheckEnd:
PROC[cifFile: Stream, state:
BOOLEAN] ~ {
endCommand = "E".
IF state THEN Error["E inside symbol def ", cifFile.s];
};
DefDelete:
PROC[cifFile: Stream, reg: Registration, state:
BOOLEAN]
RETURNS[newState:
BOOLEAN ← FALSE] ~ {
defDeleteCmd = "D" {blank} "D" integer.
index: CARD;
IF state THEN Error["DD inside symbol def ", cifFile.s];
index ← GetCard[cifFile];
reg.defDelete[index, reg.data];
};
DefStart:
PROC[cifFile: Stream, reg: Registration, state:
BOOLEAN]
RETURNS[newState:
BOOLEAN ← TRUE] ~ {
defStartCmd = "D" {blank} "S" integer [sep integer sep integer].
index, a, b: CARD;
IF state THEN Error["DS inside symbol def ", cifFile.s];
index ← GetCard[cifFile];
a ← GetCardAfterSep[cifFile, TRUE];
b ← GetCardAfterSep[cifFile, TRUE];
reg.defStart[index, a, b, reg.data];
};
DefFinish:
PROC[cifFile: Stream, reg: Registration, state:
BOOLEAN]
RETURNS[newState:
BOOLEAN ← FALSE] ~ {
defFinishCmd = "D" {blank} "F".
IF NOT state THEN Error["DF outside symbol def ", cifFile.s];
reg.defFinish[reg.data];
};
ParsePrim:
PROC [token:
ROPE, cifFile: Stream, reg: Registration] ~ {
primCmd = polygonCmd | boxCmd | roundFlashCmd | wireCmd | layerCmd | callCmd | userExtensionCmd | commentCmd.
type: PrimCmdType ← GetPrimType[token];
SELECT type
FROM
polygonCmd => Polygon[cifFile, reg];
boxCmd => Box[cifFile, reg];
roundFlashCmd => RoundFlash[cifFile, reg];
wireCmd => Wire[cifFile, reg];
layerCmd => Layer[cifFile, reg];
callCmd => Call[cifFile, reg];
userExtensionCmd => UserExtension[token, cifFile, reg];
commentCmd => Comment[token, cifFile, reg];
error => Error[Rope.Cat[token," is not a CIF cmd"], cifFile.s];
ENDCASE => ERROR; -- PrimCmdType def is not in sync with ParsePrim
};
GetPrimType:
PROC [token:
ROPE]
RETURNS [type: PrimCmdType] ~ {
type ←
SELECT Rope.Fetch[token]
FROM
'P => polygonCmd,
'B => boxCmd,
'R => roundFlashCmd,
'W => wireCmd,
'L => layerCmd,
'C => callCmd,
IN ['0..'9] => userExtensionCmd,
'( => commentCmd,
ENDCASE => error;
};
PrimCmd Procs
Polygon:
PROC[cifFile: Stream, reg: Registration] ~ {
polygonCmd = "P" path.
path: Path;
path ← GetPath[cifFile];
reg.polygon[path, reg.data]
};
Box:
PROC[cifFile: Stream, reg: Registration] ~ {
boxCmd = "B" integer sep integer sep point [sep point]..
length, width: CARD;
center, direction: Point;
length ← GetCard[cifFile];
width ← GetCardAfterSep[cifFile];
center ← GetPointAfterSep[cifFile];
direction ← GetPointAfterSep[cifFile, TRUE];
IF direction=noPoint THEN direction ← [1, 0];
reg.box[length, width, center, direction, reg.data]
};
RoundFlash:
PROC[cifFile: Stream, reg: Registration] ~ {
roundFlashCmd = "R" integer sep point.
diameter: CARD;
center: Point;
diameter ← GetCard[cifFile];
center ← GetPointAfterSep[cifFile];
reg.roundFlash[diameter, center, reg.data]
};
Wire:
PROC[cifFile: Stream, reg: Registration] ~ {
wireCmd = "W" integer sep path.
width: CARD;
path: Path;
width ← GetCard[cifFile];
path ← GetPathAfterSep[cifFile];
reg.wire[width, path, reg.data]
};
Layer:
PROC[cifFile: Stream, reg: Registration] ~ {
layerCmd = "L" {blank} shortname.
shortname: ROPE;
shortname ← GetShortName[cifFile];
reg.layer[shortname, reg.data]
};
Call:
PROC[cifFile: Stream, reg: Registration] ~ {
callCmd = "C" integer transformation.
symbol: CARD;
transformation: Transformation;
symbol ← GetCard[cifFile];
transformation ← GetTransformation[cifFile];
reg.call[symbol, transformation, reg.data]
};
UserExtension:
PROC[token:
ROPE, cifFile: Stream, reg: Registration] ~ {
userExtensionCmd = digit userText.
digit: CARD;
userText: ROPE;
digit ← Convert.IntFromRope[token];
userText ← GetUserText[cifFile];
reg.userExtension[digit, userText, reg.data]
};
Comment:
PROC[token:
ROPE, cifFile: Stream, reg: Registration] ~ {
commentCmd = "(" commentText ")".
comment: ROPE;
comment ← Rope.Cat[token, GetCommentText[cifFile]];
reg.comment[comment, reg.data]
};
Typed Get Functions
GetTransformation:
PROC [cifFile: Stream]
RETURNS [t: Transformation] ~ {
transformation = {{blank} ("T" point | "M" {blank} "X" | "M" {blank} "Y" | "R" point)}.
transf: Transformation;
t ← transf ← LIST[[translation, [0, 0]]];
DO
SELECT GetChar[cifFile]
FROM
'; => RETURN[IF t.rest=NIL THEN t ELSE t.rest];
'T => transf.rest ← LIST[[translation, GetPoint[cifFile]]];
'R => transf.rest ← LIST[[rotation, GetPoint[cifFile]]];
'M =>
SELECT GetChar[cifFile]
FROM
'X => transf.rest ← LIST[[xSym, noPoint]];
'Y => transf.rest ← LIST[[ySym, noPoint]];
ENDCASE => Error["incorrect Mirror Transformation", cifFile.s];
ENDCASE => Error["incorrect Transformation", cifFile.s];
transf ← transf.rest;
ENDLOOP;
};
GetPath:
PROC [cifFile: Stream]
RETURNS [path: Path] ~ {
path = point {sep point}.
p: Path;
p ← path ← LIST[GetPoint[cifFile]];
DO
point: Point;
point ← GetPointAfterSep[cifFile, TRUE];
IF point=noPoint THEN RETURN;
p.rest ← LIST[point];
p ← p.rest;
ENDLOOP;
};
GetPathAfterSep:
PROC [cifFile: Stream]
RETURNS [path: Path] ~ {
path = point {sep point}.
p: Path;
p ← path ← LIST[GetPointAfterSep[cifFile]];
DO
point: Point;
point ← GetPointAfterSep[cifFile, TRUE];
IF point=noPoint THEN RETURN;
p.rest ← LIST[point];
p ← p.rest;
ENDLOOP;
};
GetPoint:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [point: Point] ~ {
point = sInteger sep sInteger
point.x ← GetInt[cifFile, mayBeDefaulted];
IF point.x=defaultInt THEN RETURN[noPoint];
point.y ← GetIntAfterSep[cifFile];
};
GetPointAfterSep:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [point: Point] ~ {
point.x ← GetIntAfterSep[cifFile, mayBeDefaulted];
IF point.x=defaultInt THEN RETURN[noPoint];
point.y ← GetIntAfterSep[cifFile];
};
GetInt:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [i:
INT ← defaultInt] ~ {
sInteger = {sep} ["-"] integerD.
integerD = digit {digit}.
token: ROPE;
tokenKind: TokenKind ← GetTokenKind[cifFile.lastToken];
IF tokenKind=semicolon THEN IF mayBeDefaulted THEN RETURN ELSE Error["missing INT", cifFile.s];
token ← GetToken[cifFile];
tokenKind ← GetTokenKind[token];
SELECT tokenKind
FROM
digit => i ← Convert.IntFromRope[token];
upperChar => i ← Convert.IntFromRope[GetToken[cifFile]];
semicolon => IF mayBeDefaulted THEN RETURN ELSE Error["missing INT", cifFile.s];
ENDCASE => Error["wrong INT value", cifFile.s];
};
GetIntAfterSep:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [i:
INT ← defaultInt] ~ {
sInteger = {sep} ["-"] integerD.
integerD = digit {digit}.
token: ROPE;
tokenKind: TokenKind ← GetTokenKind[cifFile.lastToken];
IF tokenKind=semicolon THEN IF mayBeDefaulted THEN RETURN ELSE Error["missing INT", cifFile.s];
token ← GetToken[cifFile];
tokenKind ← GetTokenKind[token];
SELECT tokenKind
FROM
digit => i ← Convert.IntFromRope[token];
upperChar => i ← GetInt[cifFile, mayBeDefaulted];
semicolon => IF mayBeDefaulted THEN RETURN ELSE Error["missing INT", cifFile.s];
ENDCASE => Error["wrong INT value", cifFile.s];
};
GetCard:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [c:
CARD ← defaultCard] ~ {
integer = {sep} integerD.
integerD = digit {digit}.
token: ROPE;
tokenKind: TokenKind ← GetTokenKind[cifFile.lastToken];
IF tokenKind=semicolon THEN IF mayBeDefaulted THEN RETURN ELSE Error["missing CARD", cifFile.s];
token ← GetToken[cifFile];
tokenKind ← GetTokenKind[token];
SELECT tokenKind
FROM
digit => c ← Convert.CardFromRope[token];
upperChar => c ← Convert.CardFromRope[GetToken[cifFile]];
semicolon => IF mayBeDefaulted THEN RETURN ELSE Error["missing CARD", cifFile.s];
ENDCASE => Error["wrong CARD value", cifFile.s];
};
GetCardAfterSep:
PROC [cifFile: Stream, mayBeDefaulted:
BOOLEAN ←
FALSE]
RETURNS [c:
CARD ← defaultCard] ~ {
integer = {sep} integerD.
integerD = digit {digit}.
token: ROPE;
tokenKind: TokenKind ← GetTokenKind[cifFile.lastToken];
IF tokenKind=semicolon THEN IF mayBeDefaulted THEN RETURN ELSE Error["missing CARD", cifFile.s];
token ← GetToken[cifFile];
tokenKind ← GetTokenKind[token];
SELECT tokenKind
FROM
digit => c ← Convert.CardFromRope[token];
upperChar => c ← GetCard[cifFile, mayBeDefaulted];
semicolon => IF mayBeDefaulted THEN RETURN ELSE Error["missing CARD", cifFile.s];
ENDCASE => Error["wrong CARD value", cifFile.s];
};
GetShortName:
PROC [cifFile: Stream]
RETURNS [shortname:
ROPE] ~ {
shortname = c[c][c][c].
c = digit | upperChar
shortname ← GetToken[cifFile];
IF Rope.Length[shortname]>4 THEN Error[Rope.Cat[shortname, "is a too long name"], cifFile.s];
};
GetUserText:
PROC [cifFile: Stream]
RETURNS [userText:
ROPE] ~ {
userText = {userChar}.
userChar = any ASCII char except ";".
userText ← IO.GetTokenRope[cifFile.s, UserTextBreakProc].token;
cifFile.lastToken ← userText;
};
GetCommentText:
PROC [cifFile: Stream]
RETURNS [userText:
ROPE ← NIL] ~ {
commentText = {commentChar} | commentText "(" commentText ")" commentText.
commentChar = any ASCII char except "(", or ")".
token: ROPE;
tokenKind: TokenKind ← blank;
level: NAT ← 1;
token ← IO.GetTokenRope[cifFile.s, CommentTextBreakProc].token;
tokenKind ← GetTokenKind[token];
UNTIL level=0
AND tokenKind=semicolon
DO
userText ← Rope.Concat[userText, token];
IF tokenKind=leftPar THEN level ← level+1;
IF tokenKind=rightPar THEN level ← level-1;
token ← IO.GetTokenRope[cifFile.s, CommentTextBreakProc].token;
tokenKind ← GetTokenKind[token];
ENDLOOP;
cifFile.lastToken ← token;
};
Transformation Utilities
IsOrthogonalTransform:
PUBLIC PROC [m: TMatrix]
RETURNS [
BOOL] = {
checks transformation to make sure any rotation is a multiple of 90 deg
RETURN[(m.a11 = 0 AND m.a22 = 0) OR (m.a21 = 0 AND m.a12 = 0)];
};
MatMult:
PUBLIC PROC [m1, m2: TMatrix]
RETURNS [mProd: TMatrix] ~ {
mProd.a11 ← m1.a11*m2.a11 +m1.a12*m2.a21;
mProd.a21 ← m1.a21*m2.a11 +m1.a22*m2.a21;
mProd.a31 ← m1.a31*m2.a11 +m1.a32*m2.a21 +m1.a33*m2.a31;
mProd.a12 ← m1.a11*m2.a12 +m1.a12*m2.a22;
mProd.a22 ← m1.a21*m2.a12 +m1.a22*m2.a22;
mProd.a32 ← m1.a31*m2.a12 +m1.a32*m2.a22 +m1.a33*m2.a32;
a31
a32
mProd.a33 ← m1.a33*m2.a33;
};
TransformPt:
PUBLIC PROC [m: TMatrix, p: Point]
RETURNS [newP: Point] ~ {
newP.x ← Real.Round[m.a11*p.x+m.a21*p.y+m.a31];
newP.y ← Real.Round[m.a21*p.x+m.a22*p.y+m.a32];
};
TransfToMatrix:
PUBLIC PROC [t: Transformation]
RETURNS [m: TMatrix] ~ {
m ← GetTranslation[[0, 0]];
UNTIL t=
NIL
DO
SELECT t.first.t
FROM
translation => IF t.first.p#[0, 0] THEN m ← MatMult[m, GetTranslation[t.first.p]];
rotation => IF t.first.p#[0, 0] THEN m ← MatMult[m, GetRotation[t.first.p]];
xSym => m ← MatMult[m, GetMirrorX[]];
ySym => m ← MatMult[m, GetMirrorY[]];
ENDCASE => ERROR;
t ← t.rest;
ENDLOOP;
};
GetTranslation:
PROC [p: Point]
RETURNS [m: TMatrix] ~ {
m.a11 ← 1.0; m.a12 ← 0.0;
m.a21 ← 0.0; m.a22 ← 1.0;
m.a31 ← p.x; m.a32 ← p.y; m.a33 ← 1.0;
};
GetMirrorX:
PROC
RETURNS [m: TMatrix] ~ {
m.a11 ← -1.0; m.a12 ← 0.0;
m.a21 ← 0.0; m.a22 ← 1.0;
m.a31 ← 0.0; m.a32 ← 0.0; m.a33 ← 1.0;
};
GetMirrorY:
PROC
RETURNS [m: TMatrix] ~ {
m.a11 ← 1.0; m.a12 ← 0.0;
m.a21 ← 0.0; m.a22 ← -1.0;
m.a31 ← 0.0; m.a32 ← 0.0; m.a33 ← 1.0;
};
GetRotation:
PROC [p: Point]
RETURNS [m: TMatrix] ~ {
c: REAL ← Real.SqRt[p.x*p.x+p.y*p.y];
m.a11 ← p.x/c; m.a12 ← p.y/c;
m.a21 ← -m.a12; m.a22 ← m.a11;
m.a31 ← 0.0; m.a32 ← 0.0; m.a33 ← 1.0;
};