PWPLABasicsImpl:
CEDAR
PROGRAM
IMPORTS Atom, BasicTime, BoolOps, CDDirectory, CDExpr, Convert, FS, PWBasics, Rope
EXPORTS PWPLABasics
SHARES CDDirectory = BEGIN
OPEN PWPLABasics;
Error: PUBLIC ERROR[ec: ErrorCode, msg: ROPE] ~ CODE;
-- Looks for name1, name2, then name3; if absent, return NIL and screams if required
GetTile:
PROC[from, to:
CD.Design, tilesPrefix:
ROPE, tilesFetched:
REF
LIST
OF
ROPE, name1, name2, name3:
PW.ObjName ←
NIL, required:
BOOL]
RETURNS [newName:
PW.ObjName] =
BEGIN
tile, oldTile: CD.ObPtr;
done, found: BOOL;
name:
PW.ObjName ←
SELECT
TRUE
FROM
IsPresent[from, name1] => name1,
IsPresent[from, name2] => name2,
IsPresent[from, name3] => name3,
ENDCASE => NIL;
IF name =
NIL
AND required
THEN
BEGIN
PWBasics.Output["PWPLA could not find tile '", name1, "'"];
IF name2 # NIL THEN PWBasics.Output[" nor '", name2, "'"];
IF name3 # NIL THEN PWBasics.Output[" nor '", name3, "'"];
PWBasics.Output[".\n'"];
ERROR Error[MissingTile, NIL];
END;
IF name = NIL AND ~required THEN RETURN[NIL];
-- add the prefix to the cell name to form "tileset.tilename"
newName ← Rope.Cat[tilesPrefix, name];
IF ~AlreadyFetched[tilesFetched, name]
THEN
BEGIN
tilesFetched^ ← CONS[name, tilesFetched^];
-- check if this tile is already in the design
[found, oldTile] ← CDDirectory.Fetch[to, newName];
-- if an old version of the tile is already in the design it gets renamed
-- we need a good gensym
IF found
THEN
BEGIN
bogusName: ROPE ← Rope.Cat[newName, " renamed on ", Convert.RopeFromTime[BasicTime.Now[]]];
done ← CDDirectory.Rename[to, oldTile, bogusName];
IF ~done THEN ERROR;
END;
-- *** make a copy of the cell and include it: copy should be recursive ***
tile ← CDDirectory.Another[CDDirectory.Fetch[from, name].object, from, to];
-- rename the cell: there should be no conflict now
done ← CDDirectory.Rename[to, tile, newName];
-- If the name has changed, something is really wrong
IF ~done THEN ERROR;
END;
END;
IsPresent:
PROC [whereTilesAre:
CD.Design, name:
ROPE]
RETURNS [
BOOL] =
{RETURN[CDDirectory.Fetch[whereTilesAre, name].found]};
-- Check if all names are non-NIL
AllPresent:
PUBLIC PROC [list:
LIST
OF
PW.ObjName]
RETURNS [b:
BOOL ←
TRUE] =
{WHILE list#NIL DO b ← b AND list.first#NIL; list ← list.rest; ENDLOOP};
RequiredTile:
PUBLIC PROC[from, to:
CD.Design, tilesPrefix:
ROPE, tilesFetched:
REF
LIST
OF
ROPE, name1, name2, name3:
PW.ObjName ←
NIL]
RETURNS [
PW.ObjName] =
{RETURN[GetTile[from, to, tilesPrefix, tilesFetched, name1, name2, name3, TRUE]]};
OptionalTile:
PUBLIC PROC[from, to:
CD.Design, tilesPrefix:
ROPE, tilesFetched:
REF
LIST
OF
ROPE, name1, name2, name3:
ROPE ←
NIL]
RETURNS [
PW.ObjName] =
{RETURN[GetTile[from, to, tilesPrefix, tilesFetched, name1, name2, name3, FALSE]]};
-- Check if a tile is present
AlreadyFetched:
PROC[tilesFetched:
REF LIST
OF
ROPE, name:
ROPE]
RETURNS [
BOOL] =
BEGIN
list: LIST OF ROPE ← tilesFetched^;
WHILE list#NIL DO IF name=list.first THEN RETURN[TRUE]; list ← list.rest; ENDLOOP;
RETURN[FALSE];
END;
ListRefAnyToListRope:
PUBLIC PROC [list:
LIST
OF
REF]
RETURNS [
LIST
OF
ROPE] =
BEGIN
RETURN[IF list=NIL THEN NIL ELSE CONS[NARROW[list.first], ListRefAnyToListRope[list.rest]]];
END;
ListRefAnyToListOfListRope:
PUBLIC
PROC [list:
LIST
OF
REF]
RETURNS [llr:
LIST
OF
LIST
OF
ROPE] =
BEGIN
IF list=NIL THEN RETURN[NIL];
llr
←
CONS[
ListRefAnyToListRope[NARROW[list.first, LIST OF REF]],
ListRefAnyToListOfListRope[list.rest]];
END;
ReverseList:
PUBLIC PROC [list:
LIST
OF
PW.ObjName]
RETURNS [newList:
LIST
OF
PW.ObjName] =
BEGIN
newList ← NIL;
WHILE list#NIL DO newList ← CONS[list.first, newList]; list ← list.rest; ENDLOOP;
END;
Length:
PUBLIC PROC [list:
LIST
OF
ROPE]
RETURNS [length:
INT ← 0] =
BEGIN
WHILE list # NIL DO length ← length+1; list ← list.rest; ENDLOOP;
END;
LengthListOfList:
PUBLIC PROC [list:
LIST
OF
LIST
OF ROPE]
RETURNS [length:
INT ← 0] =
BEGIN
WHILE list # NIL DO length ← length+Length[list.first]; list ← list.rest; ENDLOOP;
END;
EnsureSize:
PUBLIC PROC [list:
LIST
OF
ROPE, quantity:
INT] =
{IF Length[list] # quantity THEN ERROR};
-- Read equations one at a time, converting to sum-of-products as we go
GetEQN:
PUBLIC PROC[par: SymTab.Ref, inNames, outNames:
LIST
OF
ROPE]
RETURNS[BoolOps.TruthTable] =
BEGIN
tt: BoolOps.TruthTable;
inAtoms, reverseAtoms: LIST OF ATOM ← NIL;
eqnTrees, reversedEqnTrees: LIST OF BoolOps.Tree ← NIL;
message: ROPE;
allErrorMessages: ROPE ← NIL;
IF inNames =
NIL
OR outNames =
NIL
THEN
ERROR Error[ParameterErrors, "Both 'Inputs' and 'Outputs' must be specified."];
-- read equations one at a time, converting to sum-of-products as we go
FOR outs:
LIST
OF
ROPE ← outNames, outs.rest
WHILE outs #
NIL
DO
eqn: ROPE ← NIL;
t: BoolOps.Tree ← NIL;
eqn ← CDExpr.FetchRope[par, outs.first].val;
IF eqn =
NIL
THEN {
allErrorMessages ← Rope.Cat[allErrorMessages, "Equation for output '", outs.first, "' is missing.\n"];
};
FOR i:
INT
DECREASING
IN [0 .. Rope.Length[eqn])
DO
-- remove trailing semicolon
ch: CHAR ← Rope.Fetch[eqn, i];
IF ch # '
AND ch # '\t
AND ch # '\n
THEN {
IF ch = '; AND i > 0 THEN eqn ← Rope.Substr[eqn, 0, i];
EXIT;
};
ENDLOOP;
message ← NIL;
t ← BoolOps.RopeToTree[eqn ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message #
NIL
THEN
allErrorMessages ← Rope.Cat[allErrorMessages, "Could not translate equation for output '", outs.first, "', error was: ", message, "\n"]
ELSE {
message ← NIL;
t ← BoolOps.TreeToSOP[t ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message #
NIL
THEN
allErrorMessages ← Rope.Cat[allErrorMessages, "Could not translate output '", outs.first, "' to sum-of-products form, error was: ", message, "\n"]
ELSE {
reversedEqnTrees ← CONS[t, reversedEqnTrees];
};
};
ENDLOOP;
IF ~Rope.Equal[allErrorMessages, NIL] THEN ERROR Error[ParameterErrors, allErrorMessages];
-- reverse our list of equations since it is backwards
FOR trees:
LIST
OF BoolOps.Tree ← reversedEqnTrees, trees.rest
WHILE trees #
NIL
DO
eqnTrees ← CONS[trees.first, eqnTrees];
ENDLOOP;
-- create a list of atoms from the input names
FOR ins:
LIST
OF
ROPE ← inNames, ins.rest
WHILE ins #
NIL
DO
reverseAtoms ← CONS[Atom.MakeAtom[ins.first], reverseAtoms];
ENDLOOP;
FOR ins:
LIST
OF
ATOM ← reverseAtoms, ins.rest
WHILE ins #
NIL
DO
inAtoms ← CONS[ins.first, inAtoms];
ENDLOOP;
-- make a truth table
message ← NIL;
tt ← BoolOps.SOPToTT[inAtoms, eqnTrees ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message #
NIL
THEN
ERROR Error[ParameterErrors, Rope.Cat["Could not translate equations to truth table form, error was: ", message, "\n"]];
RETURN[tt];
END;
FetchTT:
PUBLIC PROC [par: SymTab.Ref]
RETURNS [truthTable: BoolOps.TruthTable] =
BEGIN
-- get the truth table from a file
GetTTFile:
PROC[ttFile:
ROPE]
RETURNS[tt: BoolOps.TruthTable] =
BEGIN
ttStream: IO.STREAM;
explanation: ROPE;
IF ttFile = NIL THEN RETURN[NIL];
ttStream ← FS.StreamOpen[ttFile ! FS.Error => IF error.group = user THEN {ttStream ← NIL; CONTINUE}];
IF ttStream =
NIL
THEN
ERROR Error[ParameterErrors, Rope.Cat["Could not open truth table file '", ttFile, "'.\n"]];
tt ← BoolOps.StreamToTT[ttStream ! BoolOps.Error => {explanation ← msg; CONTINUE}];
IF explanation #
NIL
THEN
ERROR Error[ParameterErrors, Rope.Cat["Error in reading truth table: ", explanation, "\n"]];
END;
-- read the truth table directly from parameters file
GetTT:
PROC[par: SymTab.Ref]
RETURNS[BoolOps.TruthTable] =
BEGIN
ref: REF ANY;
ref ← CDExpr.FetchRef[par, "TruthTable"].val;
IF ref = NIL THEN RETURN[NIL];
IF ~ISTYPE[ref, BoolOps.TruthTable] THEN ERROR Error[ParameterErrors, Rope.Cat["TruthTable option is not of type BoolOps.TruthTable!"]];
RETURN[NARROW[ref]];
END;
-- get a truth table
SELECT
TRUE
FROM
CDExpr.FetchRope[par, "TruthTableFile"].found =>
truthTable ← GetTTFile[CDExpr.FetchRope[par, "TruthTableFile"].val]; -- Read TruthTable from a file
CDExpr.FetchRef[par, "TruthTable"].found =>
truthTable ← GetTT[par]; -- Read TruthTable directly
ENDCASE =>
-- should be GetEQN[inputNames, outputNames]
truthTable ← NIL;
PWBasics.Output[". . . not tonight dearling, I have a headache !"];
END;
DumpTruthTable:
PUBLIC PROC [tt: BoolOps.TruthTable, ttFile:
ROPE] =
BEGIN
-- dump truth table to file
ttStream: IO.STREAM;
message: ROPE ← NIL;
ttStream ← FS.StreamOpen[ttFile, $create ! FS.Error => IF error.group = user THEN {ttStream ← NIL; CONTINUE}];
IF ttStream = NIL THEN ERROR Error[NoDump, Rope.Cat["Could not create truth table dump file '", ttFile, "'.\n"]];
BoolOps.TTToStream[tt, ttStream ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message # NIL THEN ERROR Error[NoDump, Rope.Cat["Could not dump truth table to file '", ttFile, "', error was: ", message, "\n"]];
END;
END.