*/
Stuck? Need help? Ask questions on our forums.
*/

View \MANGLER.PAS

Mangler v1.32

Submitted By: Unknown
Rating: (Not rated) (Rate It)


{*  Mangler, a program to mangle pascal source files.
    Copyright (C) 1993  Berend de Boer

    This program is free software for noncommercial users; you can
    redistribute it and/or modify it under the terms of the license,
    stated in de accompanying file LICENSE.TXT.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    license for more details.

    See the accompanying READ.ME file for information on contacting the
    author.


$Author: Berend_de_Boer $
$Date: 94/03/19 20:31:37 $
$Revision: 1.3 $

Last changes:
93-04-19  Fixed bug that occured parsing objects defined in implementation
          section.
          Improved white space removal.
          Fixed bug in forward pointers referencing.
          Fixed bug that caused the identifier after the inherited keyword
          to be mangled, which should not of course.
          Changed mangling of objects: object methods are not mangled anymore.
          If all sources were read methods could be mangled well, but
          currently mangler does not do this.
          Encoding function improved so probability of collisions has greatly
          been diminished
93-07-14  Error fixed. Section variable not reset after implementation was
          read
93-07-19  Removed lexical analyzer to PASLEX.L
          Not all variants of the with statement were recognized. Fixed.
93-11-19  Labels were not supported. Fixed
          Now interface section remains intact. Only implementation section
          is mangled.
93-11-25  Files which did not contain a unit or program keyword, broke mangler
*}


{* conditional defines *}

