TextOpsImpl.mesa
Don Curry May 14, 1987 4:41:05 pm PDT
Last Edited by: Don Curry May 21, 1988 0:55:08 am PDT
DIRECTORY Atom, CD, CDCellCommands, CDCells, CDCellsInteractions, CDCommandOps, CDDirectory, CDImports, CDLayers, CDOps, CDPanel, CDPanelFonts, CDPopUpMenus, CDProperties, CDRects, CDSatellites, CDTexts, CDSequencer, CDSequencerExtras, CDValue, CDViewer, Core, CoreOps, SymTab, IO, PopUpMenus, PopUpSelection, RefTab, RegularExpression, Rope, Sisyph, TerminalIO, TextOps, ViewerClasses, ViewerOps;
TextOpsImpl: CEDAR PROGRAM
IMPORTS Atom, CD, CDCellCommands, CDCells, CDCellsInteractions, CDCommandOps, CDDirectory, CDImports, CDLayers, CDOps, CDPanel, CDPanelFonts, CDProperties, CDRects, CDSatellites, CDSequencer, CDSequencerExtras, CDTexts, CDValue, CDViewer, IO, CDPopUpMenus, CoreOps, PopUpSelection, SymTab, RefTab, RegularExpression, Rope, Sisyph, TerminalIO, ViewerOps
SHARES CDCellCommands, CDPopUpMenus =
BEGIN
Types and Data Structures
TSPType:   TYPE = TextOps.TSPType;
firstInvisible: TSPType = expr;
tspNms:  ARRAY TSPType OF IO.ROPE ← [
text:  "Text",
sat:  "Satellite",
expr:  "Expression", -- firstInvisible
name:  "Name",
iForC:  "Icon Code",
iForS:  "Icon Sch",
extract: "Extract Proc",
virus:  "Prop Virus",
any:  "ANY" ];
tspNms3:  ARRAY TSPType OF IO.ROPE ← [
text:  "text",
sat:  "sat",
expr:  "expr",
name:  "name",
iForC:  "code",
iForS:  "schm",
extract: "extr",
virus:  "virus",
any:  "any" ];
tspProps:  ARRAY TSPType OF ATOM ← [
text:  NIL,
sat:  NIL,
expr:  $SisyphExpressions,
name:  $SignalName,
iForC:  $CodeFor,
iForS:  $IconFor,
extract: $SisyphExtractProc,
virus:  $CDBringoverLibraryName,
any:  NIL];
Finder:  TYPE = RegularExpression.Finder;
InstProc:  TYPE = PROC[inst: CD.Instance]     RETURNS[quit: BOOLFALSE];
InstObProc: TYPE = PROC[inst: CD.Instance, ob: CD.Object] RETURNS[quit: BOOLFALSE];
ObjProc:  TYPE = PROC[obj: CD.Object]      RETURNS[quit: BOOLFALSE];
Signal:  SIGNAL[msg: IO.ROPE] = CODE;
TextOps Main Procs
DoPLAFix: PROC [comm: CDSequencer.Command] ~ {
DoInst: PROC[inst: CD.Instance, ob: CD.Object] RETURNS[quit: BOOLFALSE] = {
refExtrProc: REF ← CDProperties.GetProp[inst, $SisyphExtractProc];
refCodeFor: REF ← CDProperties.GetProp[inst, $CodeFor];
code: IO.ROPE;
IF refExtrProc=NIL THEN RETURN[FALSE];
IF refCodeFor=NIL THEN RETURN[FALSE];
code ← NARROW[refCodeFor];
IF code.Find["PLAGen"]=-1 THEN RETURN[FALSE];
IF code.Find["PLAOutDrHeader"]#-1 THEN RETURN[FALSE];
IF CDProperties.GetProp[inst.ob, $SisyphExtractProc]#NIL THEN ERROR;
IF CDProperties.GetProp[inst.ob, $CodeFor]#NIL THEN ERROR;
CDProperties.PutProp[inst.ob, $SisyphExtractProc, refExtrProc];
CDProperties.PutProp[inst.ob, $CodeFor,   refCodeFor];
CDProperties.PutProp[inst,  $SisyphExtractProc, NIL];
CDProperties.PutProp[inst,  $CodeFor,   NIL];
TerminalIO.PutF["%g\n", IO.rope[NARROW[code]]]};
DoObject: PROC[obj: CD.Object] RETURNS[quit: BOOLFALSE] = { };
top: CD.Object;
WHILE CDCells.IsPushedIn[comm.design] DO
[] ← CDCellsInteractions.PopFromCell[comm.design, interactive] ENDLOOP;
top ← comm.design.actual.first.dummyCell.ob;
[] ← ScanAll[comm.design, top, TRUE, FALSE, DoInst, DoObject];
TerminalIO.PutRope["Done\n"]};
InitTextScanPropPanel: PROC = {
tech:   CD.Technology ← CD.FetchTechnology[$cmosB];
panelHeight: INT ← CDValue.FetchInt[tech, $PanelHeight, global, 120];
CDValue.Store[tech,  $TextScanProp,   NEW[TSPType ← text]];
CDValue.Store[tech,  $TextScanPropNm, tspNms[text]];
CDPanel.Button[button: [text: "search", xpos: 0], command: $TextOpsSearch, tech: tech];
CDPanel.Button[button: [text: "replace", xpos: 50], command: $TextOpsReplace, tech: tech];
CDPanel.Button[button: [text: "prop:", xpos: 105], proc: ChangeTextScanProp, tech: tech];
CDPanel.Label[[width: 140, cdValueKey: $TextScanPropNm, redisplay: TRUE], tech];
CDPanel.Line[tech];
CDValue.StoreInt[tech, $PanelHeight, panelHeight+18];
ReSizePanelViewers[18]};
ReSizePanelViewers: PROC[heightChange: INT] = {
EachViewer: PROC[v: ViewerClasses.Viewer] RETURNS[BOOLTRUE] = {
design: CD.Design ← CDViewer.DesignOf[v];
IF design#NIL THEN IF RefTab.Store[designs, design, design]
THEN ReSizePanelViewer[design, heightChange]};
designs: RefTab.Ref ← RefTab.Create[];
ViewerOps.EnumerateViewers[EachViewer]};
ReSizePanelViewer: PROC[design: CD.Design, heightChange: INT] = {
viewer: ViewerClasses.Viewer ← CDPanel.Create[design];
column: ViewerClasses.Column ← ViewerOps.ViewerColumn[viewer];
ViewerOps.SetOpenHeight[viewer, viewer.wh + heightChange];
ViewerOps.ComputeColumn[column]};
TextOpsMenuCommand: PROC [command: CDSequencer.Command] ~ {
ThreeBools: TYPE = RECORD[doReplace, oneOnly, global: BOOL];
tb: ThreeBools ← SELECT command.key FROM
$FindOneMatchingRopeInDesign   => [FALSE, TRUE,  TRUE],
$FindOneMatchingRopeInContext   => [FALSE, TRUE,  FALSE],
$FindEachMatchingRopeInDesign   => [FALSE, FALSE, TRUE],
$FindEachMatchingRopeInContext  => [FALSE, FALSE, FALSE],
$ReplaceOneMatchingRopeInDesign  => [TRUE, TRUE,  TRUE],
$ReplaceOneMatchingRopeInContext  => [TRUE, TRUE,  FALSE],
$ReplaceEachMatchingRopeInDesign  => [TRUE, FALSE, TRUE],
$ReplaceEachMatchingRopeInContext  => [TRUE, FALSE, FALSE],
ENDCASE          => ERROR;
TextOpsCommand[command, tb.doReplace, tb.oneOnly, tb.global]};
TextOpsCommand: PROC
[command: CDSequencer.Command, doReplace, oneOnly, global: BOOL] ~ {
DoInst: InstObProc = {
IF aborted^ THEN RETURN[TRUE];
[quit, count] ← IF CDTexts.IsText[inst.ob]
THEN ReplaceSat[tsp, design, doReplace, oneOnly, count, inst, ob, finder, replace]
ELSE ReplaceExpr[tsp, design, doReplace, oneOnly, count, inst, finder, replace]};
DoObject: ObjProc = {[quit, count] ←
ReplaceExpr[tsp, design, doReplace, oneOnly, count, obj, finder, replace]};
design:  CD.Design ← command.design;
count:   NAT   ← 0;
tsp:   TSPType ← GetTextScanProp[design]^;
patternRef: IO.ROPE;
pattern:  IO.ROPE;
replace:  IO.ROPE;
finder:  Finder;
searched:  RefTab.Ref ← RefTab.Create[];
oneFound: BOOLFALSE;
aborted:  REF BOOLNEW[BOOLFALSE];
CDSequencer.UseAbortFlag[design, aborted];
IF doReplace THEN replace ← GetText[design];
patternRef ← GetPattern[design];
pattern  ← OnlyStarOrHashMatching[patternRef];
finder ← RegularExpression.CreateFromRope
[pattern: pattern, literal: FALSE, word: FALSE, ignoreCase: FALSE !
RegularExpression.MalformedPattern => {
TerminalIO.PutF["Syntax error in adjusted pattern \"%g\"\n", IO.rope[pattern]];
GOTO prematureExit}];
TerminalIO.PutF["\nMatch Property: %g\n", IO.rope[tspNms[tsp]]];
TerminalIO.PutF["Match Pattern: \"%g\"\n", IO.rope[patternRef]];
IF doReplace THEN
TerminalIO.PutF[" New Pattern: \"%g\"\n", IO.rope[replace]];
TerminalIO.PutF["Match Context: %g\n", IO.rope[IF global THEN "Global" ELSE "Local"]];
oneFound ← ScanContext[design, DoInst, DoObject, global, oneOnly];
TerminalIO.PutF["%g matches.\n", IO.int[count]];
IF oneFound OR doReplace AND count#0 THEN CDSequencer.ExecuteCommand
[key: $ResetScaleTop, design: design, queue: dontQueue, comm: command];
[key: $ResetScaleSel, design: design, queue: dontQueue, comm: command];
EXITS
prematureExit => TerminalIO.PutF["Replace not done.\n"]};
ChangeTextScanProp: PROC [comm: CDSequencer.Command] = {
tspNmList:  LIST OF IO.ROPE;
tspCur:   TSPType;
n:    CARDINAL;
FOR ts: TSPType DECREASING IN TSPType
DO tspNmList ← CONS[tspNms[ts], tspNmList] ENDLOOP;
TerminalIO.PutRope["Change Match Property\n"];
n ← PopUpSelection.Request[header: "Match Property", choice: tspNmList];
tspCur ← IF n=0 THEN GetTextScanProp[comm.design]^ ELSE VAL[n-1];
SetTextScanProp[comm.design, tspCur]};
TextOpsButtonCommand: PROC [comm: CDSequencer.Command] ~ {
doReplace: BOOLSELECT comm.key FROM
$TextOpsSearch => FALSE,
$TextOpsReplace => TRUE,
ENDCASE   => ERROR;
global: BOOL ← comm.b;
oneOnly: BOOL ← comm.n#2;
IF comm.n#1 THEN {
TerminalIO.PutRope["Flush TextOps Cache\n"];
RefTab.Erase[globalCtx];
globalCtx ← RefTab.Create[]};
TextOpsCommand[comm, doReplace, oneOnly, global]};
TextOps Enumeration
ScanContext: PROC
[design: CD.Design, instProc: InstObProc, objProc: ObjProc, allLevels, oneOnly: BOOL]
RETURNS[found: BOOLFALSE] = {
Push: PROC = {
inst: CD.Instance ← CDOps.TheInstance[design: design];
[]�llsInteractions.PushInCellInstance[design, inst]};
pushes: INT;
popped: BOOLFALSE;
top:  CD.Object;
IF allLevels
THENWHILE CDCells.IsPushedIn[design] DO
[] ← CDCellsInteractions.PopFromCell[design, interactive] ENDLOOP
ELSEIF CDCells.IsPushedIn[design] THEN {
popped ← TRUE;
[] ← CDCells.EnumerateInstances[design.actual.rest.first.dummyCell.ob, DeselectAll];
[] ← CDCellsInteractions.PopFromCell[design, interactive];
top ← CDOps.TheInstance[design: design].ob};
IF top=NIL THEN top ← design.actual.first.dummyCell.ob;
pushes ← ScanAll[design, top, allLevels, oneOnly, instProc, objProc];
IF popped THEN Push[];
IF pushes>1 THEN THROUGH [2..pushes] DO Push[] ENDLOOP;
RETURN[pushes>0]};
ScanAll: PROC[design: CD.Design, top: CD.Object, goDeep, oneOnly: BOOL,
instProc: InstObProc, objProc: ObjProc]
RETURNS[pushes: INT ← 0] = {
doInst: InstProc = {
obVal: REF ← RefTab.Fetch[globalCtx, inst.ob].val;
dive:  BOOL ← goDeep AND (oneOnly OR obVal=NIL OR obVal=inst.ob);
IF RefTab.Store[globalCtx, inst, inst]   THEN
IF instProc[inst, top]      THEN GOTO MarkSelected;
IF RefTab.Store[globalCtx, inst.ob, inst.ob] THEN
IF objProc[inst.ob]       THEN GOTO MarkSelected;
IF dive AND CDCells.IsCell[inst.ob]  THEN
IF ((pushes ← ScanAll[design, inst.ob, goDeep, oneOnly, instProc, objProc])>0)
THEN GOTO MarkSelected
ELSE []←RefTab.Store[globalCtx, inst.ob, NEW[INT𡤁]];
EXITS MarkSelected => {
[]�lls.EnumerateInstances[top, DeselectAll];
inst.selected ← TRUE;
RETURN[TRUE]}};
IF CDCells.EnumerateInstances[top, doInst] THEN pushes ← pushes + 1};
globalCtx: RefTab.Ref ← RefTab.Create[];
DeselectAll: InstProc = {inst.selected ← FALSE};
ScanDesign: PROC [design: CD.Design, instProc: InstProc, objProc: ObjProc] = {
CellEnumeratorProc: CDDirectory.EachEntryAction = {RETURN[ScanCell[name, ob]]};
IF CDDirectory.Enumerate[design, CellEnumeratorProc] THEN RETURN;
FOR pushes: LIST OF CD.PushRec ← design^.actual, pushes.rest WHILE pushes#NIL DO
IF ScanCell[NIL, pushes.first.dummyCell.ob] THEN EXIT ENDLOOP};
EnumDirectoryObjectsWithPattern: PROC [comm: CDSequencer.Command] ~ {
proc: PROC [design: CD.Design, ob: CD.Object] ← GetObProc[comm.key];
finder: Finder ← GetFinder[comm.design];
CellEnumeratorProc: CDDirectory.EachEntryAction = {
IF RegularExpression.SearchRope[finder, name].found
THEN proc[comm.design, ob]};
[] ← CDDirectory.Enumerate[comm.design, CellEnumeratorProc]};
EnumSelectedObjects: PROC [comm: CDSequencer.Command] ~ {
proc: PROC [design: CD.Design, ob: CD.Object] ← GetObProc[comm.key];
FOR w: CD.InstanceList ← CDOps.InstList[comm.design], w.rest WHILE w#NIL DO
IF w.first.selected THEN proc[comm.design, w.first.ob] ENDLOOP};
ListUninstantiatedIconSchematics: PROC [comm: CDSequencer.Command] ~ {
schTab: SymTab.Ref ← SymTab.Create[];
visited: RefTab.Ref ← RefTab.Create[];
EachInst: CDCells.InstEnumerator = {
sch: IO.ROPENARROW[CDProperties.GetProp[inst.ob, $IconFor]];
IF NOT CDCells.IsCell[inst.ob] THEN RETURN[FALSE];
IF sch.Length[]>0 THEN [] ← SymTab.Store[schTab, sch, sch];
IF RefTab.Store[visited, inst.ob, inst.ob]
THEN [] ← CDCells.EnumerateInstances[inst.ob, EachInst]};
EachVisited: RefTab.EachPairAction = {
nm: IO.ROPE ← CDDirectory.Name[NARROW[key], comm.design];
IF nm.Length[]>0 THEN [] ← SymTab.Delete[schTab, nm]};
EachUnVisited: SymTab.EachPairAction = {TerminalIO.PutF["%g\n", IO.rope[key]]};
FOR w: CD.InstanceList ← CDOps.InstList[comm.design], w.rest WHILE w#NIL DO
[]�hInst[w.first] ENDLOOP;
[] ← RefTab.Pairs[visited, EachVisited];
[] ← SymTab.Pairs[schTab, EachUnVisited]};
GetObProc: PROC[key: ATOM] RETURNS[proc: PROC [design: CD.Design, ob: CD.Object]] = {
proc ← SELECT key FROM
$AddIconBorderSelected,
$AddIconBorderPattern  => AddIconBorder,
$ChangeColorSelected,
$ChangeColorPattern  => ChangeColor,
ENDCASE      => ERROR};
TextOps Satellite and Expression processing
OnlyStarOrHashMatching: PROC[text: IO.ROPE] RETURNS[IO.ROPE] = {
Insert: PROC[str: IO.ROPE] =
{text ← Rope.Cat[text.Substr[0,index], str, text.Substr[index]]; index ← index+1};
index: INT;
FOR index ← 0, index+1 WHILE index<text.Length[] DO
SELECT text.Fetch[index] FROM
''      => ERROR;
'*      => Insert["#"];
'#      => LOOP;
'[, '., '], '~, '^, '$,
'+, '(, '|, '), '\\,
'<, ':, ',, '>, '{, '} => Insert["'"];
ENDCASE ENDLOOP;
RETURN[text]};
LogFind: PROC[
design:  CD.Design,
type:   IO.ROPE,
text:    IO.ROPE,
doReplace: BOOL,
delete:  BOOL,
replace:  IO.ROPE,
on:   REF,
cell:   CD.Object ] = {
line0:  IO.ROPEIO.PutFR["%g: %g ", IO.rope[type], IO.rope[text]];
online:  IO.ROPE ← "";
inline:  IO.ROPE ← "";
limit:   INT ← 90; -- 120 if small
IF doReplace THEN IF delete
THEN {line0 ← line0.Cat[IO.PutFR["deleted "]]}
ELSE {
TerminalIO.PutF["%gchanged to\n", IO.rope[line0]]; line0 ← NIL;
line0 ← IO.PutFR["  %g ", IO.rope[replace]]};
IF on#NIL THEN IF ISTYPE[on, CD.Object]
THEN online ← IO.PutFR["on obj %g ", IO.rope[GetID[design, NARROW[on]]]]
ELSE {
inst: CD.Instance ← NARROW[on];
iRope: IO.ROPE ← CDOps.InstRope[inst];
IF ~iRope.Equal["rect comment"] THEN {
oRope: IO.ROPE ← CDDirectory.Name[inst.ob, design];
IF oRope.Length[]=0
THEN online ← IO.PutFR["on inst %g ", IO.rope[iRope]]
ELSE online ← IO.PutFR["on inst %g ", IO.rope[oRope]]}};
IF cell#NIL THEN inline ← IO.PutFR["in %g", IO.rope[GetID[design, cell]]];
IF line0.Length[] + online.Length[] + inline.Length[] < limit THEN
{TerminalIO.PutF["%g%g%g\n", IO.rope[line0], IO.rope[online], IO.rope[inline]]; RETURN};
TerminalIO.PutF["%g\n", IO.rope[line0]];
SELECT online.Length[] + inline.Length[] FROM
> limit => TerminalIO.PutF["  %g\n  %g\n", IO.rope[online], IO.rope[inline]];
> 0  => TerminalIO.PutF["  %g%g\n", IO.rope[online], IO.rope[inline]];
ENDCASE};
GetID: PROC[design: CD.Design, obj: CD.Object] RETURNS[name: IO.ROPE] = {
IF obj = CDOps.RealTopCell[design] THEN RETURN["(top level of design)"];
IF obj = design.actual.first.dummyCell.ob THEN RETURN
[Rope.Cat[CDDirectory.Name[design.actual.first.mightReplace.ob, design], " (pushed in)"]];
name ← CDDirectory.Name[obj, design];
IF name.Length[]#0 THEN RETURN[name];
name ← CDOps.ToRope[obj]};
ReplaceRope: PROC[finder: Finder, text, replace: IO.ROPE] RETURNS[new: IO.ROPE] = {
found:     BOOL;
at, atEnd, before, after: INT;
IF finder=NIL THEN RETURN[text];
[found, at, atEnd, before, after] ← RegularExpression.SearchRope[finder, text];
IF NOT found THEN RETURN[text];
IF at=0 AND atEnd=0 AND before=0 AND after=0 THEN RETURN[replace]; -- * replace
new ← Rope.Cat[text.Substr[0,at], replace, text.Substr[atEnd] ]};
ReplaceSat: PROC[tsp: TSPType, design: CD.Design, doReplace, oneOnly: BOOL, count: NAT, inst: CD.Instance, cell: CD.Object, finder: Finder, replace: IO.ROPE]
RETURNS[quit: BOOL, newCount: NAT] = {
delete:  BOOL ← doReplace AND replace.Length[]=0;
textSpec:  CDTexts.TextSpecific ← NARROW[inst.ob.specific];
on:   REF ← CDSatellites.GetMaster[cell, inst];
new:   IO.ROPE ← replace;
SELECT tsp FROM
any => tsp ← IF on#NIL THEN sat ELSE text;
text => IF on#NILTHENRETURN[FALSE, count];
sat  => IF on=NIL  THENRETURN[FALSE, count];
ENDCASE =>      RETURN[FALSE, count];
IF CD.InterestSize[inst.ob].x=0 OR CD.InterestSize[inst.ob].y=0
THEN {Signal["Zero dimension satellite"]; inst.selected ← TRUE; RETURN[TRUE, count]};
IF doReplace
THEN {
newOb: CD.Object;
new ← ReplaceRope[finder, textSpec.text, replace];
IF new=textSpec.text THEN RETURN[FALSE, count];
newOb ← CDTexts.Create[new, textSpec.cdFont];
delete  ← CD.InterestSize[newOb].x=0 OR CD.InterestSize[newOb].y=0;
CDSequencer.MarkChangedIOOnly[design];
IF delete
THEN {inst.selected ← TRUE; oneOnly ← TRUE}
ELSE inst.ob ← newOb}
ELSE IF ~RegularExpression.SearchRope[finder, textSpec.text].found
THEN RETURN[FALSE, count];
LogFind[design, tspNms3[tsp], textSpec.text, doReplace, delete, new, on, cell];
RETURN[oneOnly, count+1]};
ReplaceExpr: PROC[
tsp:   TSPType,
design:  CD.Design,
doReplace: BOOL,
oneOnly:  BOOL,
count:   NAT,
from:   REF,
finder:  Finder,
replace:  IO.ROPE]
RETURNS[quit: BOOL, newCount: NAT] = {
quit ← FALSE; newCount ← count;
IF tsp#any
THEN
[quit, newCount] ← ReplaceExprOnProp
[tsp, design, doReplace, oneOnly, newCount, from, finder, replace]
ELSE FOR tsp IN [firstInvisible..any) WHILE NOT quit DO
[quit, newCount] ← ReplaceExprOnProp
[tsp, design, doReplace, oneOnly, newCount, from, finder, replace] ENDLOOP};
ReplaceExprOnProp: PROC
[tsp: TSPType, design: CD.Design, doReplace, oneOnly: BOOL, count: NAT, from: REF, finder: Finder, replace: IO.ROPE] RETURNS[quit: BOOL, newCount: NAT] = {
propAtom: ATOM ← tspProps[tsp];
new:  LIST OF IO.ROPE ← NIL;
delete: BOOL ← doReplace AND replace.Length[]=0;
changed: BOOL    ← FALSE;
exprs:  LIST OF IO.ROPE ← NIL;
ref:  REF ← CDProperties.GetProp[from, propAtom];
quit  ← FALSE;
WITH ref SELECT FROM
ropes: LIST OF IO.ROPE => {exprs ← ropes};
rope: IO.ROPE   => {exprs ← LIST[rope]};
text: REF TEXT  => {rope: IO.ROPE ← Rope.FromRefText[text]; exprs ← LIST[rope]};
atom: ATOM    => {rope: IO.ROPE ← Atom.GetPName[atom]; exprs ← LIST[rope]};
ENDCASE     => {exprs ← NIL};
FOR exprs ← exprs, exprs.rest WHILE exprs#NIL DO IF NOT doReplace
THEN {
IF ~ RegularExpression.SearchRope[finder, exprs.first].found THEN LOOP;
LogFind[design, tspNms3[tsp], exprs.first, doReplace, delete, NIL, from, NIL];
count ← count +1;
IF oneOnly THEN RETURN[TRUE, count]}
ELSE {
expr: IO.ROPE ← ReplaceRope[finder, exprs.first, replace];
IF (expr= exprs.first) THEN LOOP;
LogFind[design, tspNms3[tsp], exprs.first, doReplace, delete, expr, from, NIL];
count  ← count +1;
changed ← TRUE;
IF NOT delete THEN new ← CONS[expr, new];
IF oneOnly THEN {
FOR temp: LIST OF IO.ROPE ← exprs.rest, temp.rest WHILE temp#NIL DO
new ← CONS[temp.first, new] ENDLOOP;
quit ← TRUE; EXIT} };
ENDLOOP;
IF doReplace AND changed THEN {
one: IO.ROPEIF new=NIL THEN NIL ELSE new.first;
WITH ref SELECT FROM
ropes: LIST OF IO.ROPE => ref ← new;
rope: IO.ROPE   => ref ← one;
text: REF TEXT  => ref ← Rope.ToRefText[one];
atom: ATOM    => ref ← Atom.MakeAtom[one];
ENDCASE     => ERROR;
CDSequencer.MarkChangedIOOnly[design];
CDProperties.PutProp[from, propAtom, ref]};
RETURN[quit, count]};
Add Borders and Color
AddIconBorder: PROC [design: CD.Design, ob: CD.Object] ~ {
name:  IO.ROPE  ← CDDirectory.Name[ob, design];
layer:  CD.Layer  ← CDLayers.CurrentLayer[design];
w:   INT   ← CDLayers.LayerWidth[design, layer];
ir:   CD.Rect  ← CD.InterestRect[ob];
vert:  CD.Object  ← CDRects.CreateRect[size: [w,ir.y2-ir.y1], l: layer];
hor:  CD.Object  ← CDRects.CreateRect[size: [ir.x2-ir.x1,w], l: layer];
inst:  CD.Instance;
inst ← NEW[CD.InstanceRep ← [ob: vert, trans: [off: [ir.x1, ir.y1]] ] ];
CDProperties.PutProp[inst, $SisyphExtractProc, $ExtractNull];
IF CDCells.IncludeInstance[design, ob, inst] THEN Signal["???"];
inst ← NEW[CD.InstanceRep ← [ob: vert, trans: [off: [ir.x2-w, ir.y1]] ] ];
CDProperties.PutProp[inst, $SisyphExtractProc, $ExtractNull];
IF CDCells.IncludeInstance[design, ob, inst] THEN Signal["???"];
inst ← NEW[CD.InstanceRep ← [ob: hor, trans: [off: [ir.x1, ir.y1]] ] ];
CDProperties.PutProp[inst, $SisyphExtractProc, $ExtractNull];
IF CDCells.IncludeInstance[design, ob, inst] THEN Signal["???"];
inst ← NEW[CD.InstanceRep ← [ob: hor, trans: [off: [ir.x1, ir.y2-w]] ] ];
CDProperties.PutProp[inst, $SisyphExtractProc, $ExtractNull];
IF CDCells.IncludeInstance[design, ob, inst] THEN Signal["???"];
TerminalIO.PutF["Borders added in %g\n", IO.rope[name]];
CDOps.Redraw[design]};
ChangeColor: PROC[design: CD.Design, ob: CD.Object] = {
size:  CD.Position ← CD.InterestSize[ob];
off:  CD.Position ← CD.InterestBase[ob];
layer:  CD.Layer  ← CDLayers.CurrentLayer[design];
mask:  CD.Object  ← CDRects.CreateRect[size: size, l: layer];
maskI:  CD.Instance;
name:  IO.ROPE ← CDDirectory.Name[ob, design];
delete: BOOL ← layer = CD.commentLayer;
EachInst: CDCells.InstEnumerator = {
IF NOT CDRects.IsBareRect[inst.ob]  THEN RETURN[FALSE];
IF CD.InterestSize[inst.ob]#size   THEN RETURN[FALSE];
IF CD.InterestBase[inst.ob]#inst.trans.off THEN RETURN[FALSE];
IF CDProperties.GetProp[inst, $SisyphExtractProc]=NIL THEN RETURN[FALSE];
IF NARROW[CDProperties.GetProp[inst, $SisyphExtractProc], ATOM]#$ExtractNull
THEN RETURN[FALSE];
IF delete THEN
{maskI ← inst; TerminalIO.PutF["Color deleted in %g\n", IO.rope[name]]; RETURN[TRUE]};
IF inst.ob.layer=layer THEN
{TerminalIO.PutF["Color unchanged in %g\n", IO.rope[name]]; RETURN[TRUE]};
inst.ob ← mask;
TerminalIO.PutF["Color modified in %g\n", IO.rope[name]]; RETURN[TRUE]};
IF delete
THEN {
IF CDCells.EnumerateInstances[ob, EachInst] THEN
IF CDCells.RemoveInstance[design, ob, maskI] THEN Signal["???"]}
ELSE IF NOT CDCells.EnumerateInstances[ob, EachInst] THEN {
maskI ← NEW[CD.InstanceRep ← [ob: mask, trans: [off: off] ] ];
CDProperties.PutProp[maskI, $SisyphExtractProc, $ExtractNull];
IF CDCells.IncludeInstance[design, ob, maskI] THEN Signal["???"];
TerminalIO.PutF["Color added in %g\n", IO.rope[name]]};
CDOps.Redraw[design]};
Add Expression To Selected
AddExpressionToEachSelected: PROC [command: CDSequencer.Command] ~ {
count: NAT ← 0;
id: IO.ROPE ← SELECT command.key FROM
$AddInstanceExpressionToEachSelected => "Instance",
$AddObjectExpressionToEachSelected  => "Object",
ENDCASE          => ERROR;
expr: IO.ROPE ← TerminalIO.RequestRope[id.Cat[" expression to add to selected: "]];
FOR w: CD.InstanceList ← CDOps.InstList[command.design], w.rest WHILE w#NIL DO
IF w.first.selected THEN {
on: REF ← SELECT command.key FROM
$AddInstanceExpressionToEachSelected => w.first,
$AddObjectExpressionToEachSelected  => w.first.ob,
ENDCASE          => ERROR;
exprs: LIST OF IO.ROPE ← NARROW[CDProperties.GetProp[on, Sisyph.expressionsProp]];
FOR test: LIST OF IO.ROPE ← exprs, test.rest WHILE test#NIL DO
IF expr.Equal[test.first] THEN GOTO exit;
REPEAT exit => LOOP ENDLOOP;
exprs ← CONS[expr, exprs];
CDProperties.PutProp[on, Sisyph.expressionsProp, exprs];
count ← count+1};
ENDLOOP;
TerminalIO.PutF["%g matches.\n", IO.int[count]]};
Add Multiple Text Lines
AddTextLines: PROC [command: CDSequencer.Command] ~ {
pos:  CD.Position ← command.pos;
ob:  CD.Object;
lay:  CD.Layer ← CDLayers.CurrentLayer[command.design];
r:   IO.ROPE;
font:  CDTexts.CDFont ← CDPanelFonts.CurrentFont[command.design];
grid:  INT ← Grid[command.design, font];
CDOps.Redraw[command.design];
lay ← CDPanelFonts.LayerForText[lay, command.design.technology];
IF font=NIL THEN CDSequencer.Quit["** no font"];
pos.y ← pos.y + font.origin.y;
r ← TerminalIO.RequestRope["create text >"];
DO
WHILE ~r.IsEmpty[] AND r.Fetch[] IN [IO.NUL..IO.SP] DO r ← r.Substr[1] ENDLOOP;
WHILE ~r.IsEmpty[] AND r.Fetch[r.Length[]-1] IN [IO.NUL..IO.SP]
DO r ← r.Substr[0, r.Length[]-1] ENDLOOP;
IF r.IsEmpty[] THEN EXIT;
ob ← CDTexts.Create[text: r, font: font, layer: lay];
IF ob=NIL THEN CDSequencer.Quit["***bad line***"];
[]�Ops.IncludeObject[design: command.design, ob: ob, trans: [pos, original]];
r ← TerminalIO.RequestRope[NIL];
pos.y ← pos.y - grid ENDLOOP};
Build Cross Reference Lists
BuildCrossReferenceLists: PROC [comm: CDSequencer.Command] ~ {
InstEnumeratorProc: CDCells.InstEnumerator = {
IF CDCells.IsCell[inst.ob] AND inst.selected THEN
{nofCells ← nofCells+1; objNms ← CONS[CDDirectory.Name[inst.ob, comm.design], objNms]}};
ListEm: SymTab.EachPairAction = {list ← CONS[NARROW[val], list]};
rootCell: CD.Object ← comm.design^.actual.first.dummyCell.ob;
objNms: LIST OF IO.ROPENIL;
top:  Sisyph.Context ← Sisyph.Create[comm.design];
index:  INT ← 0;
nofCells: INT ← 0;
maxNm: INT ← 0;
OccSeq: TYPE = RECORD[name: IO.ROPE, seq: SEQUENCE size: CARDINAL OF BOOL];
list:  LIST OF REF OccSeq;
table:  SymTab.Ref ← SymTab.Create[];
[]�lls.EnumerateInstances[rootCell, InstEnumeratorProc];
FOR m: LIST OF IO.ROPE ← objNms, m.rest WHILE m#NIL DO
AddToTable: PROC[wire: Core.Wire] = {
name: IO.ROPE ← CoreOps.GetShortWireName[wire];
IF name.Length[]>0 THEN {
ref: REF OccSeq ← NARROW[SymTab.Fetch[table, name].val];
IF ref=NIL THEN ref ← NEW[OccSeq[nofCells]];
ref.name ← name;
ref[index] ← TRUE;
maxNm ← MAX[name.Length[], maxNm];
[]←SymTab.Store[table, name, ref]};
FOR i: INT IN [0..wire.size) DO AddToTable[wire[i]] ENDLOOP};
sub:  Core.CellType ← Sisyph.ES[m.first, top];
AddToTable[sub.public];
index ← index +1; ENDLOOP;
[ ] ← SymTab.Pairs[table, ListEm];
DO
ok: BOOLTRUE;
FOR l: LIST OF REF OccSeq ← list, l.rest WHILE l.rest#NIL DO
TwoOcc: TYPE = RECORD[occ1, occ2: REF OccSeq];
SELECT Rope.Compare[l.first.name, l.rest.first.name]
FROM less => LOOP; equal => ERROR; ENDCASE;
[l.first, l.rest.first] ← TwoOcc[l.rest.first, l.first];
ok ← FALSE; ENDLOOP;
IF ok THEN EXIT; ENDLOOP;
TerminalIO.PutF["\nCross reference list for:\n"];
index ← 0;
FOR m: LIST OF IO.ROPE ← objNms, m.rest WHILE m#NIL DO
TerminalIO.PutF["%4g: %g\n", IO.int[index], IO.rope[m.first]];
index ← index +1; ENDLOOP;
FOR m: LIST OF REF OccSeq ← list, m.rest WHILE m#NIL DO
IF comm.key=$BuildCrossReferenceListsUnique THEN {
used: NAT ← 0;
FOR i: INT IN [0..nofCells) DO IF m.first[i] THEN used←used+1 ENDLOOP;
IF used#1 THEN LOOP};
TerminalIO.PutF["%g ", IO.rope[m.first.name]];
FOR i: INT IN [0..(maxNm-m.first.name.Length[])) DO TerminalIO.PutRope[" "] ENDLOOP;
FOR i: INT IN [0..nofCells) DO IF m.first[i]
THEN TerminalIO.PutF["%2g", IO.int[i]]
ELSE TerminalIO.PutF[" "] ENDLOOP;
TerminalIO.PutF["\n"];
ENDLOOP};
Replace Unbound Library
ReplaceUnboundLibrary: PROC[command: CDSequencer.Command] ~ {
DoInst: InstObProc = { };
DoObject: ObjProc = {
imp: CDImports.ImportSpecific;
new: IO.ROPE;
IF  ~CDImports.IsImport[obj] THEN RETURN;
imp ← NARROW[obj.specific];
new ← ReplaceRope[finder, imp.designName, replace];
IF new=imp.designName THEN RETURN;
TerminalIO.PutF["%g\n", IO.rope[imp.objectName]];
IF imp.boundOb#NIL  THEN ERROR; -- RollBackAnd CDRead -X designFile
IF imp.boundDesign#NILTHEN ERROR; -- RollBackAnd CDRead -X designFile
imp.designName ← new;
count ← count+1};
design:  CD.Design ← command.design;
tsp:   TSPType  ← name;
count:   NAT   ← 0;
doReplace: BOOL   ← TRUE;
replace:  IO.ROPE  ← IF doReplace THEN GetText[design] ELSE NIL;
delete:  BOOL   ← doReplace AND replace.Length[]=0;
patternRef: IO.ROPE  ← GetPattern[design];
pattern:  IO.ROPE  ← OnlyStarOrHashMatching[patternRef];
oneFound: BOOL   ← FALSE;
finder:  Finder  ← RegularExpression.CreateFromRope
[pattern: pattern, literal: FALSE, word: FALSE, ignoreCase: FALSE !
RegularExpression.MalformedPattern => {
TerminalIO.PutF["Syntax error in adjusted pattern \"%g\"\n", IO.rope[pattern]];
GOTO prematureExit}];
TerminalIO.PutF["\nReplace Unbound Library\n"];
TerminalIO.PutF[" Old Library: \"%g\"\n", IO.rope[patternRef]];
TerminalIO.PutF[" New Library: \"%g\"\n", IO.rope[replace]];
TerminalIO.TOS[].Flush[];
IF ~TerminalIO.Confirm["Ready to proceed?"] THEN RETURN;
oneFound ← ScanContext[design, DoInst, DoObject, TRUE, FALSE];
TerminalIO.PutF["%g matches.\n", IO.int[count]];
CDSequencer.ExecuteCommand
[key: $ResetScaleTop, design: design, queue: dontQueue, comm: command];
EXITS
prematureExit => TerminalIO.PutF["Replace not done.\n"]};
Panel Utilities
Grid: PROC[design: CD.Design, font: CDTexts.CDFont ← NIL] RETURNS[grid: NAT] = {
viewers: CDViewer.ViewerList ← CDViewer.ViewersOf[design];
IF viewers#NIL
THEN WITH ViewerOps.GetViewer[viewers.first, $Grid] SELECT FROM
rgrid: REF CD.Number => grid ← rgrid^; ENDCASE => NULL
ELSE grid ← design.technology.lambda*2;
IF font#NIL THEN WHILE font.height > (grid*4)/3 DO grid ← grid*2 ENDLOOP};
GetFinder: PROC[design: CD.Design] RETURNS[finder: Finder] = {
pattern: IO.ROPE  ← OnlyStarOrHashMatching[GetPattern[design]];
finder ← RegularExpression.CreateFromRope
[pattern: pattern, literal: FALSE, word: FALSE, ignoreCase: FALSE]};
GetPattern: PROC [design: CD.Design] RETURNS [pattern: Rope.ROPE] = {
pattern ← CDPanel.TakeDownText[design, $pattern];
IF Rope.IsEmpty[pattern] THEN pattern ← NIL};
GetText: PROC [design: CD.Design] RETURNS [text: Rope.ROPE] = {
text ← CDPanel.TakeDownText[design, $text];
IF Rope.IsEmpty[text] THEN text ← NIL};
PutText: PROC [design: CD.Design, text: Rope.ROPE] RETURNS[Rope.ROPE] =
{CDPanel.PutUpText[design, $text, text]; RETURN[text]};
GetTextScanProp: PROC [design: CD.Design] RETURNS[tspRef: REF TSPType] = {
tspRef ← NARROW[CDValue.Fetch[design, $TextScanProp]];
IF tspRef=NIL THEN {
tspRef ← NEW[TSPType ← text];
CDValue.Store[design, $TextScanProp, tspRef]};
RETURN[tspRef]};
SetTextScanProp: PROC [design: CD.Design, tsp: TSPType] = {
tspRef: REF TSPType ← GetTextScanProp[design];
tspRef^ ← tsp;
CDValue.Store[design, $TextScanPropNm, tspNms[tsp]];
[]�Panel.PutUp[design, $TextScanPropNm]};
CycleTextScanProp: PROC[design: CD.Design] = {
tsp:  TSPType ← GetTextScanProp[design]^;
tspLast: TSPType ← LAST[TSPType];
tspn:  CARDINAL ← tsp.ORD;
tspl:  CARDINAL ← tspLast.ORD;
SetTextScanProp[design, VAL[(tspn+1) MOD (tspl+1)]]};
Command/Menu Registration and Panel Init
TextOps
menu: PopUpMenus.Menu ← CDPopUpMenus.MakeMenu[
key:  $TextOps,
header: "TextOps",
doc:  "Text Search and Replace Operations" ];
CDCommandOps.RegisterWithMenu[
menu:   $OtherProgramMenu,
entry:   "TextOps",
doc:  "Text Search and Replace Operations",
key:  $TextOps,
proc:  NIL];
CDCommandOps.RegisterWithMenu[
menu:   $OtherProgramMenu,
entry:   "DoPLAFix",
doc:  "DoPLAFix",
key:  $DoPLAFix,
proc:  DoPLAFix];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Find one matching rope in Design",
doc:  "Find one matching rope in Design",
key:  $FindOneMatchingRopeInDesign,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Find one matching rope in Context",
doc:  "Find one matching rope in Context",
key:  $FindOneMatchingRopeInContext,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Find each matching rope in Design",
doc:  "Find each matching rope in Design",
key:  $FindEachMatchingRopeInDesign,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Find each matching rope in Context",
doc:  "Find each matching rope in Context",
key:  $FindEachMatchingRopeInContext,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Replace one matching rope in Design",
doc:  "Replace one matching rope in Design",
key:  $ReplaceOneMatchingRopeInDesign,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Replace one matching rope in Context",
doc:  "Replace one matching rope in Context",
key:  $ReplaceOneMatchingRopeInContext,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Replace each matching rope in Design",
doc:  "Replace each matching rope in Design",
key:  $ReplaceEachMatchingRopeInDesign,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Replace each matching rope in Context",
doc:  "Replace each matching rope in Context",
key:  $ReplaceEachMatchingRopeInContext,
proc:  TextOpsMenuCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Search",
doc:  "Uses PROP and PATTERN from panel",
key:  $TextOpsSearch,
proc:  TextOpsButtonCommand ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Replace",
doc:  "Uses PROP, PATTERN and TEXT from panel",
key:  $TextOpsReplace,
proc:  TextOpsButtonCommand ];
CDSequencerExtras.RegisterCommand
[key: $TextOpsSearch, proc: TextOpsButtonCommand, queue: doQueue];
CDSequencerExtras.RegisterCommand
[key: $TextOpsReplace, proc: TextOpsButtonCommand, queue: doQueue];
CDValue.RegisterKey[ $TextScanProp,  NIL, $djc];
CDValue.RegisterKey[ $TextScanPropNm, NIL, $djc];
InitTextScanPropPanel[];
List Uninstantiated Icon Schematics
CDCommandOps.RegisterWithMenu[
menu:  $SisyphIconMenu,
entry:  "List Uninstantiated Icon Schematics",
doc:  "Examines the IconFor property.",
key:  $ListUninstantiatedIconSchematics,
proc:  ListUninstantiatedIconSchematics ];
Add Color
CDCommandOps.RegisterWithMenu[
menu:  $SisyphIconMenu,
entry:  "Change Color Pattern",
doc:  "Use panel color and pattern",
key:  $ChangeColorPattern,
proc:  EnumDirectoryObjectsWithPattern ];
CDCommandOps.RegisterWithMenu[
menu:  $SisyphIconMenu,
entry:  "Change Color Selected",
doc:  "Use panel color and selected cells",
key:  $ChangeColorSelected,
proc:  EnumSelectedObjects ];
Add Expression To Selected
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Add Instance Expression To Each Selected",
doc:  "Add Instance Expression To Each Selected",
key:  $AddInstanceExpressionToEachSelected,
proc:  AddExpressionToEachSelected ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Add Object Expression To Each Selected",
doc:  "Add Object Expression To Each Selected",
key:  $AddObjectExpressionToEachSelected,
proc:  AddExpressionToEachSelected ];
Add Multiple Text Lines
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Add mulitple text lines",
doc:  "Terminate with 2 CR's",
key:  $AddTextLines,
proc:  AddTextLines ];
Build Cross Reference Lists
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Build Cross Reference Lists",
doc:  "Build cross reference lists from public of selected",
key:  $BuildCrossReferenceLists,
proc:  BuildCrossReferenceLists ];
CDCommandOps.RegisterWithMenu[
menu:  $TextOps,
entry:  "Build Cross Reference Lists - Unique only",
doc:  "Build cross reference lists from public of selected - Unique only",
key:  $BuildCrossReferenceListsUnique,
proc:  BuildCrossReferenceLists ];
Replace Unbound Library
CDCommandOps.RegisterWithMenu[
menu:  $ImportMenu,
entry:  "Replace Unbound Library",
doc:  "Replace Unbound Library",
key:  $ReplaceUnboundLibrary,
proc:  ReplaceUnboundLibrary ];
END.