--File IntPhase1.mesa
--
March 27, 1981 2:17 PM

DIRECTORY


IntDefs: FROM "IntDefs",

AuxIntDefs: FROM "AuxIntDefs",
-- exports

IntUtilityDefs: FROM "IntUtilityDefs",

ParserErrorDefs: FROM "ParserErrorDefs" USING [ErrorType, Report],

IntStorageDefs: FROM "IntStorageDefs" USING
[InitStorage, FinishStorage, StoreObject, FetchObject, ReplaceObject,
BBoxRecord, NilObjectName, Object, ObjectName, ObjectRecord],

IntPhase2Defs: FROM "IntPhase2Defs" USING [InitPhase2, FinishPhase2, BareItem],

IntSetsDefs: FROM "IntSetsDefs" USING [PrintSet, InsertSet, RemoveFromSet],

IntTransDefs: FROM "IntTransDefs" USING
[InitTransformation, FinishTransformation, TransformRecord, Push, Pop,
GetLocal, Mirror, Translate, Rotate, ApplyLocal, TransformPoint],

StringDefs: FROM "StringDefs" USING [AppendString, AppendLongNumber],

OutputDefs: FROM "OutputDefs" USING
[BBBox, BBWire, BBPolygon, BBFlash, BBUserObject, SendMessage, OutputUserCommand, Map],

ParserTypeDefs: FROM "ParserTypeDefs" USING
[Point, Path, PathLength, AllocatePath, FreePath, CopyPath,
TList, RemoveTList, TEntry, TListLength, FreeUserNode];

IntPhase1: PROGRAM IMPORTS OutputDefs, ParserTypeDefs, IntUtilityDefs, ParserErrorDefs, StringDefs, IntStorageDefs, IntTransDefs, IntSetsDefs, IntPhase2Defs
EXPORTS IntDefs, AuxIntDefs, IntUtilityDefs =

BEGIN OPEN IntUtilityDefs, IntStorageDefs, IntSetsDefs;

SemanticError: PUBLIC ERROR = CODE;

-- should modify to catch IntTransDefs errors

Layer: TYPE = RECORD [length,num: CARDINAL, body: PACKED ARRAY [0..4) OF CHARACTER];
curLayer,oldLayer: Layer;
curSymbol: ObjectName;-- obj name of symbol currently being defined
curSymbolNumber: LONG CARDINAL;-- number of symbol currently being defined
defInProg: BOOLEAN;-- definition in progress
openFlag: BOOLEAN;-- a symbol is open for appending UserObjects
endSeen: BOOLEAN;
lastObj: ObjectName;-- last object in symbol guts
defSet: ObjectName;-- set of defined symbols
openMess: STRING = "Can’t Process CIF Commands While in Open Mode";

InitInterpreter: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
defInProg ← FALSE;
openFlag ← FALSE;
curLayer.length ← 0;
curLayer.num ← LAST[CARDINAL];
endSeen ← FALSE;
defSet ← NilObjectName;
IF ~IntStorageDefs.InitStorage[] OR
~IntTransDefs.InitTransformation[] OR
~InitUtilities[] OR
~IntPhase2Defs.InitPhase2[] THEN
RETURN[FALSE]
ELSE
RETURN[TRUE];
END;

FinishInterpreter: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF ~IntStorageDefs.FinishStorage[] OR
~IntTransDefs.FinishTransformation[] OR
~FinishUtilities[] OR
~IntPhase2Defs.FinishPhase2[] THEN
RETURN[FALSE]
ELSE
RETURN[TRUE];
END;

DefinedSet: PUBLIC PROCEDURE RETURNS [ObjectName] = {RETURN [defSet];};
IDefineStart: PUBLIC PROCEDURE [symbolNumber: LONG CARDINAL, multiplier, divisor: LONG CARDINAL] =
BEGIN OPEN StringDefs;
obj: ObjectName;
objRec: STEntry ObjectRecord;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END
ELSE defInProg ← TRUE;
oldLayer ← curLayer;
curLayer.length ← 0;
SetScale[multiplier,divisor];-- SetScale does range checking