{$DEFINE Pass2}                   {* do crunch pass *}
{$DEFINE DelTmpFiles}             {* delete temporary files *}
{{$DEFINE ShowProcs}               {* show procedures *}
{{$DEFINE PMD}                     {* use post mortem debugger *}

{$X+}
program Mangler;

uses LexLib,
     {$IFDEF PMD}
     BBError, PMD, MemCheck,
     {$ENDIF}
     Objects, Dos;


const
  Version = '1.32';

const
  LineWidth:word = 120;
  Prime = 67099547;

const
{* this list should be sorted! *}
  _ABSOLUTE = 1;
  _AND = 2;
  _ARRAY = 3;
  _ASM = 4;
  _ASSEMBLER = 5;
  _BEGIN = 6;
  _CASE = 7;
  _CONST = 8;
  _CONSTRUCTOR = 9;
  _DESTRUCTOR = 10;
  _DIV = 11;
  _DO = 12;
  _DOWNTO = 13;
  _ELSE = 14;
  _END = 15;
  _EXTERNAL = 16;
  _FAR = 17;
  _FILE = 18;
  _FOR = 19;
  _FORWARD = 20;
  _FUNCTION = 21;
  _GOTO = 22;
  _IF = 23;
  _IMPLEMENTATION = 24;
  _IN = 25;
  _INHERITED = 26;
  _INLINE = 27;
  _INTERFACE = 28;
  _INTERRUPT = 29;
  _LABEL = 30;
  _MOD = 31;
  _NEAR = 32;
  _NIL = 33;
  _NOT = 34;
  _OBJECT = 35;
  _OF = 36;
  _OR = 37;
  _PACKED = 38;
  _PRIVATE = 39;
  _PROCEDURE = 40;
  _PROGRAM = 41;
  _PUBLIC = 42;
  _RECORD = 43;
  _REPEAT = 44;
  _SET = 45;
  _SHL = 46;
  _SHR = 47;
  _STRING = 48;
  _THEN = 49;
  _TO = 50;
  _TYPE = 51;
  _UNIT = 52;
  _UNTIL = 53;
  _USES = 54;
  _VAR = 55;
  _VIRTUAL = 56;
  _WHILE = 57;
  _WITH = 58;
  _XOR = 59;
  SEMICOLON = 100;
  CHARACTER_STRING = 101;
  IDENTIFIER = 102;
  DOT = 103;
  DIRECTIVE = 104;
  NUMBER = 105;
  ASSIGNMENT = 106;
  COLON = 107;
  EQUAL = 108;
  LPAREN = 109;
  RPAREN = 110;
  COMMA = 111;
  OTHER = 112;
  DOTDOT = 113;
  GE = 114;
  LE = 115;
  NOTEQUAL = 116;
  _CHAR = 117;
  NEWLINE = 118;
  KEYWORD = 119;
  UPARROW = 120;
  AMPERSAND = 121;
  LBRAC = 122;
  RBRAC = 123;

type
  PScopeCol = ^TScopeCol;

  PMangleItem = ^TMangleIteM;
  TMangleItem = record
    Name : PString;
    HashedName : PString;
    ScopeCol : PScopeCol;
  end;

  TScopeCol = object(TStringCollection)
    procedure FreeItem(Item : pointer)virtual;
    procedure Insert(Item : pointer)virtual;
    function  KeyOf(Item : pointer) : pointervirtual;
    function  InsertIntrIdentifier(const Name : string; var Index : integer) : string;
    function  InsertIdentifier(const Name : string; var Index : integer) : string;
    function  AtHashedName(Index : integer) : string;
    function  AtScope(Index : integer) : PScopeCol;
    function  LastScope : PScopeCol;
  end;


  SectionTypes = (None, Decl, BetweenCaseAndOfDecl,
                  Func, FuncDecl, FuncOuter, FunctionType,
                  CompoundStatement, WithStatement, Inlin, LabelStatement);

  PSectionItem = ^TSectionItem;
  TSectionItem = record
    Section : SectionTypes;
    WithPushes,
    OpeningLevel,
    DeclType : integer;
  end;

  PSectionCol = ^TSectionCol;
  TSectionCol = object(TStringCollection)
    procedure FreeItem(Item : pointer)virtual;
  end;

var
  sourceDir : DirStr;
  DirInfo : SearchRec;
  ExitSave : pointer;
  ImplementationLineNumber : word;


function HashIt(s : string) : string;
const
  Base = 5;
  chars:array [0..Base-1] of char = ('0', '1', 'I', 'O', 'l');

  function StrBase(l : longint) : string;
  var
    s : string;
  begin
    s := '';
    while l > Base-1 do  begin
      s := chars[l mod Base] + s;
      l := l div Base;
    end{ of while }
    s := chars[l]+ s;
    StrBase := s;
  end;

var
  l, d : longint;
  i,j : word;
begin
  l := 0;
  Move(s[1], l, length(s) mod 4);
  j := length(s) mod 4 + 1;
  for i := 1 to length(s) div 4 do  begin
    Move(s[j], d, 4);
    l := l xor d;
    Inc(j, 4);
  end{ of for }
  HashIt := 'O' + StrBase(Abs(l) mod Prime);
end;


procedure TScopeCol.FreeItem(Item : pointer);
begin
  with PMangleItem(Item)^ do  begin
    DisposeStr(Name);
    DisposeStr(HashedName);
    if ScopeCol <> nil then  Dispose(ScopeCol, Done);
  end;
  Dispose(PMangleItem(Item));
end;

procedure TScopeCol.Insert(Item : pointer);
var
  Index : integer;
begin
  if Search(KeyOf(Item), Index)
   then  AtPut(Index, Item)
   else  AtInsert(Index, Item);
end;

function TScopeCol.KeyOf(Item : pointer) : pointer;
begin
  KeyOf := PMangleItem(Item)^.Name;
end;

function TScopeCol.InsertIntrIdentifier(const Name : string; var Index : integer) : string;
var
  p : PMangleItem;
begin
  New(p);
  p^.Name := NewStr(Name);
  p^.HashedName := NewStr(Name);
  p^.ScopeCol := nil;
  Insert(p);
  Index := IndexOf(p);
  InsertIntrIdentifier := p^.Name^;
end;

function TScopeCol.InsertIdentifier(const Name : string; var Index : integer) : string;
var
  p : PMangleItem;
begin
  New(p);
  p^.Name := NewStr(Name);
  p^.HashedName := NewStr(HashIt(Name));
  p^.ScopeCol := nil;
  Insert(p);
  Index := IndexOf(p);
  InsertIdentifier := p^.HashedName^;
end;

function TScopeCol.AtHashedName(Index : integer) : string;
begin
  AtHashedName := PMangleItem(At(Index))^.HashedName^;
end;

function TScopeCol.AtScope(Index : integer) : PScopeCol;
begin
  if Index = -1
   then  AtScope := nil
   else  AtScope := PMangleItem(At(Index))^.ScopeCol;
end;

function TScopeCol.LastScope : PScopeCol;
begin
  LastScope := PMangleItem(At(Count-1))^.ScopeCol;
end;


procedure TSectionCol.FreeItem(Item : pointer);
begin
  Dispose(PSectionItem(Item));
end;


function UpStr(const s : string) : stringassembler;
asm
  push   ds
  cld
  lds    si,s
  les    di,@Result
  lodsb
  stosb
  xor    ah,ah
  xchg   ax,cx
  jcxz   @3
@1:
  lodsb
  cmp    al,'a'
  jb     @2
  cmp    al,'z'
  ja     @2
  sub    al,20H
@2:
  stosb
  loop   @1
@3:
  pop    ds
end;



procedure WriteProgress;
{* writes current file with current linenumber *}
begin
  write(#13, sourceDir+DirInfo.Name, ' (', yylineno-1, ')');
end;

procedure ExitHandler;  far;
begin
  ExitProc := ExitSave;
  if TextRec(yyoutput).Mode <> fmClosed then  begin
    WriteProgress;
    Close(yyoutput);
  end;
end;

procedure Halt1;
{* stop program, delete temporary files*}
begin
  {$I-}
  Close(yyoutput);
{$IFDEF DelTmpFiles}
  Erase(yyoutput);
  {$I+}
{$ENDIF}
  Halt(1);
end;

procedure commenteof;
begin
  WriteProgress;
  writeln('  unexpected EOF inside comment');
  Halt1;
end;

function IsClosed(var t : text) : Boolean;
begin
  IsClosed := TextRec(t).Mode = fmClosed;
end;

procedure PrintError(const s : string);
begin
  WriteProgress;
  writeln('  ', s);
end;

function is_keyword(const id : string; var token : integer) : Boolean;
const
  id_len = 18;
type
  Ident = string[id_len];
const
  (* table of Pascal keywords: *)
  no_of_keywords = 59;
  keyword : array [1..no_of_keywords] of Ident = (
    'ABSOLUTE', 'AND', 'ARRAY', 'ASM', 'ASSEMBLER', 'BEGIN', 'CASE', 'CONST',
    'CONSTRUCTOR', 'DESTRUCTOR', 'DIV', 'DO',
    'DOWNTO', 'ELSE', 'END', 'EXTERNAL', 'FAR', 'FILE', 'FOR', 'FORWARD',
    'FUNCTION',
    'GOTO', 'IF', 'IMPLEMENTATION', 'IN', 'INHERITED', 'INLINE', 'INTERFACE',
    'INTERRUPT',
    'LABEL', 'MOD', 'NEAR', 'NIL', 'NOT', 'OBJECT', 'OF', 'OR',
    'PACKED', 'PRIVATE', 'PROCEDURE', 'PROGRAM', 'PUBLIC',
    'RECORD', 'REPEAT', 'SET', 'SHL', 'SHR', 'STRING', 'THEN', 'TO', 'TYPE',
    'UNIT', 'UNTIL', 'USES', 'VAR', 'VIRTUAL', 'WHILE', 'WITH', 'XOR');
var m, n, k : integer;
begin
  m := 1; n := no_of_keywords;
  while m<=n do  begin
    k := m+(n-m) div 2;
    if id=keyword[k]
     then  begin
       is_keyword := true;
       token := k;
       Exit;
     end
     else  if id>keyword[k]
            then  m := k+1
            else  n := k-1
  end{ of while }
  is_keyword := false
end;


{$I PASLEX.PAS *}



function Scramble(FromFile, ToFile : FNameStr) : Boolean;
{* mangles a given file as much as possible *}
type
  RecordTypes = (rtNone, rtRecord, rtVariantRecord);
  PInteger = ^integer;
var
  LastScopeIndex : integer;
  Section : SectionTypes;
  DeclType : word;
  RecordType : RecordTypes;
  SectionStack : PSectionCol;
  ScopeStack : PCollection;
  CurrentScope : PScopeCol;
  WithPushes : integer;
  OpeningLevel : integer;
  ParenLevel : word;
  AssemblerSection : Boolean;
  ObjectImpl : Boolean;
  ConstantArray : Boolean;


  procedure PushScope(ps : PScopeCol);
  begin
    ScopeStack^.Insert(ps);
  end;

  function PopScope : PScopeCol;
  begin
    with ScopeStack^ do  begin
      PopScope := At(Count-1);
      AtDelete(Count-1);
    end;
  end;


  procedure PushSection(Section : SectionTypes);
  var
    p : PSectionItem;
  begin
    New(p);
    p^.Section := Section;
    p^.WithPushes := WithPushes;
    p^.OpeningLevel := OpeningLevel;
    p^.DeclType := DeclType;
    with SectionStack^ do
      AtInsert(Count, p);
  end;

  function PopSection : SectionTypes;
  var
    i : PInteger;
    p : PSectionItem;
  begin
    with SectionStack^ do
      p := At(Count-1);
    DeclType := p^.DeclType;
    OpeningLevel := p^.OpeningLevel;
    WithPushes := p^.WithPushes;
    PopSection := p^.Section;
    with SectionStack^ do
      AtFree(Count-1);
  end;


  function Encode(const s : string) : string;
  begin
  {* create new scope if necessary *}
    if CurrentScope = nil then  begin
      CurrentScope := New(PScopeCol, Init(20,10));
      with ScopeStack^ do
        PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
    end;

  {* add identifier to current scope *}
    Encode := CurrentScope^.InsertIdentifier(s, LastScopeIndex);

  {* make the current identifier the new scope *}
    PushScope(CurrentScope);
    CurrentScope := CurrentScope^.AtScope(LastScopeIndex);
  end;

  function Encode2(const s : string) : string;
  {* as Encode but without setting a new scope *}
  var
    Index : integer;
  begin
  {* create new scope if necessary *}
    if CurrentScope = nil then  begin
      CurrentScope := New(PScopeCol, Init(20,10));
      with ScopeStack^ do
        PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
    end;

  {* add identifier to current scope *}
    Encode2 := CurrentScope^.InsertIdentifier(s, Index);
  end;

  function EncodeNot2(const s : string) : string;
  {* as Encode but without setting a new scope and without encoding *}
  var
    Index : integer;
  begin
  {* create new scope if necessary *}
    if CurrentScope = nil then  begin
      CurrentScope := New(PScopeCol, Init(20,10));
      with ScopeStack^ do
        PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
    end;

  {* add identifier to current scope *}
    EncodeNot2 := CurrentScope^.InsertIntrIdentifier(s, Index);
  end;

  function Encode3(const s : string) : string;
  {* inserts identifier in last scope on stack, sets scope of inserted
     identifier equal to current scope *}

  var
    Index : integer;
  begin
    with ScopeStack^, PScopeCol(At(Count-1))^ do  begin
      Encode3 := InsertIdentifier(s, Index);
      PMangleItem(At(Index))^.ScopeCol := CurrentScope;
    end;
  end;

  function EncodeNot3(const s : string) : string;
  {* inserts identifier in last scope on stack, sets scope of inserted
     identifier equal to current scope, but don't encode *}

  var
    Index : integer;
  begin
    with ScopeStack^, PScopeCol(At(Count-1))^ do  begin
      EncodeNOT3 := InsertIntrIdentifier(s, Index);
      PMangleItem(At(Index))^.ScopeCol := CurrentScope;
    end;
  end;

  function EncodeNot(const s : string) : string;
  {* as Encode, but identifier is not mangled *}
  begin
  {* create new scope if necessary *}
    if CurrentScope = nil then  begin
      CurrentScope := New(PScopeCol, Init(20,10));
      with ScopeStack^ do
        PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
    end;

  {* add identifier to current scope *}
    CurrentScope^.InsertIntrIdentifier(s, LastScopeIndex);
    EncodeNot := s;

  {* make the current identifier the new scope *}
    PushScope(CurrentScope);
    CurrentScope := CurrentScope^.AtScope(LastScopeIndex);
  end;

  function GetScope(const s : string; var Index : integer) : PScopeCol;
  {* returns scope in which s was defined if exists *}

    function Containss(Item : PScopeCol) : Booleanfar;
    begin
      Containss := (Item <> nil) and (Item^.Search(@s, Index));
    end;

  begin
    if (CurrentScope <> nil) and CurrentScope^.Search(@s, Index)
     then  GetScope := CurrentScope
     else
     {* search in scopes on ScopeStack *}
       GetScope := ScopeStack^.LastThat(@Containss);
  end;

  function GiveEncodingFor(s : string) : string;
  {* DO NOT MAKE s a const string!!! *}
  { PRE -
    POST - contents of yytext is destroyed
  }

  var
    p,d : PScopeCol;
    e : string;
    Index : integer;
  begin
    if yylex = DOT
     then  begin
     {* a dot was used to select a different scope *}
       p := GetScope(s, Index);
       if p = nil
        then  begin     {* an unknown scope was selected *}
          e := s + '.';
          while (yylex = IDENTIFIER) do  begin
            e := e + yytext;
            if yylex = DOT
             then  e := e + '.'
             else  Break;
          end;
          yyless(0);
        end
        else  begin
          PushScope(CurrentScope);
          CurrentScope := p^.AtScope(Index);
          e := p^.AtHashedName(Index) + '.';
          while (yylex = IDENTIFIER) do  begin
            if CurrentScope = nil
             then  begin
               e := e + yytext;
(* why this source??? if nil you don't know anything it seems
               d := GetScope(yytext, Index);
               if d <> nil
                then  e := e + GiveEncodingFor(yytext)
                else  e := e + yytext;
*)

             end
             else  begin
               if CurrentScope^.Search(@yytext, Index)
                then  e := e + CurrentScope^.AtHashedName(Index)
                else  e := e + yytext;
             end;
            if yylex = DOT
             then  begin
               if CurrentScope <> nil then
                 if CurrentScope^.Count = 0
                  then  CurrentScope := nil
                  else  CurrentScope := CurrentScope^.AtScope(Index);
               e := e + '.';
             end
             else  break;
          end{ of while }
          yyless(0);
          CurrentScope := PopScope;
        end;
       GiveEncodingFor := e;
     end
     else  begin
       yyless(0);
       p := GetScope(s, Index);
       if p = nil
        then  GiveEncodingFor := s
        else  GiveEncodingFor := p^.AtHashedName(Index)
     end;
  end;





{$I ASMLEX.PAS}

var
  Buffer : array[1..1024] of char;
  GlobalSection : (Un, Intr, Impl);
  RightHand : Boolean;
  ObjectDecl : Boolean;
  Index : integer;           {* scratch variable *}
  ObjectName : string;
  i : integer;               {* scratch varaible *}
  Scope : PScopeCol;         {* scratch variable *}
  TypeDecl,
  AbsoluteParsed : Boolean;
  Paren : integer;
  LastRetVal : integer;      {* previous value of yyretval *}


  procedure HandleSemiColon;
  var
    i : integer;
    wp : integer;
  begin
    ConstantArray := FALSE;
    writeln(yyoutput, yytext);
    case GlobalSection of
      Intr : case Section of
               Decl : begin
                   if not ((RecordType = rtVariantRecord) and (LastRetVal = RPAREN)) then
                     RightHand := FALSE;
                   CurrentScope := PopScope;
                 end;
               Func, FunctionType : begin
                   CurrentScope := PopScope;
                   RightHand := FALSE;
                   Section := Decl;
                 end;
               FuncDecl : RightHand := FALSE;
             end; { of case }
      Impl : case Section of
               Decl : begin
                   if not ((RecordType = rtVariantRecord) and (LastRetVal = RPAREN)) then
                     RightHand := FALSE;
                   CurrentScope := PopScope;
                 end;
               Func : begin
                   if ObjectDecl then  begin
                      CurrentScope := PopScope;
                      Section := Decl;
                   end;
                 end;
               FunctionType : begin
                   CurrentScope := PopScope;
                   RightHand := FALSE;
                   Section := Decl;
                 end;
               FuncDecl : RightHand := FALSE;
               WithStatement : begin
                   wp := WithPushes;
                   for i := 0 to wp-1 do
                     CurrentScope := PopScope;
                   Section := PopSection;
                 end;
               Inlin : begin
                   Section := PopSection;
                   CurrentScope := PopScope;
                 end;
               LabelStatement : begin
                   RightHand := FALSE;
                   Section := Decl;
                 end;
             end; { of case }
    end; { of case }
  end{ of proc HandleSemiClon *}


  procedure ParseDeclaration;
  var
    i : integer;
    saveyytext : string;
  begin
    if RightHand
     then  begin
       if not AbsoluteParsed then  begin
         Scope := GetScope(yytext, Index);
         if Scope <> nil then  begin
         {* variable of mangled types should get the *}
         {* same scope as the mangled type *}
           if CurrentScope = nil then  begin
             CurrentScope := New(PScopeCol, Init(20,10));
             with ScopeStack^ do
               PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
           end;
           Scope := Scope^.AtScope(Index);
           if Scope <> nil then  begin
             for i := 0 to Scope^.Count-1 do  begin
               CurrentScope^.Insert(Scope^.At(i));
             end;
           end;
         end;
       end;
       write(yyoutput, GiveEncodingFor(yytext));
     end
     else  begin
       ObjectName := yytext;
       AbsoluteParsed := FALSE;

     {* check if constant array variable is specified *}
       if (DeclType = _CONST) and (not RightHand) then  begin
         saveyytext := yytext;
         if (yylex = COLON) or (yyretval = EQUAL)
          then  begin
            yyless(0);
            yytext := saveyytext;
          end
          else  begin
            saveyytext := yytext;
            write(yyoutput, GiveEncodingFor(saveyytext));
            yytext := saveyytext;
            yyless(0);
            ConstantArray := TRUE;
            Exit;
          end;
       end;

     {* if we are in the interface section, don't encode *}
       if GlobalSection = Intr
        then  write(yyoutput, EncodeNot(yytext))
        else
       {* encode the lefthand *}
         write(yyoutput, Encode(yytext));

     {* create new scope if COMMA detected, set ConstantRecord if COLON *}
       if yylex = COMMA then begin
         CurrentScope := New(PScopeCol, Init(20,10));
         with ScopeStack^ do
           PMangleItem(PScopeCol(At(Count-1))^.At(LastScopeIndex))^.ScopeCol := CurrentScope;
       end;
       yyless(0);
       if yyretval = COMMA then  begin
         repeat
           if yylex = COMMA
            then  begin
              write(yyoutput, ',');
              yylex;
              if GlobalSection = Intr
               then  write(yyoutput, EncodeNot3(yytext))
               else  write(yyoutput, Encode3(yytext));
            end
            else  break;
         until false;
         yyless(0);
       end;
     end;
  end; { of ParseDeclaration }


  procedure ParseFunctionDeclaration;
  begin
    if RightHand
     then  write(yyoutput, GiveEncodingFor(yytext))
     else  begin
       if (CurrentScope <> nil) and CurrentScope^.Search(@yytext, Index)
        then  begin
          repeat
            write(yyoutput, GiveEncodingFor(yytext));
            if yylex = COMMA
             then  begin
               write(yyoutput, ',');
               yylex;
             end
             else  break;
          until false;
          yyless(0);
        end
        else  begin
          repeat
            if GlobalSection = Intr
             then  write(yyoutput, EncodeNot2(yytext))
             else  write(yyoutput, Encode2(yytext));
            if yylex = COMMA
             then  begin
               writeln(yyoutput, ',');
               yylex;
             end
             else  break;
          until false;
          yyless(0);
        end;
     end;
  end; { of ParseFunctionDeclaration }



label l1;
begin
  Scramble := FALSE;

{* open inputfile *}
  FileMode := 0;             {* open inputfile in read-only mode *}
  Assign(yyinput, FromFile);
  Reset(yyinput);
  SetTextBuf(yyinput, Buffer, 1024);

{* open output file *}
  FileMode := 1;             {* open outputfile in write-only mode *}
  Assign(yyoutput, 'NUL');   {* depress output until implemenation section *}
  Rewrite(yyoutput);
  FileMode := 2;             {* restore filemode *}

{* initialize variables *}
  yylineno := 1;
  GlobalSection := Un;
  Section := None;
  SectionStack := New(PSectionCol, Init(50, 10));
  RecordType := rtNone;
  WithPushes := 0;
  OpeningLevel := -1;
  ParenLevel := 0;
  RightHand := FALSE;
  ObjectDecl := FALSE;
  ObjectImpl := FALSE;
  CurrentScope := New(PScopeCol, Init(200, 100));
  ScopeStack := New(PCollection, Init(100, 50));
  LastScopeIndex := -1;
  AssemblerSection := FALSE;
  TypeDecl := FALSE;
  AbsoluteParsed := FALSE;
  ConstantArray := FALSE;

{* check if this is a unit *}
  repeat
    case yylex of
      _UNIT : break;
      _PROGRAM : begin
          Close(yyinput);
          Close(yyoutput);
          writeln('This is a program. Mangler can only mangle units. File skipped.');
          Exit;
        end;
    end; { of case }
  until IsClosed(yyinput) or eof(yyinput);
  if IsClosed(yyinput) or eof(yyinput) then  begin
    writeln('File is not a unit. File skipped.');
    Exit;
  end;

{* mangle *}
  write(#13, FromFile, ' (', yylineno, ')');

  while not eof(yyinput) do  begin
    LastRetVal := yyretval;
    case yylex of
      IDENTIFIER : begin
          case Section of
            Decl : ParseDeclaration;
            FuncDecl : ParseFunctionDeclaration;
          else  writeln(yyoutput, GiveEncodingFor(yytext));