PWImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by: Monier, June 24, 1985 4:30:52 pm PDT
Louis Monier August 22, 1985 12:39:11 pm PDT
Bertrand Serlet December 17, 1986 11:35:04 pm PST
DIRECTORY
Ascii, BasicTime,
CD, CDBasics, CDCells, CDCleanUp, CDCommandOps, CDDebug, CDDirectory, CDGenerate, CDInstances, CDIO, CDOps, CDProperties, CDSequencer, CDValue,
GList, HashTable,
IO,
PW,
PWObjects,
Rope, RopeList,
TerminalIO,
ViewerClasses;
PWImpl: CEDAR PROGRAM
IMPORTS Ascii, BasicTime, CD, CDBasics, CDCells, CDCleanUp, CDCommandOps, CDDebug, CDDirectory, CDGenerate, CDIO, CDInstances, CDOps, CDProperties, CDSequencer, CDValue, GList, HashTable, IO, PW, PWObjects, Rope, RopeList, TerminalIO
EXPORTS PW =
BEGIN OPEN PW;
Types and signals
AbutProblem: PUBLIC SIGNAL [what: ROPE, isX: BOOL, ob1, ob2: Object] = CODE;
Utilities
-- Reverse a list of objects
Reverse: PUBLIC PROC [objects: Objects] RETURNS [result: Objects ← NIL] = {
result ← NARROW [GList.Reverse[objects]];
};
Safe subset
-- All flavors of Abut
-- Clean the list and check that they match in size
CleanAndCheck: PROC [objects: Objects, isX: BOOL] RETURNS [newObjectsj: Objects ← NIL] =
BEGIN
size, prSize: INT ← -1;
prObj, obj: Object;
IF objects=NIL THEN RETURN[NIL]; -- the list was NIL
-- Clean the list and compare the sizes
WHILE objects#NIL DO
obj ← objects.first;
IF obj=NIL THEN {objects ← objects.rest; LOOP}; -- skip NIL objects
size ← IF isX THEN CD.InterestSize[obj].y ELSE CD.InterestSize[obj].x; -- obj is garanteed to be non-NIL
newObjectsj ← CONS [obj, newObjectsj];    -- build clean list (reversed)
IF prSize<0 THEN {prSize ← size; prObj ← obj; objects ← objects.rest; LOOP}; -- first time
IF prSize#size THEN AbutProblem["These two objects do not match\n", isX, prObj, obj];
-- just before looping
prSize ← size; prObj ← obj; objects ← objects.rest;
ENDLOOP;
newObjectsj ← Reverse[newObjectsj];
END;
AbutX: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] =
{objects: Objects ← NIL;
RETURN [AbutListX[LIST[t1,t2,t3,t4,t5,t6]]]};
AbutY: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] = {
objects: Objects ← NIL; RETURN [AbutListY[LIST[t1,t2,t3,t4,t5,t6]]];
};
AbutListX: PUBLIC PROC [objects: Objects] RETURNS [obj: Object] = {
objects ← CleanAndCheck[objects, TRUE];
obj ← IF objects=NIL THEN NIL ELSE PWObjects.CreateNewAbutX[objects];
};
AbutListY: PUBLIC PROC [objects: Objects] RETURNS [obj: Object] = {
objects ← CleanAndCheck[objects, FALSE];
obj ← IF objects=NIL THEN NIL ELSE PWObjects.CreateNewAbutY[objects];
};
-- Arrays and other repetitions, using simple Abut. (other flavors of Abut ???)
MapFunctionX: PUBLIC PROC [function: XYFunction, lx: INT ← 0, ux: INT] RETURNS [new: Object] =
BEGIN  
row: Objects ← NIL;
IF lx>=ux THEN RETURN[NIL];
FOR x: INT DECREASING IN [lx .. ux) DO
row ← CONS [function[x, 0], row];
ENDLOOP;
RETURN [AbutListX[row]];
END;
MapFunctionY: PUBLIC PROC [function: XYFunction, ly: INT ← 0, uy: INT] RETURNS [new: Object] =
BEGIN  
row: Objects ← NIL;
IF ly>=uy THEN RETURN[NIL];
FOR y: INT DECREASING IN [ly .. uy) DO
row ← CONS [function[0, y], row];
ENDLOOP;
RETURN [AbutListY[row]];
END;
MapFunction: PUBLIC PROC [function: XYFunction, lx: INT ← 0, ux: INT, ly: INT ← 0, uy: INT] RETURNS [new: Object]=
BEGIN  
rows: Objects ← NIL;
IF lx>=ux OR ly>=uy THEN RETURN[NIL];
FOR y: INT DECREASING IN [ly .. uy) DO
row: Objects ← NIL;
make a row
FOR x: INT DECREASING IN [lx .. ux) DO
row ← CONS [function[x, y], row];
ENDLOOP;
create the object corresponding to the row
rows ← CONS [AbutListX[row], rows];
ENDLOOP;
create the tile corresponding to the set of rows
RETURN [AbutListY[rows]];
END;
ArrayX: PUBLIC PROC [ob: Object, nx: INT ← 1] RETURNS [new: Object] =
BEGIN  
row: LIST OF Object ← NIL;
IF nx=0 OR ob=NIL THEN RETURN[NIL];
FOR x: INT IN [0 .. nx) DO
row ← CONS [ob, row];
ENDLOOP;
RETURN [AbutListX[row]];
END;
ArrayY: PUBLIC PROC [ob: Object, ny: INT ← 1] RETURNS [new: Object] =
BEGIN  
row: LIST OF Object ← NIL;
IF ny=0 OR ob=NIL THEN RETURN[NIL];
FOR x: INT IN [0 .. ny) DO
row ← CONS [ob, row];
ENDLOOP;
RETURN [AbutListY[row]];
END;
Array: PUBLIC PROC [ob: Object, nx,ny: INT ← 1] RETURNS [new: Object] =
{new ← ArrayY[ArrayX[ob, nx], ny];};
-- This is a particular case where we want to attach to an object a property logically attached to an application: orientation, instance name, ...
-- For mapping rectangles
MapRectProc: TYPE = PROC [rect: CD.Rect] RETURNS [CD.Rect];
-- Planar tranformations
ChangeOrientation: PUBLIC PROC [obj: CD.Object, orientation: CD.Orientation] RETURNS [cell: CD.Object] =
BEGIN
cell ← CreateEmptyCell[];
[] ← IncludeInCell[cell: cell, obj: obj, orientation: orientation];
RepositionCell[cell];
END;
Flatten: PUBLIC PROC [cell: Object] RETURNS [new: Object] = {
TopEnumerate: CDCells.InstEnumerator = {
ob: Object ← Flatten[inst.ob];
IF NOT CDCells.IsCell[ob] OR CDProperties.GetObjectProp[ob, $DontFlatten]#NIL
THEN instances ← CONS [CDInstances.NewInst[ob, inst.trans, CDProperties.DCopyProps[inst.properties]], instances]
ELSE {
trans: CD.Transformation ← inst.trans;
InsideEnumerate: CDCells.InstEnumerator = {
instances ← CONS [CDInstances.Composed[inst, trans], instances];
};
[] ← CDCells.EnumerateInstances[ob, InsideEnumerate];
};
};
instances: InstanceList ← NIL;
IF CDProperties.GetObjectProp[cell, $DontFlatten]#NIL THEN RETURN [cell];
IF NOT CDCells.IsCell[cell] THEN {
new ← CDDirectory.Expand[cell, NIL, NIL].new;
RETURN [IF new=NIL THEN cell ELSE Flatten[new]];
};
[] ← CDCells.EnumerateInstances[cell, TopEnumerate];
new ← PWObjects.CreateCell[instances: instances, ir: CD.InterestRect[cell], properties: cell.properties];
};
Get: PUBLIC PROC [design: Design, name: ROPE] RETURNS [ob: Object] = {
IF design=NIL THEN ERROR;
ob ← CDDirectory.Fetch[design, name].object;
IF ob=NIL THEN {TerminalIO.PutF["Object %g not found in the design.\n", IO.rope[name]]; ERROR};
};
-- Open a design, given a file name
OpenDesign: PUBLIC PROC [fileName: ROPE] RETURNS [design: CD.Design] =
BEGIN
-- for now, nothing is checked
design ← CDIO.ReadDesign[fileName, NIL, CDIO.GetWorkingDirectory[]];
CDValue.Store[design, $KeepObjects, $KeepObjects]; -- to avoid finalization
END;
-- Registration of UserProc: this creates the entry "Run MyWondeful generator" in the menu
Register: PUBLIC PROC [userProc: UserProc, name: ROPE] = {
TerminalIO.PutF["Generator program %g %g.\n", IO.rope[name], IO.rope[
IF CDGenerate.Register[context: CDGenerate.AssertContext["PatchWork"], key: name, generator: userProc, cache: FALSE] THEN "recorded" ELSE "overwritten"]];
};
Shared abuts
Novice users, please skip this section
sharedAbutXCache: HashTable.Table; -- Object -> HashTable(Object -> Object)
sharedAbutYCache: HashTable.Table; -- Object -> HashTable(Object -> Object)
SharedAbutX: PUBLIC PROC [t1, t2: Object ← NIL] RETURNS [obj: Object] = {
subTable: HashTable.Table ← NARROW [HashTable.Fetch[sharedAbutXCache, t1].value];
IF subTable=NIL THEN {subTable ← HashTable.Create[2]; [] ← HashTable.Store[sharedAbutXCache, t1, subTable]};
obj ← NARROW [HashTable.Fetch[subTable, t2].value];
IF obj#NIL THEN RETURN;
obj ← AbutX[t1, t2];
[] ← HashTable.Store[subTable, t2, obj];
};
SharedAbutY: PUBLIC PROC [t1, t2: Object ← NIL] RETURNS [obj: Object] = {
subTable: HashTable.Table ← NARROW [HashTable.Fetch[sharedAbutYCache, t1].value];
IF subTable=NIL THEN {subTable ← HashTable.Create[2]; [] ← HashTable.Store[sharedAbutYCache, t1, subTable]};
obj ← NARROW [HashTable.Fetch[subTable, t2].value];
IF obj#NIL THEN RETURN;
obj ← AbutY[t1, t2];
[] ← HashTable.Store[subTable, t2, obj];
};
SharedAbutListX: PUBLIC PROC [objects: Objects] RETURNS [obj: Object] = {
revList: Objects ← NIL;
IF objects=NIL THEN RETURN [NIL];
IF objects.rest=NIL THEN RETURN [objects.first];
WHILE objects#NIL AND objects.rest#NIL DO
revList ← CONS [SharedAbutX[objects.first, objects.rest.first], revList];
objects ← objects.rest.rest;
ENDLOOP;
IF objects#NIL THEN revList ← CONS [objects.first, revList];
obj ← SharedAbutListX[Reverse[revList]];
};
SharedAbutListY: PUBLIC PROC [objects: Objects] RETURNS [obj: Object] = {
revList: Objects ← NIL;
IF objects=NIL THEN RETURN [NIL];
IF objects.rest=NIL THEN RETURN [objects.first];
WHILE objects#NIL AND objects.rest#NIL DO
revList ← CONS [SharedAbutY[objects.first, objects.rest.first], revList];
objects ← objects.rest.rest;
ENDLOOP;
IF objects#NIL THEN revList ← CONS [objects.first, revList];
obj ← SharedAbutListY[Reverse[revList]];
};
FlushSharedCache: PUBLIC PROC = {
sharedAbutXCache ← HashTable.Create[2];
sharedAbutYCache ← HashTable.Create[2];
};
-- Adds the properties of obj which are copiable to the ones of newObj
AppendProps: PUBLIC PROC [newObj, obj: CD.Object] = {
FOR l: CDProperties.PropList ← obj.properties, l.rest WHILE l#NIL DO
IF ISTYPE[l.first.key, ATOM] AND CDProperties.GetObjectProp[newObj, l.first.key]=NIL THEN {
p: CDProperties.PropertyProcs = CDProperties.FetchProcs[l.first.key];
IF p#NIL AND p.makeCopy#NIL THEN
CDProperties.PutObjectProp[
newObj, l.first.key, p.makeCopy[l.first.key, l.first.val]];
};
ENDLOOP;
};
Tools for helping the designer debug his PW/Core code
Draw: PUBLIC PROC [obj: CD.Object, technologyName: ATOMNIL] RETURNS [design: CD.Design] = {
viewer: ViewerClasses.Viewer;
design ← CDOps.CreateDesign[CD.FetchTechnology[IF technologyName=NIL THEN $cmosB ELSE technologyName]];
CDValue.Store[design, $KeepObjects, $KeepObjects]; -- to avoid finalization
CDOps.IncludeObjectI[design, obj, [0, 0]];
viewer ← CDViewer.CreateViewer[design];
design ← CDDebug.Draw[obj, IF technologyName=NIL THEN $cmosB ELSE technologyName].dummyDesign;
};
Goodies
-- Input and output from the terminal
-- The arithmetic stuff that people always want
PowersOfTwo: ARRAY [0 .. 33] OF INT = [
1B, 2B, 4B, 1B1, 2B1, 4B1,
1B2, 2B2, 4B2, 1B3, 2B3, 4B3,
1B4, 2B4, 4B4, 1B5, 2B5, 4B5,
1B6, 2B6, 4B6, 1B7, 2B7, 4B7,
1B8, 2B8, 4B8, 1B8, 2B8, 4B8,
1B9, 2B9, 4B9, 1B10];
ODD: PUBLIC PROC [i: INT] RETURNS [BOOL] =
{RETURN [(i MOD 2 # 0)]};
EVEN: PUBLIC PROC [i: INT] RETURNS [BOOL] =
{RETURN [(i MOD 2 = 0)]};
Log2: PUBLIC PROC [n: INT] RETURNS [INT] =
{RETURN [IF n<=1 THEN 0 ELSE 1 + Log2[n / 2]]};
TwoToThe: PUBLIC PROC [x: INT] RETURNS [INT] =
{RETURN[PowersOfTwo[x]]};
TwoToTheLog2: PUBLIC PROC [n: INT] RETURNS [INT] =
{RETURN [TwoToThe[Log2[n]]]};
XthBitOfN: PUBLIC PROC [x, n: INT] RETURNS [BOOL] =
Warning: bit 0 is the LOW order bit (lsb)
{RETURN [IF x<0 THEN FALSE ELSE (n/TwoToThe[x]) MOD 2 =1]};
Commands
-- PW heart: this one really make abuts by calling the user proc.
RunGenerator: PROC [comm: CDSequencer.Command] = {
design: CD.Design ← comm.design;
pos: CD.Position ← comm.pos;
context: CDGenerate.Context ← CDGenerate.AssertContext["PatchWork"];
key: Rope.ROPE;
ob: CD.Object;
time: BasicTime.GMT ← BasicTime.Now[]; -- Start the stop watch
min, sec: INT;
TerminalIO.PutF["PatchWork menu selected\n"];
key ← CDGenerate.SelectOneOf[context, "select generate"];
IF Rope.IsEmpty[key] THEN {TerminalIO.PutF["no generator selected\n"]; RETURN};
-- Generate the object
ob ← CDGenerate.FetchNCall[context, design, key ! AbutProblem => {AbutError[design, ob1, ob2, what, isX, pos]; CONTINUE}];
-- Now figure out how long it took to generate this wonderful piece of layout
sec ← BasicTime.Period[time, BasicTime.Now[]];
min ← sec/60; sec ← sec MOD 60;
TerminalIO.PutF["PW completed in "];
IF min#0 THEN TerminalIO.PutF["%g min ", IO.int[min]]; TerminalIO.PutF["%g sec\n", IO.int[sec]];
IF ob=NIL THEN {TerminalIO.PutF["No returned object\n"]; RETURN};
CDOps.IncludeObjectI[design, ob, pos];
CDCleanUp.CleanUp[design];
};
AbutError: PROC [design: CD.Design, obj1, obj2: CD.Object, what: ROPE, isX: BOOL, where: CD.Position ← [0, 0]] =
BEGIN
translation: CD.Position ← CD.InterestSize[obj1];
IF isX THEN translation.y ← 0 ELSE translation.x ← 0;
-- Scream
TerminalIO.PutF["PW Error while abuting '%g' and object '%g': %g\n", IO.rope[CDDirectory.Name[obj1]], IO.rope[CDDirectory.Name[obj2]], IO.rope[what]];
TerminalIO.PutF["Repaint the viewer to see the objects\n"];
-- Show the objects
CDOps.IncludeObjectI[design, obj1, where];
IF isX THEN translation.y ← 0 ELSE translation.x ← 0;
CDOps.IncludeObjectI[design, obj2, CDBasics.AddPoints[where, translation]];
ERROR;
END;
Directory list
ListMatchingCells: PROC [comm: CDSequencer.Command] = {
EachEntry: CDDirectory.EachEntryAction = {
count ← count+1;
IF NOT Rope.Match[match, name, FALSE] THEN RETURN;
displayed ← displayed+1;
list ← CONS [
WITH ob.specific SELECT FROM cp: CD.CellSpecific => cp.name, ENDCASE => IO.PutFR["%g[%g]", IO.rope[name], IO.rope[CDOps.ObjectRope[ob]]],
list];
};
match: ROPE;
list: LIST OF Rope.ROPENIL; count: INT ← 0; displayed: INT ← 0;
TerminalIO.PutF["List matching objects\n"];
match ← TerminalIO.RequestRope["Pattern : "];
[] ← CDDirectory.Enumerate[comm.design, EachEntry];
list ← RopeList.Sort[list, RopeList.Compare];
FOR l: LIST OF Rope.ROPE ← list, l.rest WHILE l#NIL DO
TerminalIO.PutF["  %g", IO.rope[l.first]];
ENDLOOP;
TerminalIO.PutF["\n %g objects counted %g displayed\n", IO.int[count], IO.int[displayed]];
};
ListIcons: PROC [comm: CDSequencer.Command] = {
EachEntry: CDDirectory.EachEntryAction = {
count ← count+1;
IF NOT Rope.Match["*.icon", name, FALSE] THEN RETURN;
IF NOT Ascii.Letter[Rope.Fetch[name]] AND NOT Ascii.Digit[Rope.Fetch[name]] THEN RETURN;
displayed ← displayed+1;
list ← CONS [
WITH ob.specific SELECT FROM cp: CD.CellSpecific => cp.name, ENDCASE => IO.PutFR["%g[%g]", IO.rope[name], IO.rope[CDOps.ObjectRope[ob]]],
list];
};
list: LIST OF Rope.ROPENIL; count: INT ← 0; displayed: INT ← 0;
TerminalIO.PutF["List icons\n"];
[] ← CDDirectory.Enumerate[comm.design, EachEntry];
list ← RopeList.Sort[list, RopeList.Compare];
FOR l: LIST OF Rope.ROPE ← list, l.rest WHILE l#NIL DO
TerminalIO.PutF["  %g", IO.rope[l.first]];
ENDLOOP;
TerminalIO.PutF["\n %g objects counted %g displayed\n", IO.int[count], IO.int[displayed]];
};
Initialization
CDCommandOps.RegisterWithMenu[$DirectoryMenu, "list matching cells", "patterns OK. case NoN SignIFiCaNt", NIL, ListMatchingCells, dontQueue];
CDCommandOps.RegisterWithMenu[$DirectoryMenu, "list icons", "lists cells of the form *.icon", NIL, ListIcons, dontQueue];
CDSequencer.ImplementCommand[$PatchWork, RunGenerator];
CDCommandOps.RegisterWithMenu[$RectProgramMenu, "PatchWork generator", "proposes a menu of generators", $PatchWork];
TerminalIO.PutF["PatchWork generator recorded.\n"];
[] ← PWObjects.RegisterProp[$DontFlatten, TRUE];
FlushSharedCache[];
END.