curSymbolNumber ← symbolNumber;
curSymbol ← lastObj ← obj ← LookUp[symbolNumber];
FetchObject[obj,@objRec];

IF objRec.defined THEN
-- redefining this symbol
BEGINOPEN ParserErrorDefs, OutputDefs;
mess: STRING ← [300];
AppendString[mess,"Redefining Symbol "L];
AppendLongNumber[mess,symbolNumber,10];
Report[mess,Advisory];
mess.length ← 0;
PrintSet[objRec.cby,mess];
IF mess.length > 0 THEN
BEGIN
SendMessage[Other,"Is called in symbols: "L,FALSE];
SendMessage[Other,mess];
END;
defSet ← RemoveFromSet[defSet,symbolNumber];
IF objRec.bound THEN
lastObj ← curSymbol ← RedefineSymbol[symbolNumber]-- copy, invalidate ancestors
ELSE
BEGIN-- re-use this STEntry
-- should also remove this symbol from son’s cby sets
FreeGuts[objRec.guts];-- release storage of internals
objRec.guts ← NilObjectName;
objRec.defined ← FALSE;
objRec.bbValid ← FALSE;
ReplaceObject[@objRec,obj];
END;
RETURN;
END
ELSE
RETURN;

END;

IDefineEnd: PUBLIC PROCEDURE =
BEGIN
objRec: STEntry ObjectRecord;
defSet ← InsertSet[defSet,curSymbolNumber];
defInProg ← FALSE;
SetScale[1,1];
curLayer ← oldLayer;
FetchObject[curSymbol,@objRec];
objRec.defined ← TRUE;
ReplaceObject[@objRec,curSymbol];
END;

IDeleteDef: PUBLIC PROCEDURE [nSym: LONG CARDINAL] =
BEGIN
defSet ← DeleteSymbol[defSet,nSym];
END;

ILayer: PUBLIC PROCEDURE [layerName: STRING] =
BEGIN
IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
curLayer.length ← layerName.length;
SELECT layerName.length FROM
1 =>
BEGIN
curLayer.body[0] ← layerName[0];
curLayer.body[1] ← ’ ;
curLayer.body[2] ← ’ ;
curLayer.body[3] ← ’ ;
END;
2 =>
BEGIN
curLayer.body[0] ← layerName[0];
curLayer.body[1] ← layerName[1];
curLayer.body[2] ← ’ ;
curLayer.body[3] ← ’ ;
END;
3 =>
BEGIN
curLayer.body[0] ← layerName[0];
curLayer.body[1] ← layerName[1];
curLayer.body[2] ← layerName[2];
curLayer.body[3] ← ’ ;
END;
4 =>
BEGIN
curLayer.body[0] ← layerName[0];
curLayer.body[1] ← layerName[1];
curLayer.body[2] ← layerName[2];
curLayer.body[3] ← layerName[3];
END;
ENDCASE;
curLayer.num ← OutputDefs.Map[curLayer.body];
END;
IWire: PUBLIC PROCEDURE [width: LONG CARDINAL, a: ParserTypeDefs.Path] =
BEGIN OPEN ParserTypeDefs;
obj: ObjectName;
path1,path2: Path;
l,r,b,t: LONG INTEGER;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
IF PathLength[a] = 1 THEN
ParserErrorDefs.Report["Wire with Only One Point in Path", Advisory];
IF width = 0 THEN
BEGIN
ParserErrorDefs.Report["Wire with Null Width Specified, Ignored", Advisory];
RETURN;
END;

CheckLayer[];
path1 ← AllocatePath[];
CopyPath[a,path1];
IF defInProg THEN
BEGIN
tpath: Path ← AllocatePath[];
ScalePath[path1,tpath];
FreePath[path1];
path1 ← tpath;
width ← ScaleLong[width];
END;

path2 ← AllocatePath[];
CopyPath[path1,path2];
[l,r,b,t] ← OutputDefs.BBWire[curLayer.num,width,path2];

obj ← StoreObjectRecord[ObjectRecord[Wire[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num,
width: width,
p: path1]]];

