{from Jason at uiuc April 29, 1988}

{ ptest - test the Preas/Roberts placement language parser

Copyright (C) 1987 by Jonathan Rose. All rights reserved.
}



program TestParser(input,output);

{ pbenchtypes.h contains the data types, structures, and constants
for the Pascal-language parser of the standard placement program input
format specified by Preas and Roberts.


Copyright (C) 1987 by Jonathan Rose. All rights reserved.

}



{Constants Section - all grouped together in accordance with Pascal's wishes }

const MAXNAMESIZE = 100;
KEYWORDSIZE = 15;

{Module Type Constants}

STANDARD = 1;
PAD = 2;
GENERAL = 3;
PARENT = 4;
FEEDTHROUGH = 5;
ENDFILE = 6;

{ Unspecified Position and width on terminal constants }

NOPOSITION = -1.0;
NOWIDTH = -1.0;


{IO Pin type constants}

I = 1;
O = 2;
B = 3;
PI = 4;
PO = 5;
PB = 6;
F = 7;

{Pin side constants}
BOTTOM = 1;
RIGHT = 2;
TOP = 3;
LEFT = 4;
NOSIDE = 5;

{Layer type constants}
PDIFF = 1;
NDIFF = 2;
POLY = 3;
METAL1 = 4;
METAL2 = 5;
NOLAYER = 6;


{Reflection Type Constants}
RFLNONE = 1;
RFLY = 2;

{Rotation Type Constants}

ROT0 = 1;
ROT90 = 2;
ROT180 = 3;
ROT270 = 4;




{ Character strings that define the key words and comments }

{ Comment Delimiters }

STARTCOMMENT = '/* ';
ENDCOMMENT = '*/ ';

{ End of entry delimeter }

LineTerminator = ';';

{ Module Parameters }

ModuleKeyword = 'MODULE ';
EndModuleKeyword = 'ENDMODULE ';
TypeKeyword = 'TYPE ';
WidthKeyword = 'WIDTH ';
HeightKeyword = 'HEIGHT ';

{ IOList Delimiters }
IOListKeyword = 'IOLIST ';
EndIOListKeyword = 'ENDIOLIST ';

{ Network Delimiters }
NetworkKeyword = 'NETWORK ';
EndNetworkKeyword = 'ENDNETWORK ';

{ Placement Delimiters }
PlacementKeyword = 'PLACEMENT ';
EndPlacementKeyword = 'ENDPLACEMENT ';

{ Module Types }
StandardKeyword = 'STANDARD ';
PadKeyword = 'PAD ';
GeneralKeyword = 'GENERAL ';
ParentKeyword = 'PARENT ';
FeedthroughKeyword = 'FEEDTHROUGH ';

{ IOList Terminal Types }
InputTerminal = 'I ';
OutputTerminal = 'O ';
BiDirectionTerminal = 'B ';
PadInputTerminal = 'PI ';
PadOutputTerminal = 'PO ';
PadBiTerminal = 'PB ';
FeedThroughTerminal = 'F ';

{ Side Types }
BottomSide = 'BOTTOM ';
RightSide = 'RIGHT ';
TopSide = 'TOP ';
LeftSide = 'LEFT ';

{ Layer Types }
PDiffLayer = 'PDIFF ';
NDiffLayer = 'NDIFF ';
PolyLayer = 'POLY ';
Metal1Layer = 'METAL1 ';
Metal2Layer = 'METAL2 ';

{ Reflections }
NoReflection = 'RFLNONE ';
YReflection = 'RFLY ';

{ Rotations }
Rot0String = 'ROT0 ';
Rot90String = 'ROT90 ';
Rot180String = 'ROT180 ';
Rot270String = 'ROT270 ';




{Types Definitions}


type NameType = packed array [1..MAXNAMESIZE] of char;

{ Keyword Type is a smaller size due to the need in Pascal to pad blanks}

KeywordType = packed array [1..KEYWORDSIZE] of char;


ModuleType = STANDARD .. ENDFILE;

Number = real;

TerminalType = I .. F;

SideType = BOTTOM .. NOSIDE;

LayerType = PDIFF .. NOLAYER;


IOListPointer = ^IOList;

IOList = record
SignalName: NameType;
Terminal: TerminalType;
Side:SideType;
Position: Number;
Width: Number;
Layer: LayerType;
Link: IOListPointer;
end;

SignalListPointer = ^SignalList;

SignalList = record
SignalName: NameType;
Link:SignalListPointer;
end;


NetworkListPointer = ^NetworkList;

NetworkList = record
InstanceName: NameType;
ModuleName: NameType;
SignalListHead: SignalListPointer;
Link: NetworkListPointer;
end;

ReflectionType = RFLNONE .. RFLY;
RotationType = ROT0 .. ROT270 ;


PlacementListPointer = ^PlacementList;

PlacementList = record
InstanceName: NameType;
XLocation: Number;
YLocation: Number;
Reflection: ReflectionType;
Rotation: RotationType;
Link: PlacementListPointer;
end;


type FileNameType = packed array [1..100] of char;

var InputFile : text;
 OutputFile: text;

Name: NameType;
ModType: ModuleType;
Width: Number;
Height: Number;
IOListHead: IOListPointer;
NetworkListHead: NetworkListPointer;
PlacementListHead: PlacementListPointer;
Done: boolean;
ModuleNumber: integer;
LineNumber: integer;

InputFileName : FileNameType;
OutputFileName: FileNameType;

procedure GetModule(var TInputFile: text;
   var TName: NameType;
   var TModType: ModuleType;
   var TWidth: Number;
   var THeight: Number;
   var TIOListHead: IOListPointer;
   var TNetworkListHead: NetworkListPointer;
   var TPlacementListHead: PlacementListPointer;
   var TLineNumber: integer);

{ Getmodule - routines for parsing the standard placement input form
specified by Preas and Roberts.

Copyright (C) 1987 by Jonathan Rose. All rights reserved.
}




{ Constants }

const FATAL = 1;
WARNING = 2;


BLANK = ' ';
TAB = ' ';

EOFConst = -1;

{ Global Variables }

var
 InputField: NameType;

 EOFDetected: boolean;
 ExpectingEOF: boolean;
 LineTerminated: boolean;

 LLineNumber: integer;


{ Support Routines }


{ Error is called when an exception occurs - FATAL ones cause
an halt, while WARNINGS are just printed and execution continues }

procedure Error(EString: NameType; ErrorType: integer);

begin
if ErrorType = WARNING then write('Warning: ')
else write('Fatal Error: ');

writeln(EString,' on line ',LLineNumber,' of input file');

if ErrorType = FATAL then halt;
end;



{ kstrequal returns true if the two strings are the same, false otherwise }

function kstrequal(s1: KeywordType;s2: NameType): boolean;

var j: integer;
begin

j := 1;
kstrequal := true;

while (s1[j] = s2[j]) and (s1[j] <> BLANK) and (s2[j] <> BLANK)
 do j := j + 1;

if s1[j] <> s2[j] then kstrequal := false;
end;


{ nstrcpy copies one name type into another }

procedure nstrcpy(var s1: NameType; s2: NameType);
var j: integer;

begin
j := 1;

while j <= MAXNAMESIZE do begin

 s1[j] := s2[j];
 j := j + 1;
end;
end;

{ nstrlen returns the length of the first string on nonblank characters
in the NameType string }

function nstrlen(s1: NameType): integer;

var j: integer;

begin

j := 1;
while not ((s1[j] = BLANK) or (s1[j] = chr(0))) do j := j + 1;

nstrlen := j - 1;

end;


{ ScanField picks up the next field, whilst counting the number of
lines that go by }

function ScanField (var InputFile: text; var SText:NameType): integer;

var NextChar: char;
TextP: integer;

{ Skip over blanks, tabs and newlines, counting newlines }

begin

if eof(InputFile) then
 ScanField := EOFConst
else begin

 read(InputFile,NextChar);
 if not eof(InputFile) then
  if eoln(InputFile) then LLineNumber := LLineNumber + 1;

 while ((NextChar = BLANK) or (NextChar = TAB)) and (not eof(InputFile))
 do begin

  read(InputFile,NextChar);
  if not eof(InputFile) then
  if eoln(InputFile) then LLineNumber := LLineNumber + 1;
 end;

 if eof(InputFile) then
  ScanField := EOFConst
 else begin

  TextP := 1;
  SText[TextP] := NextChar;
  TextP := TextP + 1;

  { Collect Characters until a white space or end of file }

  read(InputFile,NextChar);
  if not eof(InputFile) then
  if eoln(InputFile) then LLineNumber := LLineNumber + 1;

  while (NextChar <> BLANK) and (NextChar <> TAB) and
   not eof(InputFile) do begin

  SText[TextP] := NextChar;
  TextP := TextP + 1;
  read(InputFile,NextChar);
  if not eof(InputFile) then
   if eoln(InputFile) then LLineNumber := LLineNumber + 1;
  end;

  SText[TextP] := BLANK;

  ScanField := 1;

 end;
end;
end;


{ GetField reads a field from the input stream. A field is any set of
characters delimited by blanks, tabs or newlines. }

procedure GetField(var IFile: text; var GText: NameType);
var ErrFlag: integer;

begin
ErrFlag := ScanField(IFile,GText);

if (ErrFlag = EOFConst) and (not ExpectingEOF) then
 Error('Unexpected End of File',FATAL)

else if (ErrFlag = EOFConst) and ExpectingEOF then
 EOFDetected := true;
end;


{ ReadField reads a field from the input stream, ignoring comments }

procedure ReadField (var IFile: text; var RText: NameType);
var FieldLength: integer;
begin

GetField(IFile,RText);

while kstrequal(STARTCOMMENT,RText) do begin

 { get fields until end of comment }

 GetField(IFile,RText);

 while not kstrequal(ENDCOMMENT,RText) do begin
  if EOFDetected then Error('Non-terminated Comment',FATAL);
  GetField(IFile,RText);
 end;
 GetField(IFile,RText);
end;

{ Strip off the line terminator if it is attached to the field,
and set end of logical line indicator }

FieldLength := nstrlen(RText);

if RText[FieldLength] = LineTerminator then begin
 LineTerminated := true;
 RText[FieldLength] := BLANK;
end;
end;


{ ConvertToNumber does the conversion from ascii to a floating point
number }

function ConvertToNumber(CText: NameType): Number;

var CharPosition: integer;
 SubTotal: Number;
 ch: char;
 NewDigit: Number;
 DivFactor: Number;

begin

SubTotal := 0.0;
CharPosition := 1;

ch := CText[CharPosition];
while (ch <> BLANK) and (ch <> '.') do begin

 if (ord(ch) < ord('0')) or (ord(ch) > ord('9')) then

  Error('Illegal Number specified',FATAL)
 else begin
  NewDigit := ord(ch) - ord('0');
  SubTotal := (SubTotal * 10.0) + NewDigit;
 end;

 CharPosition := CharPosition + 1;
 ch := CText[CharPosition];
end;

DivFactor := 10.0;
if ch = '.' then begin
 CharPosition := CharPosition + 1;
 ch := CText[CharPosition];
 while ch <> BLANK do begin

  if (ord(ch) < ord('0')) or (ord(ch) > ord('9')) then
  Error('Illegal Number specified',FATAL)
  else begin
  NewDigit := ord(ch) - ord('0');
  SubTotal := SubTotal + NewDigit / DivFactor;
  DivFactor := DivFactor * 10;
  end;

  CharPosition := CharPosition + 1;
  ch := CText[CharPosition];
 end;
end;

ConvertToNumber := SubTotal;
end;

{ Get Line Terminator looks for the line terminator as the next
field if it wasn't already encountered }

procedure GetLineTerminator(var IFile: text);

var TermField: NameType;

begin
if LineTerminated then
 LineTerminated := false
else begin
 ReadField(IFile,TermField);

 if LineTerminated then LineTerminated := false
 else Error('Missing Line Terminator (;)',FATAL);
end;
end;



{ GetIOList reads in the IO List }

procedure GetIOList(var IFile: text; var IOListHead: IOListPointer);

var IOField: NameType;
EndDetected: boolean;

NewIOPin: IOListPointer;
IOListTail: IOListPointer;

begin
IOListHead := nil;

GetLineTerminator(IFile);

{ Main loop to read each pin }

EndDetected := false;
while not EndDetected do begin

 { Read Signal Name or end of IO List }

 ReadField(IFile,IOField);

if kstrequal(EndIOListKeyword,IOField) then begin
  EndDetected := true;
  GetLineTerminator(IFile);
 end

 else begin { Create a New IO Pin }

  new(NewIOPin);

  NewIOPin^.Side := NOSIDE;
  NewIOPin^.Position := NOPOSITION;
  NewIOPin^.Layer := NOLAYER;
  NewIOPin^.Width := NOWIDTH;
  NewIOPin^.Link := nil;

  nstrcpy(NewIOPin^.SignalName,IOField);

  { Read Terminal Type }

  ReadField(IFile,IOField);


  if kstrequal(InputTerminal,IOField) then
  NewIOPin^.Terminal := I
  else if kstrequal(OutputTerminal,IOField) then
  NewIOPin^.Terminal := O
  else if kstrequal(BiDirectionTerminal,IOField) then
  NewIOPin^.Terminal := B
  else if kstrequal(PadInputTerminal,IOField) then
  NewIOPin^.Terminal := PI
  else if kstrequal(PadOutputTerminal,IOField) then
  NewIOPin^.Terminal := PO
  else if kstrequal(PadBiTerminal,IOField) then
  NewIOPin^.Terminal := PB
  else if kstrequal(FeedThroughTerminal,IOField) then
  NewIOPin^.Terminal := F

  else Error('Unknown Terminal Type Specification',FATAL);

  if not LineTerminated then begin

  ReadField(IFile,IOField);

  if (not LineTerminated) or (LineTerminated and
   (nstrlen(IOField) <> 0)) then begin

   if kstrequal(BottomSide,IOField) then
   NewIOPin^.Side := BOTTOM
   else if kstrequal(RightSide,IOField) then
   NewIOPin^.Side := RIGHT
   else if kstrequal(TopSide,IOField) then
   NewIOPin^.Side := TOP
   else if kstrequal(LeftSide,IOField) then
   NewIOPin^.Side := LEFT
  
   else Error('Unknown Side Type Specification',FATAL);

   if not LineTerminated then begin

   ReadField(IFile,IOField);

   if (not LineTerminated) or
    (LineTerminated and (nstrlen(IOField) <> 0)) then
    begin

    NewIOPin^.Position := ConvertToNumber(IOField);

    if not LineTerminated then begin

    ReadField(IFile,IOField);

    if (not LineTerminated) or
     (LineTerminated and (nstrlen(IOField) <> 0))
     then begin

     NewIOPin^.Width := ConvertToNumber(IOField);
     if not LineTerminated then begin

     ReadField(IFile,IOField);

     if (not LineTerminated) or
      (LineTerminated and
      (nstrlen(IOField) <> 0)) then begin

      if kstrequal(PDiffLayer,IOField) then
      NewIOPin^.Layer := PDIFF
      else if kstrequal(NDiffLayer,IOField)
      then
      NewIOPin^.Layer := NDIFF
      else if kstrequal(PolyLayer,IOField)
      then
      NewIOPin^.Layer := POLY
      else if kstrequal(Metal1Layer,IOField)
      then
      NewIOPin^.Layer := METAL1
      else if kstrequal(Metal2Layer,IOField)
      then
      NewIOPin^.Layer := METAL2
     
      else Error('Unknown Layer Specification', FATAL);

      GetLineTerminator(IFile);
     end;
     end;
    end;
    end;
   end;
   end;
  end;
  end;

  { Link the new IO Pin }

  if IOListHead = nil then
  IOListHead := NewIOPin
  else
  IOListTail^.Link := NewIOPin;

  IOListTail := NewIOPin;

  LineTerminated := false;

 end;
end;
end;


procedure GetNetworkList(var IFile: text; var NetworkListHead: NetworkListPointer);
var NetworkField: NameType;
EndDetected: boolean;
 SignalListEnd: boolean;
 NewNetworkEntry, NetworkListTail: NetworkListPointer;
 NewSignal, SignalListTail: SignalListPointer;


begin
NetworkListHead := nil;
GetLineTerminator(IFile);

{ Main loop to read each Network Entry }

EndDetected := false;
while not EndDetected do begin

 { Read Instance Name or end of Network List }

 ReadField(IFile,NetworkField);

if kstrequal(EndNetworkKeyword,NetworkField) then begin
  EndDetected := true;
  GetLineTerminator(IFile);
 end

 else begin { Create a New Network Entry }

  new(NewNetworkEntry);

NewNetworkEntry^.SignalListHead := nil;
NewNetworkEntry^.Link := nil;

  nstrcpy(NewNetworkEntry^.InstanceName,NetworkField);

  { Read Module Name }

  ReadField(IFile,NewNetworkEntry^.ModuleName);

  { Get the signal names and link onto this entry's list }

  SignalListEnd := false;

  while not SignalListEnd do begin

  if LineTerminated then
   SignalListEnd := true
  
  else begin
   ReadField(IFile,NetworkField);

   if LineTerminated and (nstrlen(NetworkField) = 0) then
   SignalListEnd := true
  
   else begin

   { Create a New Signal }

   new(NewSignal);
   NewSignal^.Link := nil;
   nstrcpy(NewSignal^.SignalName,NetworkField);

   { Link onto this entry's list }

   if NewNetworkEntry^.SignalListHead = nil then
    NewNetworkEntry^.SignalListHead := NewSignal
   else
    SignalListTail^.Link := NewSignal;

   SignalListTail := NewSignal;
  end;

  end;

  end;

  { Link the new NetworkEntry }

  if NetworkListHead = nil then
  NetworkListHead := NewNetworkEntry
  else
  NetworkListTail^.Link := NewNetworkEntry;

  NetworkListTail := NewNetworkEntry;

  LineTerminated := false;
 end;
end;
end;

procedure GetPlacementList(var IFile: text; var PlacementListHead: PlacementListPointer);

var PlacementField: NameType;
 EndDetected: boolean;
 NewPlacementEntry, PlacementListTail: PlacementListPointer;

begin
PlacementListHead := nil;

GetLineTerminator(IFile);

{ Main loop to read each Placement Entry }

EndDetected := false;
while not EndDetected do begin

 { Read Instance Name or end of Placement List }

 ReadField(IFile,PlacementField);

if kstrequal(EndPlacementKeyword,PlacementField) then begin
  EndDetected := true;
  GetLineTerminator(IFile);
 end

 else begin { Create a New Placement Entry }

  new(NewPlacementEntry);

NewPlacementEntry^.Reflection := RFLNONE;
NewPlacementEntry^.Rotation := ROT0;
NewPlacementEntry^.Link := nil;

  nstrcpy(NewPlacementEntry^.InstanceName,PlacementField);

  { Read X Location }

  ReadField(IFile,PlacementField);

NewPlacementEntry^.XLocation := ConvertToNumber(PlacementField);

  { Read Y Location }

  ReadField(IFile,PlacementField);

NewPlacementEntry^.YLocation := ConvertToNumber(PlacementField);

  { Check for End of Line or reflections, rotations }

  while not LineTerminated do begin
  ReadField(IFile,PlacementField);

  if not (LineTerminated and (nstrlen(PlacementField) = 0))
    then begin

   if kstrequal(NoReflection,PlacementField) then
   NewPlacementEntry^.Reflection := RFLNONE

   else if kstrequal(YReflection,PlacementField) then
   NewPlacementEntry^.Reflection := RFLY

   else if kstrequal(Rot0String,PlacementField) then
   NewPlacementEntry^.Rotation := ROT0

   else if kstrequal(Rot90String,PlacementField) then
   NewPlacementEntry^.Rotation := ROT90

   else if kstrequal(Rot180String,PlacementField) then
   NewPlacementEntry^.Rotation := ROT180

   else if kstrequal(Rot270String,PlacementField) then
   NewPlacementEntry^.Rotation := ROT270

   else Error('Unknown Rotation or Reflection type',FATAL);
  end;
  end;

  LineTerminated := false;

  { Link the new placement entry }

  if PlacementListHead = nil then
  PlacementListHead := NewPlacementEntry
  else
  PlacementListTail^.Link := NewPlacementEntry;

  PlacementListTail := NewPlacementEntry;
 end;
end;
end;



{ This is the main routine, called to get the next module. Depending on the
type of module, the Network List and the Placement List may or may not
be empty. }

begin { GetModule }

LineTerminated := false;
EOFDetected := false;
LLineNumber := TLineNumber;

{ Set all the optional return arguments to Null at start }

TWidth := 0.0;
THeight := 0.0;

TIOListHead := nil;
TNetworkListHead := nil;
TPlacementListHead := nil;


{ Get Module Declaration, return with ENDFILE type if end of file }

ExpectingEOF := true;
ReadField(TInputFile,InputField);

if EOFDetected then
 TModType := ENDFILE

else if not kstrequal(ModuleKeyword,InputField) then
 Error('Missing Module Declaration', FATAL)

else begin

ExpectingEOF := false;
ReadField(TInputFile,TName);

GetLineTerminator(TInputFile);

{ Get Type }

ReadField(TInputFile,InputField);

if not kstrequal(TypeKeyword,InputField) then
 Error('Missing Type Declaration - Must be after MODULE', FATAL)
else

ReadField(TInputFile,InputField);

if kstrequal(StandardKeyword,InputField) then TModType := STANDARD
else if kstrequal(PadKeyword,InputField) then TModType := PAD
else if kstrequal(GeneralKeyword,InputField) then TModType := GENERAL
else if kstrequal(ParentKeyword,InputField) then TModType := PARENT
else if kstrequal(FeedthroughKeyword,InputField) then TModType := FEEDTHROUGH

else Error('Unknown Module Type Specification',FATAL);

GetLineTerminator(TInputFile);

{ Loop to pick up the next field and process it - either
width, height, IOList, NetworkList or PlacementList }

ReadField(TInputFile,InputField);

while not (kstrequal(EndModuleKeyword,InputField)) do begin

 if kstrequal(WidthKeyword,InputField) then begin

  ReadField(TInputFile,InputField);

  TWidth := ConvertToNumber(InputField);

  GetLineTerminator(TInputFile);
 end

 else if kstrequal(HeightKeyword,InputField) then begin

  ReadField(TInputFile,InputField);

  THeight := ConvertToNumber(InputField);

  GetLineTerminator(TInputFile);
 end

 else if kstrequal(IOListKeyword,InputField) then

  GetIOList(TInputFile,TIOListHead)

 else if kstrequal(NetworkKeyword,InputField) then

  GetNetworkList(TInputFile,TNetworkListHead)

 else if kstrequal(PlacementKeyword,InputField) then

  GetPlacementList(TInputFile,TPlacementListHead)

 else Error('Unknown Keyword',FATAL);

 ReadField(TInputFile,InputField);
end;

GetLineTerminator(TInputFile);

end;
{ Update LineNumber }
TLineNumber := LLineNumber;
end; { GetModule }

procedure WriteModule(var WOutputFile: text;
   WName: NameType;
   WModType: ModuleType;
   WWidth: Number;
   WHeight: Number;
   WIOListHead: IOListPointer;
   WNetworkListHead: NetworkListPointer;
   WPlacementListHead: PlacementListPointer);

{ Writemodule - routines for printing out a module in the format
specified by Preas and Roberts.

Copyright (C) 1987 by Jonathan Rose. All rights reserved.
}


var IOListPin: IOListPointer;
CurrentNetworkEntry: NetworkListPointer;
CurrentSignal: SignalListPointer;
CurrentPlacementEntry: PlacementListPointer;


procedure PrintKeyword(var OFile: text; Keyword: KeywordType);

const BLANK = ' ';
var CharPosition: integer;

begin

CharPosition := 1;

while not ((Keyword[CharPosition] = BLANK) or
  (Keyword[CharPosition] = chr(0))) do begin

 write(OFile,Keyword[CharPosition]);

 CharPosition := CharPosition + 1;
end;
end;


procedure PrintName(var OFile: text; Name: NameType);

const BLANK = ' ';
var CharPosition: integer;

begin

CharPosition := 1;

while not ((Name[CharPosition] = BLANK) or
  (Name[CharPosition] = chr(0))) do begin

 write(OFile,Name[CharPosition]);

 CharPosition := CharPosition + 1;
end;
end;



begin { WriteModule }


writeln(WOutputFile);
PrintKeyword (WOutputFile, ModuleKeyword);
write(WOutputFile,' ');
PrintName (WOutputFile, WName);
writeln(WOutputFile,';');

write(WOutputFile,' ');
PrintKeyword(WOutputFile,TypeKeyword);
write(WOutputFile,' ');

if WModType = STANDARD then
 PrintKeyword(WOutputFile,StandardKeyword)

else if WModType = PAD then
 PrintKeyword(WOutputFile,PadKeyword)

else if WModType = GENERAL then
 PrintKeyword(WOutputFile,GeneralKeyword)

else if WModType = PARENT then
 PrintKeyword(WOutputFile,ParentKeyword)

else if WModType = FEEDTHROUGH then
 PrintKeyword(WOutputFile,FeedthroughKeyword)

else
 writeln(WOutputFile,'UNKNOWN!!!');

writeln(WOutputFile,';');

if (WWidth <> 0.0) then begin
 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,WidthKeyword);
 writeln(WOutputFile,' ',WWidth:1:1,';');
end;

if (WHeight <> 0.0) then begin
 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,HeightKeyword);
 writeln(WOutputFile,' ',WHeight:1:1,';');
end;

IOListPin := WIOListHead;

if (WIOListHead <> nil) then begin
 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,IOListKeyword);
 writeln(WOutputFile,';');
end;

while (IOListPin <> nil) do begin

 write(WOutputFile,' ');
 PrintName(WOutputFile,IOListPin^.SignalName);
 write(WOutputFile,' ');

 if (IOListPin^.Terminal = I ) then
  PrintKeyword(WOutputFile,InputTerminal)

 else if (IOListPin^.Terminal = O) then
  PrintKeyword(WOutputFile,OutputTerminal)

 else if (IOListPin^.Terminal = B) then
  PrintKeyword(WOutputFile,BiDirectionTerminal)

 else if (IOListPin^.Terminal = PI) then
  PrintKeyword(WOutputFile,PadInputTerminal)

 else if (IOListPin^.Terminal = PO) then
  PrintKeyword(WOutputFile,PadOutputTerminal)

 else if (IOListPin^.Terminal = PB) then
  PrintKeyword(WOutputFile,PadBiTerminal)

 else if (IOListPin^.Terminal = F) then
  PrintKeyword(WOutputFile,FeedThroughTerminal)

 else
  write(WOutputFile,'UNKNOWN!!! ');

 if (IOListPin^.Side <> NOSIDE) then begin

  if (IOListPin^.Side = BOTTOM) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,BottomSide);
  end

  else if (IOListPin^.Side = RIGHT) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,RightSide);
  end

  else if (IOListPin^.Side = TOP) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,TopSide);
  end

  else if (IOListPin^.Side = LEFT) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,LeftSide);
  end

  else
  write(WOutputFile,'UNKNOWN!!!');


  if (IOListPin^.Position <> NOPOSITION) then begin
  write(WOutputFile,' ',IOListPin^.Position:1:1);

  if (IOListPin^.Width <> NOWIDTH) then begin
   write(WOutputFile,' ',IOListPin^.Width:1:1);

   if (IOListPin^.Layer <> NOLAYER) then begin

   write(WOutputFile,' ');

   if (IOListPin^.Layer = PDIFF) then
    PrintKeyword(WOutputFile, PDiffLayer)

   else if (IOListPin^.Layer = NDIFF) then
    PrintKeyword(WOutputFile, NDiffLayer)

   else if (IOListPin^.Layer = POLY) then
    PrintKeyword(WOutputFile,PolyLayer)

   else if (IOListPin^.Layer = METAL1) then
    PrintKeyword(WOutputFile,Metal1Layer)

   else if (IOListPin^.Layer = METAL2) then
    PrintKeyword(WOutputFile,Metal2Layer)

   else
    PrintKeyword(WOutputFile,'UNKNOWN LAYER!!');
   end;
  end;
  end;
 end;


 writeln(WOutputFile,';');
 IOListPin := IOListPin^.Link;
