-- File: DisjointCollapse.mesa
-- routines to remove small symbols
-- Written by Martin Newell/Dan Fitzpatrick June 1981
-- Last edited (Alto/Pilot): September 1, 1981 7:57 PM

DIRECTORY

DisjointTypes: FROM "DisjointTypes" USING [Instance, Rectangle, Symbol, Geometry, PropID, PropList],
DisjointAllocDefs: FROM "DisjointAllocDefs" USING [EnumerateSymbols, AllocateGeometry, AllocateInstance, FreeGeometry, FreeInstance, FreeRectangle, FreeMarkedSymbols],
DisjointCollapseDefs: FROM "DisjointCollapseDefs",
DisjointPropDefs: FROM "DisjointPropDefs" USING [AllocPropID, PutProp, GetProp, RemoveProp],
IODefs: FROM "IODefs" USING [WriteLine];

DisjointCollapse: PROGRAM
IMPORTS DisjointAllocDefs, DisjointPropDefs, IODefs
EXPORTS DisjointCollapseDefs =
BEGIN
OPEN DisjointAllocDefs, DisjointPropDefs, DisjointTypes, IODefs;

GarbageCollect: PUBLIC PROCEDURE[symb: Symbol] =
BEGIN

Mark: PROC[symb: Symbol] RETURNS[BOOLEAN] =
BEGIN
PutProp[@symb.prop,markID,1];
RETURN[FALSE];
END; -- Proc Mark

Check: PROC[symb: Symbol] =
-- Remove mark from symb and all subsymbols
BEGIN
n: INTEGER;
FOR in:Instance ← symb.insts,in.next UNTIL in = NIL DO
n ← GetProp[in.symbol.prop,markID];
IF n = 1 THENCheck[in.symbol];-- never seen before
ENDLOOP;
RemoveProp[symb.prop,markID];-- Remove mark from symbol
END; -- Proc Check

Remove: PROC[symb: Symbol] RETURNS[BOOLEAN] =
BEGIN
IF GetProp[symb.prop,markID] # NIL THEN ReleaseSymbol[symb];
RETURN[FALSE];
END; -- Proc Remove

[] ← EnumerateSymbols[Mark];-- Mark all symbol
Check[symb];-- UnMark all sub-symbols of symb
RemoveProp[symb.prop,markID];-- UnMark symb
[] ← EnumerateSymbols[Remove];-- Free crude attached to marked symbols
FreeMarkedSymbols[markID];-- Free all marked symbols
END;

SetCollapseSize: PUBLIC PROCEDURE [n:INTEGER] =
BEGIN
IF n < 0 THEN WriteLine["can’t call SetCollapseSize with negative value"]
ELSE CollapseSize ← n;
END;

Collapse: PUBLIC PROCEDURE[symb: Symbol] =
BEGIN

Mark: PROC[symb: Symbol] RETURNS[BOOLEAN] =
BEGIN
PutProp[@symb.prop,markID,1];
RETURN[FALSE];
END; -- Proc Mark

Remove: PROC[symb: Symbol] RETURNS[BOOLEAN] =
BEGIN
n: INTEGER ← GetProp[symb.prop,markID];
IF n = 2 THEN ReleaseSymbol[symb]-- throw away, too small
ELSE RemoveProp[symb.prop,markID];-- unmark symbol
RETURN[FALSE];
END; -- Proc Remove

[] ← EnumerateSymbols[Mark];
DoCollapse[symb];
PutProp[@symb.prop,markID,3];-- be careful not to throw away top level symbol
-- if top symbol has only one call replace it with the called symbol
IF symb.insts # NIL AND symb.geom = NIL AND symb.insts.next = NIL THEN {
tmp: Symbol ← symb.insts.symbol;
FOR p:Instance ← tmp.insts,p.next UNTIL p = NIL DO
p.xOffset ← p.xOffset + symb.insts.xOffset;
p.yOffset ← p.yOffset + symb.insts.yOffset;
ENDLOOP;
FOR p:Geometry ← tmp.geom,p.next UNTIL p = NIL DO
p.l ← p.l + symb.insts.xOffset;
p.b ← p.b + symb.insts.yOffset;
p.r ← p.r + symb.insts.xOffset;
p.t ← p.t + symb.insts.yOffset;
ENDLOOP;
symb.insts ← tmp.insts;
tmp.insts ← NIL;
symb.geom ← tmp.geom;
tmp.geom ← NIL;
};
[] ← EnumerateSymbols[Remove];
FreeMarkedSymbols[markID];
END;

DoCollapse: PROCEDURE[symb: Symbol] =
BEGIN
n: INTEGER;
next: Instance ← symb.insts;
symb.insts ← NIL;
FOR in:Instance ← next,next UNTIL in = NIL DO
next ← in.next;
n ← GetProp[in.symbol.prop,markID];
IF n = 1 THEN {-- never seen before
DoCollapse[in.symbol];
n ← GetProp[in.symbol.prop,markID];
};
SELECT n FROM
2 => Combine[symb,in];-- throw away, too small
3 => {in.next ← symb.insts; symb.insts ← in};-- retain
ENDCASE => SIGNAL CollapseError["bad mark value in Collapse"];-- bug
ENDLOOP;
-- count instances and geoms
n ← 0;
FOR in:Instance ← symb.insts,in.next UNTIL in = NIL DO
n ← n + 1;
ENDLOOP;
FOR g:Geometry ← symb.geom,g.next UNTIL g = NIL DO
n ← n + 1;
ENDLOOP;
IF n > CollapseSize THEN PutProp[@symb.prop,markID,3]
ELSE PutProp[@symb.prop,markID,2];
END;

Combine: PROCEDURE[symb: Symbol, inst:Instance] =
BEGIN
osymb: Symbol ← inst.symbol;
-- copy instances and geom of osymb onto symb
FOR in:Instance ← osymb.insts,in.next UNTIL in = NIL DO
new: Instance ← AllocateInstance[];
new↑ ← [
next: symb.insts,
symbol: in.symbol,
xOffset: in.xOffset + inst.xOffset,
yOffset: in.yOffset + inst.yOffset
];
symb.insts ← new;
ENDLOOP;
FOR g:Geometry ← osymb.geom,g.next UNTIL g = NIL DO
new: Geometry ← AllocateGeometry[];
new↑ ← [
next: symb.geom,
layer: g.layer,
l: g.l + inst.xOffset,
b: g.b + inst.yOffset,
r: g.r + inst.xOffset,
t: g.t + inst.yOffset
];
symb.geom ← new;
ENDLOOP;
-- now free inst
FreeInstance[inst];
END;

ReleaseSymbol: PROCEDURE[symb: Symbol] =
BEGIN
nexti: Instance;
nextw: Rectangle;
nextg: Geometry;
-- delete instances, windows, and geom of symb
FOR in:Instance ← symb.insts,nexti UNTIL in = NIL DO
nexti ← in.next;
FreeInstance[in];
ENDLOOP;
FOR rec:Rectangle ← symb.windows,nextw UNTIL rec = NIL DO
nextw ← rec.next;
FreeRectangle[rec];
ENDLOOP;
FOR g:Geometry ← symb.geom,nextg UNTIL g = NIL DO
nextg ← g.next;
FreeGeometry[g];
ENDLOOP;
END;

markID: PropID ← AllocPropID[];
CollapseSize: INTEGER ← 1;

CollapseError: SIGNAL[msg: STRING] ← CODE;

END.