IF defInProg THEN
BEGIN
LinkObject[lastObj,obj];
lastObj ← obj;
END
ELSE IntPhase2Defs.BareItem[obj];

FreePath[path1];
FreePath[path2];
END;

IFlash: PUBLIC PROCEDURE [diameter: LONG CARDINAL, center: ParserTypeDefs.Point] =
BEGIN
obj: ObjectName;
l,r,b,t: LONG INTEGER;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
IF diameter = 0 THEN
BEGIN
ParserErrorDefs.Report["Flash with Null diameter Specified, Ignored", Advisory];
RETURN;
END;
CheckLayer[];
IF defInProg THEN
BEGIN
diameter ← ScaleLong[diameter];
center ← ScalePoint[center];
END;

[l,r,b,t] ← OutputDefs.BBFlash[curLayer.num,diameter,center];

obj ← StoreObjectRecord[ObjectRecord[Flash[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num,
diameter: diameter,
center: center]]];
IF defInProg THEN
BEGIN
LinkObject[lastObj,obj];
lastObj ← obj;
END
ELSE IntPhase2Defs.BareItem[obj];
END;

IPolygon: PUBLIC PROCEDURE [a: ParserTypeDefs.Path] =
BEGIN OPEN ParserTypeDefs;
obj: ObjectName;
path1,path2: Path;
l,r,b,t: LONG INTEGER;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
IF ParserTypeDefs.PathLength[a] < 3 THEN
BEGIN
ParserErrorDefs.Report["Polygon with < 3 Points in Path, Ignored", Advisory];
RETURN;
END;
CheckLayer[];

path1 ← AllocatePath[];
CopyPath[a,path1];
IF defInProg THEN
BEGIN
tpath: Path ← AllocatePath[];
ScalePath[path1,tpath];
FreePath[path1];
path1 ← tpath;
END;

path2 ← AllocatePath[];
CopyPath[path1,path2];
[l,r,b,t] ← OutputDefs.BBPolygon[curLayer.num,path2];

obj ← StoreObjectRecord[ObjectRecord[Polygon[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num,
p: path1]]];

IF defInProg THEN
BEGIN
LinkObject[lastObj,obj];
lastObj ← obj;
END
ELSE IntPhase2Defs.BareItem[obj];

FreePath[path1];
FreePath[path2];
END;

IBox: PUBLIC PROCEDURE [length, width: LONG CARDINAL, center: ParserTypeDefs.Point,
xRotation, yRotation: LONG INTEGER] =
BEGIN
obj: ObjectName;
l,r,b,t: LONG INTEGER;
len,wid: LONG INTEGER;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
IF width = 0 OR length = 0 THEN
BEGIN
ParserErrorDefs.Report["Box with Null Width or Length Specified, Ignored", Advisory];
RETURN;
END;
IF xRotation = 0 AND yRotation = 0 THEN
BEGIN
ParserErrorDefs.Report["0,0 Rotation Defaulted to 1,0", Advisory];
xRotation ← 1;
END;

CheckLayer[];

IF defInProg THEN
BEGIN
length ← ScaleLong[length];
width ← ScaleLong[width];
center ← ScalePoint[center];
END;

[l,r,b,t] ← OutputDefs.BBBox[curLayer.num,length,width,center,xRotation,yRotation];

len ← length; wid ← width;
IF yRotation=0 AND
len=r-l AND wid=t-b AND center.x=(l+r)/2 AND center.y=(b+t)/2 THEN
obj ← StoreObjectRecord[ObjectRecord[MBox[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num]]]
ELSE
obj ← StoreObjectRecord[ObjectRecord[Box[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num,
length: length, width: width,
center: center,
xRot: xRotation, yRot: yRotation]]];

IF defInProg THEN
BEGIN
LinkObject[lastObj,obj];
lastObj ← obj;
END
ELSE IntPhase2Defs.BareItem[obj];
END;
ICallSymbol: PUBLIC PROCEDURE [symbolNumber: LONG CARDINAL, list: ParserTypeDefs.TList] =
BEGIN OPEN ParserTypeDefs, IntTransDefs;
entry: TEntry;
obj: ObjectName;

IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
Push[];
-- build up transformation matrix for this list of transformations
THROUGH [1..TListLength[list]] DO
[,entry] ← RemoveTList[list];
WITH entry SELECT FROM
Mirror => SELECT coords FROM
X => Mirror[x];
Y => Mirror[y];
ENDCASE;
Translate => IF x # 0 OR y # 0 THEN -- don’t do null translations
IF defInProg THEN
Translate[ScaleLongInt[x],ScaleLongInt[y]]
ELSE
Translate[x,y];
Rotate => Rotate[xRot,yRot];
ENDCASE;
ENDLOOP;

obj ← StoreObjectRecord[ObjectRecord[Call[
bb: BBoxRecord[left:0,right:0,bottom:0,top:0],
next: NilObjectName,
symNumber: symbolNumber,
uniqueID: NilObjectName,
t: GetLocal[]]]];

Pop[];
IF defInProg THEN
BEGIN-- make entries in calls/called by sets
lRec: STEntry ObjectRecord;
callee: ObjectName;

LinkObject[lastObj,obj];
lastObj ← obj;
callee ← LookUp[symbolNumber];
FetchObject[callee,@lRec];
lRec.cby ← InsertSet[lRec.cby,curSymbolNumber];
ReplaceObject[@lRec,callee];
END
ELSE IntPhase2Defs.BareItem[obj];

END;

-- bind a symbol (and all of its internals) now
BindSymbol: PUBLIC PROCEDURE [sym: LONG CARDINAL] RETURNS [ObjectName] =
BEGIN
this: ObjectName ← LookUp[sym];
lRec: STEntry ObjectRecord;
temp,next: ObjectName;
tempRec: ObjectRecord;

-- get STEntry for sym
FetchObject[this,@lRec];
IF ~lRec.defined THEN
BEGIN OPEN StringDefs;
s: STRING ← [100];
AppendString[s,"Call to Undefined Symbol: "];
AppendLongNumber[s,lRec.symNumber,10];
LogError[s];
RETURN [NilObjectName];
END;
IF lRec.expanded THEN
BEGIN OPEN StringDefs;
s: STRING ← [100];
AppendString[s,"Symbol Calls Itself: "];
AppendLongNumber[s,lRec.symNumber,10];
LogError[s];
lRec.expanded ← FALSE;-- patch and go on
ReplaceObject[@lRec,this];
RETURN [NilObjectName];
END
ELSE
BEGIN
IF lRec.bound THEN RETURN [this];
lRec.expanded ← TRUE;
ReplaceObject[@lRec,this];
END;

-- now bind up all internals
FOR temp ← lRec.guts, next UNTIL temp = NilObjectName DO
FetchObject[temp,@tempRec];
WITH locRec : tempRec SELECT FROM
Call =>
BEGIN
locRec.uniqueID ← BindSymbol[locRec.symNumber];
ReplaceObject[@locRec,temp];
next ← locRec.next;
END;
Box => next ← locRec.next;
MBox => next ← locRec.next;
Flash => next ← locRec.next;
Polygon => {ParserTypeDefs.FreePath[locRec.p]; next ← locRec.next;};
Wire => {ParserTypeDefs.FreePath[locRec.p]; next ← locRec.next;};
User => {ParserTypeDefs.FreeUserNode[locRec.data]; next ← locRec.next;};
ENDCASE;
ENDLOOP;

lRec.bound ← TRUE;
lRec.expanded ← FALSE;
-- now get the bounding box
FindSymBB[@lRec];
ReplaceObject[@lRec,this];
RETURN [this];
END;
-- find the bounding box for a symbol call, leave results in thing↑
FindCallBB: PUBLIC PROCEDURE [thing: Object] =
BEGIN
WITH symCall: thing↑ SELECT FROM
Call =>
BEGIN OPEN IntTransDefs;
lRec: STEntry ObjectRecord;
x,y: LONG INTEGER;

