SisyphImpl.mesa
Copyright Ó 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Created by Sindhu and Serlet, November 27, 1985 9:11:39 pm PDT
Pradeep Sindhu June 25, 1987 4:59:16 pm PDT
Barth, October 15, 1986 10:25:34 am PDT
Bertrand Serlet, September 16, 1987 4:15:58 pm PDT
Jean-Marc Frailong December 12, 1987 5:41:02 pm PST
Christian Jacobi, July 15, 1986 6:24:40 pm PDT
Don Curry May 7, 1987 10:30:40 pm PDT
DIRECTORY
AMTypes USING [TV, TVEqual, TVType, UnderClass],
AMBridge USING [SomeRefFromTV, TVForFrame, TVForReferent, TVToRef],
AMModel USING [Context],
AMModelBridge USING [ContextForFrame],
CD, CDBasics, CDCells, CDDirectory, CDImports, CDLayers, CDProperties, CDRects, CDSatellites, CDSequencer, CDSymbolicObjects, CDTexts,
Convert,
Core, CoreClasses, CoreOps, CoreProperties,
CoreGeometry,
IO,
Interpreter USING [Evaluate],
PrincOpsUtils,
PW, RefTab, Rope, RopeList,
Sinix,
Sisyph,
SymTab, TerminalIO;
SisyphImpl: CEDAR PROGRAM    
IMPORTS AMBridge, AMModelBridge, AMTypes, CDBasics, CDCells, CDDirectory, CDImports, CDLayers, CDProperties, CDRects, CDSatellites, CDSequencer, CDTexts, Convert, CoreClasses, CoreOps, CoreProperties, CoreGeometry, Interpreter, IO, PrincOpsUtils, PW, RefTab, Rope, RopeList, Sinix, SymTab, TerminalIO
EXPORTS Sisyph
SHARES CDCells, CDImports, CDRects, CDTexts, Sinix =
BEGIN OPEN Sisyph;
Constants and Variables
expressionsProp: PUBLIC ATOMPW.RegisterProp[$SisyphExpressions, TRUE];
designRope: PRIVATE ROPE ← "design";
name of Sisyph variable that holds the ChipNDale design.
globalNamesRope: PRIVATE ROPE ← "globalNames";
name of Sisyph variable that represents a list of names of global wires.
corePropsRope: PUBLIC ROPE ← "coreProps";
coreInstPropsRope: PUBLIC ROPE ← "coreInstProps";
cdObjRope: PRIVATE ROPE ← "cdObj";
name of Sisyph variable that holds the CD object being extracted.
localVariablesRopes: ROPES = LIST ["&", "&&", corePropsRope, coreInstPropsRope, cdObjRope, "cI", "wI", "wire"];
parmNamesProp: PUBLIC ATOMPW.RegisterProp[$SisyphParmNames, TRUE];
defaultGlobalNames: PUBLIC ROPESLIST ["Vdd", "Gnd", "RosemaryLogicTime"];
mode: PUBLIC Sinix.Mode ← NEW [Sinix.ModeRec ← [
extractProcProp: PW.RegisterProp[$SisyphExtractProc, TRUE],
decoration: CoreGeometry.CreateDecoration["Sisyph"],
instanceEqualProc: InstanceEqual,
objectEqualProc: ObjectEqual,
instanceLayer: Sinix.DefaultInstanceLayer,
userData: DefaultUserData,
fusionByName: schematics,
touchProc: Touch,
nameProc: Name
]];
ExtractProcs
ResultFor: PROC [cx: Context, obj: CD.Object, properties: CD.PropList] RETURNS [result: REFNIL] = {
codeFor: ROPENARROW [CDProperties.GetListProp[properties, $CodeFor]];
iconFor: ROPENARROW [CDProperties.GetListProp[properties, $IconFor]];
IF codeFor=NIL THEN codeFor ← NARROW [CDProperties.GetObjectProp[obj, $CodeFor]];
IF iconFor=NIL THEN iconFor ← NARROW [CDProperties.GetObjectProp[obj, $IconFor]];
SELECT TRUE FROM
codeFor#NIL AND iconFor#NIL => ERROR; -- that's one two many!
codeFor#NIL       => {
result ← EvalToRef[cx, codeFor];
IF result#NIL THEN WITH result SELECT FROM
ct: CellType => {};
w: Wire  => {}; 
w: Wires  => {};
ENDCASE     => ERROR;
};
iconFor#NIL       => result ← ES[iconFor, cx];
ENDCASE        => ERROR; -- one of them at least should be present!
};
ExtractCellIcon: PUBLIC Sinix.ExtractProc = {
cx: Context ← EvaluateParameters[userData, obj, properties];
iconCT: CellType ← NARROW [Sinix.ExtractCell[obj, mode, NIL, cx].result];
iconName: ROPE ← Name[obj, cx];
cellType: CellType;
name: ROPE;
result ← ResultFor[cx, obj, properties];
IF result=NIL THEN RETURN;
cellType ← NARROW [result];
name ← CoreOps.GetCellTypeName[cellType];
IF name=NIL THEN name ← CDNameToCTName[iconName, ".icon"];
cellType ← Sinix.CreateIcon[cellType: cellType, name: name];
result ← cellType;
CoreGeometry.PutObject[mode.decoration, cellType, obj];
Check public
IF NOT CheckAndDecorate[iconName, obj, iconCT.public, cellType.public, GetGlobalNames[cx]] THEN {
TerminalIO.PutF["*** Error: icon public and schematic public for cell icon %g do NOT conform\n", IO.rope[iconName]];
TerminalIO.PutF["Icon public is:"];
CoreOps.PrintWire[wire: iconCT.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\nSchematic public is:"];
CoreOps.PrintWire[wire: cellType.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\n"];
ERROR
};
cellType.properties ← PutCoreProps[cellType.properties, GetCoreProps[cx]];
props ← GetCoreInstProps[cx];
};
ExtractNamedWireIcon: PUBLIC Sinix.ExtractProc = {
cx: Context ← EvaluateParameters[userData, obj, properties];
iconCT: CellType ← NARROW [Sinix.ExtractCell[obj, mode, NIL, cx].result];
iconName: ROPE = Name[obj, cx];
resultWire: WireSeq;
result ← ResultFor[cx, obj, properties];
IF result=NIL
THEN {result ← resultWire ← iconCT.public; RETURN}
ELSE WITH result SELECT FROM
wires: Wires  => {
result ← resultWire ← CoreOps.CreateWire[wires];
resultWire.properties ← PutCoreProps[resultWire.properties, GetCoreProps[cx]];
};
ww: Wire   => {result ← ww; resultWire ← CoreOps.CreateWire[LIST [ww]]};
cellType: CellType => result ← resultWire ← CoreOps.CopyWire[cellType.public];
ENDCASE   => ERROR;
special case
SELECT TRUE FROM
iconCT.public.size=1 AND resultWire.size=1 => {
Special case mostly for compatibility
result ← resultWire ← resultWire[0];
CoreGeometry.PutIndirectLazyPins[mode.decoration, resultWire, iconCT.public[0]];
};
CheckAndDecorate[iconName, obj, iconCT.public, resultWire] => {};
ENDCASE => {
TerminalIO.PutF["*** Error: icon wire and result wire for wire icon %g don't conform\n", IO.rope[iconName]];
TerminalIO.PutF["Icon wire is:"];
CoreOps.PrintWire[wire: iconCT.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\nResult wire is:"];
CoreOps.PrintWire[wire: resultWire, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\n"];
ERROR
};
props ← GetCoreInstProps[cx];
};
ExtractUnNamedWireIcon: PUBLIC Sinix.ExtractProc = {
FlushName: CoreOps.EachWireProc = {[] ← CoreOps.SetShortWireName[wire, NIL]};
wire: Wire;
[result, props] ← ExtractNamedWireIcon[obj, mode, properties, userData];
wire ← NARROW [result];
[] ← CoreOps.VisitWire[wire, FlushName];
};
ExtractWire: Sinix.ExtractProc = {
wire: Wire;
cx: Context;
resultVar, resultExpr: ROPE;
IF obj.class#CDRects.bareRectClass THEN ERROR;
IF obj.layer#CD.commentLayer THEN ERROR;
IF obj.layer#CD.commentLayer THEN
IF CDLayers.Kind[obj.layer]#paint OR CD.LayerTechnology[obj.layer]#NIL THEN ERROR;
Once we get rid of "wire ← ..." we can avoid Evaluating the context, by just evaluating the coreInstProps. Things will gho much, much faster!
[cx, resultVar, resultExpr] ← OldEvaluateParameters[userData, obj, properties, IsResultVar];
IF resultVar=NIL
THEN wire ← CoreOps.CreateWire[]
ELSE {
EvalExpr[cx, resultVar, resultExpr, FALSE];
wire ← NARROW [GetRef[cx, "wire"]];
TerminalIO.PutF["*** Old Syntax: 'wire ←%g'. Sisyph might not be able to cope with that in near future!\n", IO.rope[resultExpr]];
};
CoreGeometry.PutPins[mode.decoration, wire, LIST [[obj]]];
wire.properties ← PutCoreProps[wire.properties, GetCoreInstProps[cx]];
result ← wire;
};
ExtractImport: Sinix.ExtractProc = {
cx: Context = NARROW [userData];
design: CD.Design ← GetDesign[cx];
import: CDImports.ImportSpecific = NARROW [obj.specific];
CDSequencer.CheckAborted[design];
IF import.boundOb=NIL OR import.boundDesign=NIL THEN {
IF NOT CDImports.LoadAndBindDesign[into: design, importeeName: import.designName, allowConflicts: interactive, useCache: TRUE] THEN {TerminalIO.PutF["*** Error: Cannot load import %g[%g] which is unbound.\n", IO.rope[import.objectName], IO.rope[import.designName]]; ERROR};
IF import.boundOb=NIL OR import.boundDesign=NIL THEN {TerminalIO.PutF["*** Error: Cannot bind import %g[%g].\n", IO.rope[import.objectName], IO.rope[import.designName]]; ERROR};
};
RETURN Sinix.Extract[obj: import.boundOb, mode: mode, properties: properties, userData: Create[import.boundDesign, cx]];
};
ES, ExtractSchematicByName: PUBLIC PROC [name: ROPE, cx: Context] RETURNS [CellType] = {
design: CD.Design = GetDesign[cx];
obj: CD.Object = CDDirectory.Fetch[design, name].object;
IF obj=NIL THEN {TerminalIO.PutF["*** Error: ES cannot find object '%g' in design '%g'.\n", IO.rope[name], IO.rope[design.name]]; ERROR};
RETURN [NARROW [Sinix.Extract[obj: obj, mode: mode, properties: NIL, userData: cx].result]];
};
Context Handling Procedures
Create: PUBLIC PROC [design: CD.Design, previousCx: Context ← NIL] RETURNS [cx: Context] = {
EvalDesignExprs: PROC [exprs: ROPES] = {
WHILE exprs#NIL DO
expr: ROPE ← exprs.first;
tokenKind1, tokenKind2: IO.TokenKind; token1, token2, rest: ROPE;
[tokenKind1, token1, rest] ← ParseRope[expr];
[tokenKind2, token2, rest] ← ParseRope[rest];
SELECT TRUE FROM
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '←] => EvalExpr[cx, token1, rest];
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '~] => EvalExpr[cx, token1, rest, FALSE];
ENDCASE => {
TerminalIO.PutF["*** Old syntax in design satellite or expression: Side effect in '%g'.\n", IO.rope[expr]];
Eval[cx, expr];
};
exprs ← exprs.rest;
ENDLOOP;
};
cx ← IF previousCx=NIL THEN SymTab.Create[11] ELSE Copy[previousCx];
Store[cx, designRope, NEW [CD.Design ← design]];
Insert[cx, globalNamesRope, NEW [ROPES ← defaultGlobalNames]];
EvalDesignExprs[NARROW [CDProperties.GetDesignProp[design, expressionsProp]]];
EvalDesignExprs[CDSatellites.GetSatelliteRopes[design]];
};
Copy: PUBLIC PROC [oldCx: Context] RETURNS [cx: Context] = {
Old version that copies TVs. TVs should now be immutable... (needs to be checked...)
CopyItem: SymTab.EachPairAction = {
[] ← SymTab.Store[cx, key, AMTypes.Copy[val]];
};
cx ← SymTab.Create[(SymTab.GetSize[oldCx]/2)*2+3]; -- and Odd size, at least as big as oldCx
[] ← SymTab.Pairs[oldCx, CopyItem];
Store[cx, corePropsRope, NEW [Properties ← NIL]];
Store[cx, coreInstPropsRope, NEW [Properties ← NIL]];
Store[cx, cdObjRope, NEW [CD.Object ← NIL]];
};
Copy: PUBLIC PROC [oldCx: Context] RETURNS [cx: Context] = {
CopyItem: SymTab.EachPairAction = { -- JMF: TV's don't need copying any longer
[] ← SymTab.Store[cx, key, AMTypes.Copy[val]];
};
cx ← SymTab.Create[(SymTab.GetSize[oldCx]/2)*2+3]; -- and Odd size, at least as big as oldCx
[] ← SymTab.Pairs[oldCx, CopyItem];
cx ← SymTab.Copy[oldCx]; -- JMF: copy the table, no more necessary to copy TV's
Store[cx, corePropsRope, NEW [Properties ← NIL]]; -- and erase some standard variables
Store[cx, coreInstPropsRope, NEW [Properties ← NIL]];
Store[cx, cdObjRope, NEW [CD.Object ← NIL]];
};
Store: PUBLIC PROC [cx: Context, var: ROPE, value: REFNIL] = {
[] ← SymTab.Store[cx, var, TVFromRef[value]];
};
Insert: PUBLIC PROC [cx: Context, var: ROPE, value: REFNIL] = {
[] ← SymTab.Insert[cx, var, TVFromRef[value]];
};
FetchInt: PUBLIC PROC [cx: Context, var: ROPE] RETURNS [found: BOOLFALSE, value: INT] = {
val: SymTab.Val ← SymTab.Fetch[cx, var].val;
IF val=NIL THEN RETURN;
WITH RefFromTV[val] SELECT FROM
refInt: REF INT => RETURN [TRUE, refInt^];
refNat: REF NAT => RETURN [TRUE, INT[refNat^]];
ENDCASE => ERROR; -- Wrong type
};
FetchAtom: PUBLIC PROC [cx: Context, var: ROPE] RETURNS [found: BOOLFALSE, value: ATOM] = {
val: SymTab.Val ← SymTab.Fetch[cx, var].val;
IF val=NIL THEN RETURN;
RETURN [TRUE, NARROW [RefFromTV[val]]];
};
FetchRope: PUBLIC PROC [cx: Context, var: ROPE] RETURNS [found: BOOLFALSE, value: ROPE] = {
val: SymTab.Val ← SymTab.Fetch[cx, var].val;
IF val=NIL THEN RETURN;
WITH RefFromTV[val] SELECT FROM
rope: ROPE  => RETURN [TRUE, rope];
text: REF TEXT => RETURN [TRUE, Rope.FromRefText[text]];
ENDCASE   => ERROR; -- Wrong type
};
EvalToTV: PROC [cx: Context, expr: ROPE] RETURNS [result: AMTypes.TV, errorRope: ROPE, noResult: BOOL] = TRUSTED {
[result, errorRope, noResult] ← Interpreter.Evaluate[
rope: expr,
context: AMModelBridge.ContextForFrame[
AMBridge.TVForFrame[PrincOpsUtils.GetReturnFrame[]]
],
symbolsList: LIST [cx]
];
};
EvalToRef: PUBLIC PROC [cx: Context, expr: ROPE] RETURNS [result: REF] = TRUSTED {
tv: AMTypes.TV; errorRope: ROPE; noResult: BOOL;
[tv, errorRope, noResult] ← EvalToTV[cx, expr];
SELECT TRUE FROM
errorRope#NIL => SIGNAL InterpreterError[cx, NIL, expr, errorRope];
noResult => SIGNAL InterpreterError[cx, NIL, expr, "No Result"];
ENDCASE => result ← RefFromTV[tv];
};
EvalExpr : PUBLIC PROC [cx: Context, var, expr: ROPE, checkDefined: BOOLTRUE] = {
result: AMTypes.TV; errorRope: ROPE; noResult: BOOL;
[result, errorRope, noResult] ← EvalToTV[cx, expr];
SELECT TRUE FROM
errorRope#NIL => SIGNAL InterpreterError[cx, var, expr, errorRope];
noResult  => SIGNAL InterpreterError[cx, var, expr, "No Result"];
Rope.Fetch[var]='& OR NOT checkDefined => [] ← SymTab.Store[cx, var, result];
ENDCASE  => IF SymTab.Store[cx, var, result] THEN SIGNAL InterpreterError[cx, var, expr, "Variable has not been previously declared (with a Sisyph.Store)"];
};
AddProp: PUBLIC PROC [cx: Context, key: ATOM, expr: ROPE, inst: BOOL] = {
AddPropInternal[cx, key, EvalToRef[cx, expr], inst];
};
AddPropInternal: PROC [cx: Context, key: ATOM, value: REF, inst: BOOL] = {
previousProps: Properties ← (IF inst THEN GetCoreInstProps ELSE GetCoreProps)[cx];
Store[cx, IF inst THEN coreInstPropsRope ELSE corePropsRope, NEW [Properties ← CoreProperties.PutProp[previousProps, key, value]]];
};
Eval: PRIVATE PROC [cx: Context, expr: ROPE] = {
result: ROPE; errorRope: ROPE; noResult: BOOL; -- JMF: use Evaluate instead of EvaluateToRope
TRUSTED {[result, errorRope, noResult] ← Interpreter.EvaluateToRope[
rope: expr,
context: AMModelBridge.ContextForFrame[
AMBridge.TVForFrame[PrincOpsUtils.GetReturnFrame[]]
],
symbolsList: LIST [cx]
]};
result: AMTypes.TV; errorRope: ROPE; noResult: BOOL; -- JMF
TRUSTED {[result, errorRope, noResult] ← Interpreter.Evaluate[
rope: expr,
context: AMModelBridge.ContextForFrame[
AMBridge.TVForFrame[PrincOpsUtils.GetReturnFrame[]]
],
symbolsList: LIST [cx]
]};
IF errorRope=NIL THEN RETURN;
SIGNAL InterpreterError[cx, NIL, expr, errorRope];
};
ContextEqual: PROC [cx1, cx2: Context, parmNames: ROPES] RETURNS [BOOL] = { -- JMF
Returns FALSE iff a is a subset of b
IsDifferent: PROC [a, b: Context] RETURNS [BOOL] = {
CheckAbsentInb: SymTab.EachPairAction = {
found: BOOL; bVal: SymTab.Val;
IF val=NIL THEN RETURN;
If its a local variable then return right away
IF RopeList.Memb[localVariablesRopes, key] THEN RETURN [FALSE];
If its not a parameter then return right away also
IF parmNames#NIL AND NOT RopeList.Memb[parmNames, key] THEN RETURN [FALSE];
It is a parameter. Check if its there in the context and equal
[found, bVal] ← SymTab.Fetch[b, key];
quit ← NOT found OR bVal=NIL OR NOT TVEqual[bVal, val];
};
RETURN [SymTab.Pairs[a, CheckAbsentInb]];
};
RETURN [cx1=cx2 OR (NOT IsDifferent[cx1, cx2] AND NOT IsDifferent[cx2, cx1])]
};
ContextEqual: PROC [cx1, cx2: Context, parmNames: ROPES] RETURNS [BOOL] = { -- JMF
New version that does parametered cells a bit faster by limiting searching when parameters are specified
Conventions are the parmNames=LIST["0"] ==> no parameters at all
IsSubset: PROC [a, b: Context] RETURNS [BOOL] = {
Returns TRUE iff a is a subset of b, parmNames is always NIL in this routine
CheckAbsentInb: SymTab.EachPairAction = {
found: BOOL; bVal: SymTab.Val;
IF val=NIL THEN RETURN [FALSE]; -- value is in fact not in a ...
If its a local variable then return right away
IF RopeList.Memb[localVariablesRopes, key] THEN RETURN [FALSE];
It is a potential parameter. Check if its there in the context and equal
[found, bVal] ← SymTab.Fetch[b, key];
quit ← NOT found OR bVal=NIL OR NOT TVEqual[bVal, val];
};
RETURN [NOT SymTab.Pairs[a, CheckAbsentInb]];
};
SELECT TRUE FROM
cx1=cx2 => RETURN [TRUE]; -- for sure...
parmNames=NIL => RETURN [IsSubset[cx1, cx2] AND IsSubset[cx2, cx1]]; -- most general case
parmNames.rest=NIL AND Rope.Equal[parmNames.first, "0"] => RETURN [TRUE]; -- no parameters
ENDCASE => { -- limited parameter list, compare only those
found1: BOOL; val1: SymTab.Val;
found2: BOOL; val2: SymTab.Val;
FOR keys: ROPES ← parmNames, keys.rest UNTIL keys=NIL DO
[found1, val1] ← SymTab.Fetch[cx1, keys.first];
[found2, val2] ← SymTab.Fetch[cx2, keys.first];
found1 ← found1 AND val1#NIL; -- TV's are not supposed to be NIL ...
found2 ← found2 AND val2#NIL;
IF found1#found2 THEN RETURN[FALSE]; -- present in only one table
IF found1 AND NOT TVEqual[val1, val2] THEN RETURN[FALSE]; --values differ
ENDLOOP;
RETURN [TRUE]; -- all parameters matched
};
};
InstanceEqual: PROC [obj: CD.Object, p1: CD.PropList, ud1: REF, p2: CD.PropList, ud2: REF] RETURNS [BOOL] = {
instArgs1: ROPESNARROW [CDProperties.GetListProp[p1, expressionsProp]];
satArgs1: ROPESNARROW [CDProperties.GetListProp[p1, Sinix.satellitesProp]];
extract1: ATOMNARROW [CDProperties.GetListProp[p1, mode.extractProcProp]];
instArgs2: ROPESNARROW [CDProperties.GetListProp[p2, expressionsProp]];
satArgs2: ROPESNARROW [CDProperties.GetListProp[p2, Sinix.satellitesProp]];
extract2: ATOMNARROW [CDProperties.GetListProp[p2, mode.extractProcProp]];
parmNames: ROPESNARROW [CDProperties.GetObjectProp[obj, parmNamesProp]];
IF extract1#extract2 THEN RETURN [FALSE];
IF NOT EqualRopes[instArgs1, instArgs2] OR NOT EqualRopes[satArgs1, satArgs2] THEN RETURN [FALSE];
Handle zero parameters as a special case
IF parmNames#NIL AND Rope.Equal[parmNames.first, "0"] THEN RETURN [TRUE];
RETURN [ContextEqual[NARROW [ud1], NARROW [ud2], NIL]];
};
ObjectEqual: PROC [obj: CD.Object, ud1: REF, ud2: REF] RETURNS [BOOL] = {
parmNames: ROPESNARROW [CDProperties.GetObjectProp[obj, parmNamesProp]];
IF parmNames#NIL AND Rope.Equal[parmNames.first, "0"] THEN RETURN [TRUE];
RETURN [ContextEqual[NARROW [ud1], NARROW [ud2], parmNames]];
};
Conveniences
EqualRopes: PUBLIC PROC [ropes1, ropes2: ROPES] RETURNS [BOOL] = {
FOR r1: ROPES ← ropes1, r1.rest WHILE r1#NIL DO
IF NOT RopeList.Memb[ropes2, r1.first] THEN RETURN [FALSE];
ENDLOOP;
FOR r2: ROPES ← ropes2, r2.rest WHILE r2#NIL DO
IF NOT RopeList.Memb[ropes1, r2.first] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
};
Cons: PUBLIC PROC [r: ROPE, lor: ROPES] RETURNS [ROPES] = {
RETURN [CONS [r, lor]];
};
List: PUBLIC PROC [r1, r2, r3, r4, r5, r6: ROPENIL] RETURNS [lor: ROPESNIL] = {
IF r6#NIL THEN lor ← CONS [r6, lor];
IF r5#NIL THEN lor ← CONS [r5, lor];
IF r4#NIL THEN lor ← CONS [r4, lor];
IF r3#NIL THEN lor ← CONS [r3, lor];
IF r2#NIL THEN lor ← CONS [r2, lor];
IF r1#NIL THEN lor ← CONS [r1, lor];
};
Utilities for the implementation
CheckAndDecorate: PROC [iconName: ROPE, icon: CD.Object, drawnPublic, resultPublic: WireSeq, globalNames: ROPESNIL] RETURNS [ok: BOOLTRUE] = {
resultToDrawn: RefTab.Ref ← RefTab.Create[]; -- associates resultPublic to drawnPublic
Construct the association by searching in resultPublic for every name found in drawnPublic
FOR i: NAT IN [0 .. drawnPublic.size) DO
EachResultWire: CoreOps.EachWireProc = {
resultName: ROPE ← CoreOps.GetShortWireName[wire];
IF NOT Rope.Equal[resultName, drawnName] THEN RETURN;
IF wire=resultWire THEN RETURN;
IF resultWire#NIL THEN {TerminalIO.PutF["*** Cell %g: Drawn Icon has a wire %g whose name appears more than once in the schematic\n", IO.rope[iconName], IO.rope[drawnName]]; quit ← TRUE; ok ← FALSE; RETURN};
resultWire ← wire;
};
drawnWire: Wire ← drawnPublic[i];
drawnName: ROPE ← CoreOps.GetShortWireName[drawnWire];
resultWire: Wire ← NIL;
IF drawnName=NIL THEN {TerminalIO.PutF["*** Cell %g: Drawn Icon has an unnamed wire\n", IO.rope[iconName]]; ok ← FALSE; LOOP};
IF CoreOps.VisitWire[resultPublic, EachResultWire] THEN LOOP;
IF resultWire=NIL THEN {TerminalIO.PutF["*** Cell %g: Drawn Icon has wire %g that doesn't correspond to any wire in the schematic\n", IO.rope[iconName], IO.rope[drawnName]]; ok ← FALSE; LOOP};
We decorate the resultWire with the pins of drawnWire
CoreGeometry.PutIndirectLazyPins[mode.decoration, resultWire, drawnWire];
[] ← RefTab.Store[resultToDrawn, resultWire, drawnWire];
ENDLOOP;
Ensure that each resultPublic corresponds to some iconic Wire (apart may be from the wires in globalNames). Warning only for those.
FOR i: NAT IN [0 .. resultPublic.size) DO
resultWire: Wire ← resultPublic[i];
resultName: ROPE ← CoreOps.GetShortWireName[resultWire];
drawnWire: Wire ← NARROW [RefTab.Fetch[resultToDrawn, resultWire].val];
IF resultName=NIL THEN LOOP;
IF drawnWire#NIL THEN LOOP;
IF RopeList.Memb[globalNames, resultName] THEN LOOP;
TerminalIO.PutF["*** Warning in Cell %g: schematic has wire %g that corresponds to no wire in the drawn Icon\n", IO.rope[iconName], IO.rope[resultName]];
ENDLOOP;
};
ProcessGlobalName: PROC [record: CellType, name: ROPE] = { -- JMF
Replaced by new version, only a few extremely minor speedup hacks -- JMF
globals: Wires;
ReplaceSeq: PROC [old: WireSeq] RETURNS [new: WireSeq] = {
new ← old;
FOR i: NAT IN [0 .. old.size) DO new[i] ← Replace[old[i]] ENDLOOP;
};
Replace: PROC [old: Wire] RETURNS [new: Wire] = {
IF old.size#0 THEN RETURN [ReplaceSeq[old]];
RETURN [IF CoreOps.Member[globals, old] THEN global ELSE old];
};
ReplaceAndCleanSeq: PROC [old: WireSeq] RETURNS [new: WireSeq] = {
news: Wires ← LIST [global];
FOR i: NAT IN [0 .. old.size) DO
new ← Replace[old[i]];
IF NOT CoreOps.Member[news, new] THEN news ← CONS [new, news];
ENDLOOP;
new ← CoreOps.CreateWire[news];
};
InsertGlobal: PROC [wire: Wire] = {
IF wire.size#0 THEN SIGNAL GlobalNonAtomic[record, name, wire];
IF NOT CoreOps.Member[globals, wire] THEN globals ← CONS [wire, globals];
};
FindGlobals: CoreOps.EachWirePairProc = {
actualName: ROPE = CoreOps.GetShortWireName[actualWire];
publicName: ROPE = CoreOps.GetShortWireName[publicWire];
IF NOT Rope.Equal[publicName, name] THEN RETURN; -- not a sub global candidate
IF actualName=NIL OR Rope.Equal[actualName, name] THEN InsertGlobal[actualWire];
};
FindWire: CoreOps.EachWireProc = {
IF Rope.Equal[CoreOps.GetShortWireName[wire], name] THEN InsertGlobal[wire];
};
rct: CoreClasses.RecordCellType = NARROW [record.data];
global: Wire;
FOR i: NAT IN [0..rct.size) DO
[] ← CoreOps.VisitBindingSeq[rct[i].actual, rct[i].type.public, FindGlobals];
ENDLOOP;
[] ← CoreOps.VisitWireSeq[rct.internal, FindWire];
IF globals=NIL THEN RETURN;
IF globals.rest=NIL AND Rope.Equal[CoreOps.GetShortWireName[globals.first], name] AND CoreOps.RecursiveMember[record.public, globals.first] THEN RETURN; -- all right already, nothing to do!
global ← CoreOps.SetShortWireName[globals.first, name];
globals ← globals.rest;
record.public ← ReplaceAndCleanSeq[record.public];
rct.internal ← ReplaceAndCleanSeq[rct.internal];
FOR i: NAT IN [0..rct.size) DO rct[i].actual ← ReplaceSeq[rct[i].actual] ENDLOOP;
WHILE globals#NIL DO
Sinix.FuseProperties[mode, globals.first, global, CoreOps.GetCellTypeName[record]];
globals ← globals.rest;
ENDLOOP;
};
ProcessGlobalName: PROC [record: CellType, name: ROPE] = { -- JMF
Minor speedup modifications, no semantic changes -- JMF
The algorithm is:
- globals ← list of wires in internal/public to be identified with global `name'
Identified by looking up the internal by name and the 1st levels bindings by name of the internal and actual simultaneously (actual may be unnamed)
- if there is a single such wire, it has the right name and is public, then we're done
- define the 1st elem of globals to be the new single wire representing `name'
- cleanup public, internal and 1st level actuals
- public and internal may change size (extra copies of the global are removed, global is forced to be public)
- 1st level actuals cannot change sizes
- merge properties of all other representative of global into it
globals: Wires;
ReplaceSeq: PROC [old: WireSeq] ~ {
FOR i: NAT IN [0 .. old.size) DO old[i] ← Replace[old[i]] ENDLOOP;
};
Replace: PROC [old: Wire] RETURNS [new: Wire] = {
new ← old; -- presumably ...
IF old.size#0 THEN FOR i: NAT IN [0 .. old.size) DO new[i] ← Replace[old[i]] ENDLOOP
ELSE IF CoreOps.Member[globals, old] THEN new ← global;
};
ReplaceAndCleanSeq: PROC [old: WireSeq] RETURNS [new: WireSeq] = {
news: Wires ← LIST [global];
FOR i: NAT IN [0 .. old.size) DO
new ← Replace[old[i]];
IF NOT CoreOps.Member[news, new] THEN news ← CONS [new, news];
ENDLOOP;
new ← CoreOps.CreateWire[news];
};
InsertGlobal: PROC [wire: Wire] = {
IF wire.size#0 THEN SIGNAL GlobalNonAtomic[record, name, wire];
IF NOT CoreOps.Member[globals, wire] THEN globals ← CONS [wire, globals];
};
FindGlobals: CoreOps.EachWirePairProc = {
publicName: ROPE = CoreOps.GetShortWireName[publicWire];
IF Rope.Equal[publicName, name] THEN { -- a sub global candidate
actualName: ROPE = CoreOps.GetShortWireName[actualWire];
IF actualName=NIL OR Rope.Equal[actualName, name] THEN InsertGlobal[actualWire];
};
};
FindWire: CoreOps.EachWireProc = {
IF Rope.Equal[CoreOps.GetShortWireName[wire], name] THEN InsertGlobal[wire];
};
rct: CoreClasses.RecordCellType = NARROW [record.data];
global: Wire;
FOR i: NAT IN [0..rct.size) DO
[] ← CoreOps.VisitBindingSeq[rct[i].actual, rct[i].type.public, FindGlobals];
ENDLOOP;
[] ← CoreOps.VisitWireSeq[rct.internal, FindWire];
IF globals=NIL THEN RETURN;
IF globals.rest=NIL AND Rope.Equal[CoreOps.GetShortWireName[globals.first], name] AND CoreOps.RecursiveMember[record.public, globals.first] THEN RETURN; -- all right already, nothing to do!
global ← CoreOps.SetShortWireName[globals.first, name];
globals ← globals.rest;
record.public ← ReplaceAndCleanSeq[record.public];
rct.internal ← ReplaceAndCleanSeq[rct.internal];
FOR i: NAT IN [0..rct.size) DO ReplaceSeq[rct[i].actual] ENDLOOP;
WHILE globals#NIL DO
Sinix.FuseProperties[mode, globals.first, global, CoreOps.GetCellTypeName[record]];
globals ← globals.rest;
ENDLOOP;
};
Name: PROC [obj: CD.Object, userData: REF] RETURNS [ROPE] = {
cx: Context ← NARROW [userData];
RETURN [CDDirectory.Name[obj, GetDesign[cx]]];
};
Touch: CoreGeometry.TouchProc = {
IF instance1.obj.class#CDRects.bareRectClass OR instance2.obj.class#CDRects.bareRectClass THEN ERROR; -- the only thing we know is Rectangles!
IF ~SilTouchRect[CoreGeometry.InlineBBox[instance1], CoreGeometry.InlineBBox[instance2]] THEN RETURN;
IF instance1.obj.layer=instance2.obj.layer THEN RETURN [TRUE];
RETURN [CDLayers.AbstractToPaint[instance1.obj.layer]=CDLayers.AbstractToPaint[instance2.obj.layer]];
};
SilTouchRect: PROC [r1, r2: CD.Rect] RETURNS [BOOL] = INLINE {
Intersect: PROC [i1min, i1max, i2min, i2max: CD.Number] RETURNS [BOOL] = INLINE {
RETURN [(i1max >= i2min) AND (i2max >= i1min)];
};
Adjoin: PROC [i1min, i1max, i2min, i2max: CD.Number] RETURNS [BOOL] = INLINE {
RETURN [(i2min >= i1min AND i2min <= i1max AND i2max >= i1max) OR
(i1min >= i2min AND i1min <= i2max AND i1max >= i2max)];
};
RETURN [(Intersect[r1.x1, r1.x2, r2.x1, r2.x2] AND Adjoin[r1.y1, r1.y2, r2.y1, r2.y2]) OR (Intersect[r1.y1, r1.y2, r2.y1, r2.y2] AND Adjoin[r1.x1, r1.x2, r2.x1, r2.x2]) OR CDBasics.Inside[r1, r2] OR CDBasics.Inside[r2, r1]]
};
GetRef: PROC [cx: Context, var: ROPE] RETURNS [ref: REF] = {
val: SymTab.Val ← SymTab.Fetch[cx, var].val;
IF val=NIL THEN ERROR;
ref ← RefFromTV[val];
};
GetCDObj: PUBLIC PROC [cx: Context] RETURNS [CD.Object] = {
RETURN [NARROW [GetRef[cx, cdObjRope]]]
};
GetCoreProps: PUBLIC PROC [cx: Context] RETURNS [Properties] = {
RETURN [NARROW [GetRef[cx, corePropsRope]]]
};
GetCoreInstProps: PUBLIC PROC [cx: Context] RETURNS [Properties] = {
RETURN [NARROW [GetRef[cx, coreInstPropsRope]]]
};
GetDesign: PUBLIC PROC [cx: Context] RETURNS [CD.Design] = {
RETURN [NARROW [GetRef[cx, designRope]]]
};
GetGlobalNames: PUBLIC PROC [cx: Context] RETURNS [ROPES] = {
RETURN [NARROW [GetRef[cx, globalNamesRope]]]
};
RefFromTV: PROC [tv: REF] RETURNS [REF] = {
IF tv=NIL THEN RETURN [NIL];
IF ~ISTYPE [tv, AMTypes.TV] THEN ERROR;
TRUSTED {RETURN [SELECT AMTypes.UnderClass[AMTypes.TVType[tv]] FROM
nil, atom, rope, list, ref => AMBridge.TVToRef[tv],
ENDCASE  => AMBridge.SomeRefFromTV[tv]]};
};
TVFromRef: PROC [ref: REF] RETURNS [AMTypes.TV] = TRUSTED {
RETURN [AMBridge.TVForReferent[ref]];
};
TVEqual: PROC [tv1, tv2: REF] RETURNS [BOOL] = { -- JMF
Added code to handle the special case of ROPE as well as ROPES
IF AMTypes.TVEqual[tv1, tv2] THEN RETURN [TRUE];
WITH RefFromTV[tv1] SELECT FROM -- manage standard types not covered by AMTypes
rr1: ROPES => WITH RefFromTV[tv2] SELECT FROM
rr2: ROPES => RETURN [EqualRopes[rr1, rr2]];
ENDCASE => RETURN [FALSE];
r1: ROPE => WITH RefFromTV[tv2] SELECT FROM -- JMF
r2: ROPE => RETURN [Rope.Equal[r1, r2]]; -- JMF
ENDCASE => RETURN [FALSE]; -- JMF
more cases could be added in the SELECT statement, but some are not necessary (for example REF INT, due to the behavior of AMTypes.TVEqual).
ENDCASE => RETURN [FALSE];
};
PutCoreProps: PROC [onto, from: Properties] RETURNS [new: Properties] = {
PutProp: PROC [prop: ATOM, val: REF ANY] = {
new ← CoreProperties.PutProp[new, prop, val];
};
new ← onto;
CoreProperties.Enumerate[from, PutProp];
};
StripCellTypeName: PROC [cellType: CellType, dropPart: ROPE] = {
[] ← CoreOps.SetCellTypeName[cellType, CDNameToCTName[CoreOps.GetCellTypeName[cellType], dropPart]];
};
CDNameToCTName: PROC [cdName, dropPart: ROPE] RETURNS [ctName: ROPE] = {
ctName ← Rope.Substr[cdName, 0, Rope.Index[cdName, 0, dropPart]];
};
DefaultUserData: PROC [design: CD.Design] RETURNS [REF] ~ {
RETURN [Create[design]];
};
Semi-public Utilities
ParseRope: PUBLIC PROC [rope: ROPE] RETURNS [tokenKind: IO.TokenKind, token, rest: ROPE] = {
ENABLE {
IO.Error => GOTO Error;
IO.EndOfStream => GOTO EOF;
};
stream: IO.STREAMIO.RIS[rope];
charsSkipped1: INTIO.SkipWhitespace[stream];
ampersand: BOOLIO.PeekChar[stream]='&;
charsSkipped2: INT;
IF ampersand THEN {[] ← IO.GetChar[stream]; charsSkipped1 ← charsSkipped1 + 1};
[tokenKind, token, charsSkipped2] ← IO.GetCedarTokenRope[stream];
rest ← Rope.Substr[rope, charsSkipped1+charsSkipped2+Rope.Length[token]];
IF ampersand THEN {
IF tokenKind=tokenID THEN token ← Rope.Cat["&", token] ELSE GOTO Error;
TerminalIO.PutF["*** Variable name starting with &: %g.\n", IO.rope[token]];
};
EXITS
Error => RETURN [tokenERROR, NIL, NIL];
EOF => RETURN [tokenEOF, NIL, NIL];
};
IsParsedID: PUBLIC PROC [tokenKind: IO.TokenKind, token, id: ROPE] RETURNS [BOOL] = {
RETURN [tokenKind=tokenID AND Rope.Equal[token, id]];
};
IsParsedChar: PUBLIC PROC [tokenKind: IO.TokenKind, token: ROPE, char: CHAR] RETURNS [BOOL] = {
RETURN [tokenKind=tokenSINGLE AND Rope.Fetch[token]=char];
};
EvaluateParameters: PUBLIC PROC [userData: REF, obj: CD.Object, properties: CD.PropList] RETURNS [cx: Context] = {
EvalExprs: PROC [exprs: ROPES, inst: BOOL] = {
WHILE exprs#NIL DO
expr: ROPE ← exprs.first;
tokenKind1, tokenKind2: IO.TokenKind; token1, token2, rest: ROPE;
[tokenKind1, token1, rest] ← ParseRope[expr];
[tokenKind2, token2, rest] ← ParseRope[rest];
SELECT TRUE FROM
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '←] => EvalExpr[cx, token1, rest];
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, ':] => AddProp[cx, Convert.AtomFromRope[token1], rest, inst];
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '~] => EvalExpr[cx, token1, rest, FALSE];
Rope.Find[expr, "←"]>=0 OR Rope.Find[expr, ":"]>=0=> {
TerminalIO.PutF["*** Old syntax in %g: Side effect in '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[expr]];
Eval[cx, expr];
};
ENDCASE => {
previousName: ROPENARROW [CoreProperties.GetProp[(IF inst THEN GetCoreInstProps ELSE GetCoreProps)[cx], CoreOps.nameProp]];
IF previousName#NIL AND NOT Rope.Equal[previousName, expr] THEN {
TerminalIO.PutF["*** Error in object '%g': Conflicting names '%g' and '%g' [probably two satellites].\n", IO.rope[Name[obj, cx]], IO.rope[previousName], IO.rope[expr]]; ERROR;
};
IF Rope.Find[expr, " "]>=0 THEN TerminalIO.PutF["*** Warning in %g: name contains space '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[expr]];
AddPropInternal[cx, CoreOps.nameProp, expr, inst];
};
exprs ← exprs.rest;
ENDLOOP;
};
cx ← Copy[NARROW [userData]];
Store[cx, cdObjRope, NEW [CD.Object ← obj]];
CDSequencer.CheckAborted[GetDesign[cx]];
EvalExprs[NARROW [CDProperties.GetObjectProp[obj, expressionsProp]], FALSE];
EvalExprs[NARROW [CDProperties.GetObjectProp[obj, Sinix.satellitesProp]], FALSE];
EvalExprs[NARROW [CDProperties.GetListProp[properties, expressionsProp]], TRUE];
EvalExprs[NARROW [CDProperties.GetListProp[properties, Sinix.satellitesProp]], TRUE];
};
ProcessGlobalNames: PUBLIC PROC [record: CellType, cx: Context] = {
FOR names: ROPES ← GetGlobalNames[cx], names.rest WHILE names#NIL DO
ProcessGlobalName[record, names.first];
ENDLOOP;
};
Compatibility code
We are trying to get rid of these ones, do not use them!
This was the top-level extract proc. It evaluates the arguments to the schematic and then calls either ExtractCellIconInternal or Sinix.ExtractCell to do its job.
ExtractSchematic: PRIVATE Sinix.ExtractProc = {
cx: Context;
resultVar, resultExpr: ROPE;
[cx, resultVar, resultExpr] ← OldEvaluateParameters[userData, obj, properties, IsResultVar];
SELECT TRUE FROM
Rope.Equal[resultVar, "cI"] => {
cellType: CellType;
EvalExpr[cx, resultVar, resultExpr, FALSE];
cellType ← ExtractCellIconInternal[obj, cx];
TerminalIO.PutF["*** Old syntax for cell icon in %g: '%g ←%g'. Convert!\n", IO.rope[Name[obj, userData]], IO.rope[resultVar], IO.rope[resultExpr]];
cellType.properties ← PutCoreProps[cellType.properties, GetCoreProps[cx]];
result ← cellType; props ← GetCoreInstProps[cx];
};
Rope.Equal[resultVar, "wI"] => {
wire: Wire;
EvalExpr[cx, resultVar, resultExpr, FALSE];
wire ← ExtractNamedWireIconInternal[obj, cx];
TerminalIO.PutF["*** Old syntax for named wire icon in %g: '%g ←%g'. Convert!\n", IO.rope[Name[obj, userData]], IO.rope[resultVar], IO.rope[resultExpr]];
wire.properties ← PutCoreProps[wire.properties, GetCoreProps[cx]];
result ← wire; props ← GetCoreInstProps[cx];
};
Rope.Equal[resultVar, "wire"] => ERROR;
ENDCASE => {
cellType: CellType;
High risk code here: it costed BS 3 days of debugging. The problem is that since Sinix.ExtractCell caches its result, some instances might have been created earlier with the returned cellType, and so it is incorrect to modify it in place. Therefore ProcessGlobalNames must not change the cellType when all the work has already been done. Properties are a bit funny too, and this code assumes that all cellType properties come from object expression/satellite, but not from an instance expression/satellite. Therefore it is incorrect to put "coreProps ← ..." as an instance satellite.
result ← Sinix.ExtractCell[obj, mode, properties, cx].result;
cellType ← NARROW [result];
ProcessGlobalNames[cellType, cx];
cellType.properties ← PutCoreProps[cellType.properties, GetCoreProps[cx]];
StripCellTypeName[cellType, ".sch"];
props ← GetCoreInstProps[cx];
};
};
This proc is called for cell icons. If a core definition is found then it is returned, otherwise a core cell of class Unspecified is returned.
ExtractCellIconInternal: PROC [icon: CD.Object, cx: Context] RETURNS [cellType: CellType] = {
iconName: ROPE = Name[icon, cx];
iconCT: CellType ← NARROW [Sinix.ExtractCell[icon, mode, NIL, cx].result];
Compute core for the schematic
cellType ← NARROW [GetRef[cx, "cI"]];
IF cellType=NIL
THEN {
cellType ← CoreOps.CreateCellType[class: CoreClasses.unspecifiedCellClass, public: iconCT.public, name: CDNameToCTName[iconName, ".icon"]];
}
ELSE {
name: ROPE ← CoreOps.GetCellTypeName[cellType];
IF name=NIL THEN name ← CDNameToCTName[iconName, ".icon"];
cellType ← Sinix.CreateIcon[cellType: cellType, name: name];
};
CoreGeometry.PutObject[mode.decoration, cellType, icon];
Check public
IF NOT CheckAndDecorate[iconName, icon, iconCT.public, cellType.public, GetGlobalNames[cx]] THEN {
TerminalIO.PutF["*** Error: icon public and schematic public for cell icon %g do NOT conform\n", IO.rope[iconName]];
TerminalIO.PutF["Icon public is:"];
CoreOps.PrintWire[wire: iconCT.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\nSchematic public is:"];
CoreOps.PrintWire[wire: cellType.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\n"];
ERROR
}
};
This proc is called for wire icons. If a core definition is found it is returned, otherwise an error is signalled. As for cell icons, pins provide the attatchment points to the wire.
ExtractNamedWireIconInternal: PROC [icon: CD.Object, cx: Context] RETURNS [result: Wire] = {
iconName: ROPE = Name[icon, cx];
iconCT: CellType ← NARROW [Sinix.ExtractCell[icon, mode, NIL, cx].result];
result ← NARROW [GetRef[cx, "wI"]];
IF result=NIL THEN ERROR;
Check wire
IF iconCT.public.size=1 THEN {
special case
CoreGeometry.PutPins[mode.decoration, result, CoreGeometry.GetPins[mode.decoration, iconCT.public[0]]];
RETURN;
};
IF ~CheckAndDecorate[iconName, icon, iconCT.public, CoreOps.CreateWire[LIST [result]]] THEN {
TerminalIO.PutF["*** Error: icon wire and result wire for wire icon %g don't conform\n", IO.rope[iconName]];
TerminalIO.PutF["Icon wire is:"];
CoreOps.PrintWire[wire: iconCT.public, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\nResult wire is:"];
CoreOps.PrintWire[wire: result, out: TerminalIO.TOS[], level: LAST [NAT]];
TerminalIO.PutF["\n"];
ERROR
}
};
IsResultVarProc: TYPE = PROC [var: ROPE] RETURNS [BOOL];
Tells if this variable is a result variable.
IsResultVar: IsResultVarProc = {
RETURN [Rope.Equal[var, "cI"] OR Rope.Equal[var, "wI"] OR Rope.Equal[var, "wire"]];
};
OldEvaluateParameters: PROC [userData: REF, obj: CD.Object, properties: CD.PropList, isResultVar: IsResultVarProc ← NIL] RETURNS [cx: Context, resultVar, resultExpr: ROPENIL] = {
EvalExprs: PROC [exprs: ROPES, inst: BOOL] = {
tempResultVar, tempResultExpr: ROPENIL;
WHILE exprs#NIL DO
expr: ROPE ← exprs.first;
tokenKind1, tokenKind2: IO.TokenKind; token1, token2, rest: ROPE;
[tokenKind1, token1, rest] ← ParseRope[expr];
[tokenKind2, token2, rest] ← ParseRope[rest];
SELECT TRUE FROM
tokenKind1=tokenID AND isResultVar#NIL AND isResultVar[token1] => {
IF tempResultVar#NIL THEN {TerminalIO.PutF["*** Error: multiple result expressions encountered: '%g' and '%g'.\n", IO.rope[expr], IO.rope[tempResultVar]]; ERROR};
following token must be ': or '← (': is a hack for the sequence stuff)
IF NOT IsParsedChar[tokenKind2, token2, '←] AND NOT IsParsedChar[tokenKind2, token2, ':] THEN {TerminalIO.PutF["*** Error: result expression has wrong syntax: '%g'.\n", IO.rope[expr]]; ERROR};
tempResultVar ← token1; tempResultExpr ← rest;
};
inst AND IsParsedID[tokenKind1, token1, "name"] AND IsParsedChar[tokenKind2, token2, '←] => {
tokenKind: IO.TokenKind; token, rr: ROPE;
[tokenKind, token, rr] ← ParseRope[rest];
IF tokenKind=tokenROPE
THEN TerminalIO.PutF["*** Old syntax in %g: Use an expression or a satellite %g instead of '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[token], IO.rope[expr]]
ELSE TerminalIO.PutF["*** Old syntax in %g: Use 'CoreName: %g' instead of '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[rest], IO.rope[expr]];
AddProp[cx, CoreOps.nameProp, rest, inst];
};
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '←] => EvalExpr[cx, token1, rest];
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, ':] => AddProp[cx, Convert.AtomFromRope[token1], rest, inst];
tokenKind1=tokenID AND IsParsedChar[tokenKind2, token2, '~] => EvalExpr[cx, token1, rest, FALSE];
Rope.Find[expr, "←"]>=0 OR Rope.Find[expr, ":"]>=0=> {
TerminalIO.PutF["*** Old syntax in %g: Side effect in '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[expr]];
Eval[cx, expr];
};
ENDCASE => {
previousName: ROPENARROW [CoreProperties.GetProp[(IF inst THEN GetCoreInstProps ELSE GetCoreProps)[cx], CoreOps.nameProp]];
IF previousName#NIL AND NOT Rope.Equal[previousName, expr] THEN {
TerminalIO.PutF["*** Error in object '%g': Conflicting names '%g' and '%g' [probably two satellites].\n", IO.rope[Name[obj, cx]], IO.rope[previousName], IO.rope[expr]]; ERROR;
};
IF Rope.Find[expr, " "]>=0 THEN TerminalIO.PutF["*** Warning in %g: name contains space '%g'.\n", IO.rope[Name[obj, userData]], IO.rope[expr]];
AddPropInternal[cx, CoreOps.nameProp, expr, inst];
};
exprs ← exprs.rest;
ENDLOOP;
IF tempResultVar#NIL THEN {resultVar ← tempResultVar; resultExpr ← tempResultExpr};
};
cx ← Copy[NARROW [userData]];
Store[cx, cdObjRope, NEW [CD.Object ← obj]];
CDSequencer.CheckAborted[GetDesign[cx]];
EvalExprs[NARROW [CDProperties.GetObjectProp[obj, expressionsProp]], FALSE];
EvalExprs[NARROW [CDProperties.GetObjectProp[obj, Sinix.satellitesProp]], FALSE];
EvalExprs[NARROW [CDProperties.GetListProp[properties, expressionsProp]], TRUE];
EvalExprs[NARROW [CDProperties.GetListProp[properties, Sinix.satellitesProp]], TRUE];
};
Exceptions
GlobalNonAtomic: PUBLIC SIGNAL [record: CellType, name: ROPE, wire: Wire] = CODE;
InterpreterError: PUBLIC SIGNAL [cx: Context, var, expr, errorRope: ROPE] = CODE;
Module Initialization
Sinix.RegisterExtractProc[$SisyphExtractSchematic, ExtractSchematic];
Sinix.RegisterExtractProc[$SisyphExtractCellIcon, ExtractCellIcon];
Sinix.RegisterExtractProc[$SisyphExtractNamedWireIcon, ExtractNamedWireIcon];
Sinix.RegisterExtractProc[$SisyphExtractUnNamedWireIcon, ExtractUnNamedWireIcon];
Sinix.RegisterExtractProc[$SisyphExtractWire, ExtractWire];
Sinix.RegisterExtractProc[$SisyphExtractImport, ExtractImport];
CDProperties.PutProp[CDCells.pCellClass, mode.extractProcProp, $SisyphExtractSchematic];
CDProperties.PutProp[CDRects.bareRectClass, mode.extractProcProp, $SisyphExtractWire];
CDProperties.PutProp[CDTexts.rigidTextClass, mode.extractProcProp, $ExtractNull];
CDProperties.PutProp[CDTexts.flipTextClass, mode.extractProcProp, $ExtractNull];
CDProperties.PutProp[CDImports.importsClass, mode.extractProcProp, $SisyphExtractImport];
END.