end;

if (WIOListHead <> nil) then begin
 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,EndIOListKeyword);
 writeln(WOutputFile,';');
end;

if (WNetworkListHead <> nil) then begin

 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,NetworkKeyword);
 writeln(WOutputFile,';');

 CurrentNetworkEntry := WNetworkListHead;

 while (CurrentNetworkEntry <> nil) do begin

  write(WOutputFile,' ');
  PrintName(WOutputFile,CurrentNetworkEntry^.InstanceName);
  write(WOutputFile,' ');
  PrintName(WOutputFile,CurrentNetworkEntry^.ModuleName);

  CurrentSignal := CurrentNetworkEntry^.SignalListHead;

  while (CurrentSignal <> nil) do begin
  
  write(WOutputFile,' ');
  PrintName(WOutputFile,CurrentSignal^.SignalName);

  CurrentSignal := CurrentSignal^.Link;
  end;

  writeln(WOutputFile,';');

  CurrentNetworkEntry := CurrentNetworkEntry^.Link;
 end;

 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,EndNetworkKeyword);
 writeln(WOutputFile,';');
end;

if (WPlacementListHead <> nil) then begin

 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,PlacementKeyword);
 writeln(WOutputFile,';');

 CurrentPlacementEntry := WPlacementListHead;

 while (CurrentPlacementEntry <> nil) do begin

  write(WOutputFile,' ');
  PrintName(WOutputFile,CurrentPlacementEntry^.InstanceName);
  write(WOutputFile,' ',CurrentPlacementEntry^.XLocation:1:1,' ',
  CurrentPlacementEntry^.YLocation:1:1);

  if (CurrentPlacementEntry^.Reflection = RFLNONE) then
  write(WOutputFile,' ')

  else if (CurrentPlacementEntry^.Reflection = RFLY) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,YReflection);
  end

  else
  write(WOutputFile,' UNKNOWN REFLECTION');

  if (CurrentPlacementEntry^.Rotation = ROT0) then
  write(WOutputFile,' ')

  else if (CurrentPlacementEntry^.Rotation = ROT90) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,Rot90String);
  end

  else if (CurrentPlacementEntry^.Rotation = ROT180) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,Rot180String);
  end

  else if (CurrentPlacementEntry^.Rotation = ROT270) then begin
  write(WOutputFile,' ');
  PrintKeyword(WOutputFile,Rot270String);
  end

  else
  write(WOutputFile,' UNKNOWN ROTATION');

  writeln(WOutputFile,';');
  

  CurrentPlacementEntry := CurrentPlacementEntry^.Link;
 end;

 { writeln(WOutputFile,' ',EndPlacementKeyword,';'); BTP April 29, 1988 12:59:08 pm PDT }
 write(WOutputFile,' ');
 PrintKeyword(WOutputFile,EndPlacementKeyword);
 writeln(WOutputFile,';');
end;


write(WOutputFile,' ');
PrintKeyword(WOutputFile,EndModuleKeyword);
writeln(WOutputFile,';');
end; { WriteModule }

begin { main body of pptest }

{ if argc < 2 then
 writeln('Usage: ptest inputfile outputfile'); }

LineNumber := 1;

{ Open input and output files }

{ argv(1,InputFileName); }
{ argv(2,OutputFileName); }

reset(InputFile, InputFileName);

rewrite(OutputFile, OutputFileName);

Done := false;
ModuleNumber := 1;
while not Done do begin

 GetModule(InputFile, Name, ModType, Width, Height, IOListHead,
   NetworkListHead, PlacementListHead, LineNumber);

 if ModType = ENDFILE then Done := true

 else begin
  writeln('Got Module ',ModuleNumber);

  WriteModule(OutputFile, Name, ModType, Width, Height, IOListHead,
   NetworkListHead, PlacementListHead);
 end;

ModuleNumber := ModuleNumber + 1;
end;

end.