IF symCall.uniqueID # NilObjectName THEN
FetchObject[symCall.uniqueID,@lRec]
ELSE
BEGIN
symCall.bb ← BBoxRecord[left:1,right:0,bottom:0,top:0];
RETURN;-- ignore symbols that couldn’t be bound
END;

IF ~lRec.bbValid THEN
BEGIN
FindSymBB[@lRec];-- get bounding box for this symbol
ReplaceObject[@lRec,symCall.uniqueID];
END;

IF lRec.bb.left>lRec.bb.right THEN-- null BB
symCall.bb ← BBoxRecord[left:1,right:0,bottom:0,top:0]
ELSE
BEGIN
Push[];
ApplyLocal[@symCall.t];
[x,y] ← TransformPoint[lRec.bb.left,lRec.bb.bottom];
InitMM[x,y];
[x,y] ← TransformPoint[lRec.bb.right,lRec.bb.bottom];
MinMax[x,y];
[x,y] ← TransformPoint[lRec.bb.right,lRec.bb.top];
MinMax[x,y];
[x,y] ← TransformPoint[lRec.bb.left,lRec.bb.top];
MinMax[x,y];
[symCall.bb.left,symCall.bb.right,symCall.bb.bottom,symCall.bb.top] ← Extent[];
DoneMM[];
Pop[];
END;

END;
ENDCASE;
END;

-- find the bounding box for a symbol, leave results in sym↑
FindSymBB: PROCEDURE [sym: POINTER TO STEntry ObjectRecord] =
BEGIN OPEN IntTransDefs;
first: BOOLEAN ← TRUE;-- first point flag
temp,next: ObjectName;
tempRec: ObjectRecord;

IF sym.bbValid THEN RETURN;
IF sym.guts = NilObjectName THEN
BEGIN OPEN StringDefs;
foo: STRING ← [100];
AppendString[foo,"Null BB Assumed for Empty Symbol: "];
AppendLongNumber[foo,sym.symNumber,10];
ParserErrorDefs.Report[foo, Advisory];
sym.bb ← BBoxRecord[left:1,right:0,bottom:0,top:0];
sym.bbValid ← TRUE;
RETURN;
END;

FOR temp ← sym.guts, next UNTIL temp = NilObjectName DO
FetchObject[temp,@tempRec];

WITH locRec : tempRec SELECT FROM
Call =>
IF locRec.uniqueID # NilObjectName THEN
BEGIN
FindCallBB[@locRec];
IF locRec.bb.left<=locRec.bb.right THEN-- ignore null BBs
BEGIN
IF first THEN
{first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top];}
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
END;
-- save the result
ReplaceObject[@locRec,temp];
next ← locRec.next;
END;-- otherwise ignore
Box =>
BEGIN
IF first THEN
BEGIN first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top]; END
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
next ← locRec.next;
END;
MBox =>
BEGIN
IF first THEN
BEGIN first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top]; END
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
next ← locRec.next;
END;
Flash =>
BEGIN
IF first THEN
BEGIN first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top]; END
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
next ← locRec.next;
END;
Polygon =>
BEGIN OPEN ParserTypeDefs;
IF first THEN
BEGIN first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top]; END
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
ParserTypeDefs.FreePath[locRec.p];
next ← locRec.next;
END;
Wire =>
BEGIN OPEN ParserTypeDefs;
IF first THEN
BEGIN first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top]; END
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
ParserTypeDefs.FreePath[locRec.p];
next ← locRec.next;
END;
User =>
BEGIN
IF locRec.bb.left<=locRec.bb.right THEN-- ignore null BBs
BEGIN
IF first THEN
{first ← FALSE; InitMM[locRec.bb.right,locRec.bb.top];}
ELSE MinMax[locRec.bb.right,locRec.bb.top];
MinMax[locRec.bb.left,locRec.bb.bottom];
END;
ParserTypeDefs.FreeUserNode[locRec.data];
next ← locRec.next;
END;
ENDCASE;
ENDLOOP;
IF first THEN-- no error free contents
{sym.bb.left ← 1; sym.bb.right ← sym.bb.bottom ← sym.bb.top ← 0;}
ELSE
BEGIN
[sym.bb.left,sym.bb.right,sym.bb.bottom,sym.bb.top] ← Extent[];
DoneMM[];
END;
sym.bbValid ← TRUE;
END;

IComment: PUBLIC PROCEDURE [contents: STRING] =
BEGIN
NULL;
END;

IUserCommand: PUBLIC PROCEDURE[command: [0..9], userText: STRING] =
BEGIN
IF openFlag THEN
BEGIN OPEN ParserErrorDefs;
Report[openMess, FatalInternal];
RETURN;
END;
OutputDefs.OutputUserCommand[command,userText];
END;

IEnd: PUBLIC PROCEDURE =
BEGIN
endSeen ← TRUE;
END;

-- check for a valid layer
CheckLayer: PROCEDURE = INLINE
BEGIN
IF curLayer.length = 0 THEN LogError["No Layer Specified"];
END;

StoreObjectRecord: PROCEDURE [object: ObjectRecord] RETURNS[ObjectName] = INLINE
BEGIN
RETURN[StoreObject[@object]];
END;

IOpenSymbol: PUBLIC PROCEDURE [sym: ObjectName] =
BEGIN
temp,nextTemp: ObjectName;
tempRec: ObjectRecord;
symRec: STEntry ObjectRecord;

IF defInProg THEN
BEGIN OPEN ParserErrorDefs;
Report["Can’t Enter Open Mode While DS in Progress", FatalInternal];
RETURN;
END
ELSE openFlag ← TRUE;

FetchObject[sym,@symRec];
IF symRec.guts = NilObjectName THEN lastObj ← sym
ELSE
FOR temp ← symRec.guts, nextTemp UNTIL temp = NilObjectName DO
lastObj ← temp;
FetchObject[temp,@tempRec];-- get next object in symbol def
WITH foo:tempRec SELECT FROM
Call =>nextTemp ← foo.next;
Wire =>{nextTemp ← foo.next; ParserTypeDefs.FreePath[foo.p];};
Flash =>nextTemp ← foo.next;
Polygon =>{nextTemp ← foo.next; ParserTypeDefs.FreePath[foo.p];};
Box =>nextTemp ← foo.next;
MBox =>nextTemp ← foo.next;
User =>{nextTemp ← foo.next; ParserTypeDefs.FreeUserNode[foo.data];};
ENDCASE;
ENDLOOP;

END;

ICloseSymbol: PUBLIC PROCEDURE = { openFlag ← FALSE; RETURN;};

ISymBB: PUBLIC PROCEDURE [sym: ObjectName] RETURNS [l,r,b,t: LONG INTEGER] =
BEGIN
symRec: STEntry ObjectRecord;
FetchObject[sym,@symRec];
RETURN[
symRec.bb.left,
symRec.bb.right,
symRec.bb.bottom,
symRec.bb.top];
END;

IUserObject: PUBLIC PROCEDURE [size: CARDINAL, data: POINTER TO UNSPECIFIED] =
BEGIN OPEN ParserTypeDefs;
obj: ObjectName;
l,r,b,t: LONG INTEGER;

[l,r,b,t] ← OutputDefs.BBUserObject[curLayer.num,size,data];

obj ← StoreObjectRecord[ObjectRecord[User[
bb: BBoxRecord[left:l,right:r,bottom:b,top:t],
next: NilObjectName,
layer: curLayer.num,
size: size,
data: data]]];

IF defInProg OR openFlag THEN
BEGIN
LinkObject[lastObj,obj];
lastObj ← obj;
END
ELSE IntPhase2Defs.BareItem[obj];
END;

-- scale a long cardinal by factors set up by a call to SetScale
IScaleLong: PUBLIC PROCEDURE [n: LONG CARDINAL] RETURNS [LONG CARDINAL] =
BEGIN
RETURN[ScaleLong[n]];
END;

-- scale a long integer by factors set up by a call to SetScale
IScaleLongInt: PUBLIC PROCEDURE [n: LONG INTEGER] RETURNS [LONG INTEGER] =
BEGIN
RETURN[ScaleLongInt[n]];
END;

END.