Press enter to see results or esc to cancel.

The Compilation Process

This is where the actual compilation starts. Turbo Pascal first clears and prepares some variables and data structures and then compiles the module (unit or program). If program was compiled then compiler checks for indirect references and calls a procedure to link the code and data and to create the executable file.

Const

  stProgram            = - 1;
  stUnit               =   0;
  stUnitInterface      =   1;
  stUnitImplementation =   2;

Procedure Compile;
Var CurrentUnitInstance: PUnitHeader;
begin
  SetErrorAddress (@CompilerError);
  PushedUnitLevel := 0;
  Saved_PushedUnitLevel := $FFFF;
  CurrentSourceFile := @EndOfFileStructure;
  FirstSourceFile := @EndOfFileStructure;
  TempBufferFreePositionOffset := Ofs (TempBuffer);
  If FPU then Include (InitialEnvironmentFlags,  enfCPU87);
  InitialUnitFlags := [];
  ClearCurrentModuleVarsAndCreateUnit;
  CompileModule;
  If SourceType >= stUnit then
    If IsLastUnitAlreadyLoaded (CurrentUnitInstance) then RemoveLastLoadedUnit;
  FindReferencedModules;
  If SourceType >= stUnit then
    begin
      ModuleStack := 0;
      ModuleHeapMin := 0;
      ModuleHeapMax := 0;
    end else Link;
end;

Before each module is compiled Turbo Pascal resets token data and module flags, opens source file, creates conditional defines and according to the first token it compiles the module either as a unit or as a program.

Procedure CompileModule;
Var SavedFirstSourceFile: PFileStructure;
begin
  TokenForSlash := Token_Slash;
  TokenForEqual := Token_Equal;
  ModuleUnitFlags := InitialUnitFlags;
  OpenSourceFile (StoreFileNameToFilesBlock (CurrentFileName + CurrentSourceFilePathLength,
                                                                 ufPascalSource), CurrentFileName);
  SetFileDateTime;
  SavedFirstSourceFile := FirstSourceFile;
  FirstSourceFile := CurrentSourceFile;
  CreateConditionalDefines;
  GetNextToken;
  If not (cmoCompileOnlyInterfaceUsedUnits in CompilerModeOptions) and (Token <> Token_UNIT) then CompileProgram
    else CompileUnit;
  FirstSourceFile := SavedFirstSourceFile;
  ReturnToPreviousSourceFile;
end;

If the program has optional Program name declaration it is processed and the name is stored into main symbol table, otherwise the default PROGRAM name is used. Next used units are processed and loaded, declarations are processed and main program block is compiled. At the end object files are imported, checks are made for undefined Forward and External declarations and symbol tables are joined and compacted.

Procedure CompileProgram;
begin
  SourceType := stProgram;
  If CheckAndGetNextToken (Token_PROGRAM) then
    begin
      ExpectIdentifier;
      CreateSymbolTableAndStoreMainModuleNameIdentifier;
      GetNextToken;
      If CheckAndGetNextToken (Token_LeftParenthesis) then
        begin
          Repeat
            ExpectTokenAndGetNext (Token_Identifier);
          until not CheckAndGetNextToken (Token_Comma);
          ExpectTokenAndGetNext (Token_RightParenthesis);
        end;
      ExpectTokenAndGetNext (Token_Semicolon);
    end else begin
               CopyStringToCurrentIdentifier ('PROGRAM');
               CreateSymbolTableAndStoreMainModuleNameIdentifier;
             end;
  SetModuleFlagsAndProcessUsedUnits;
  ProcessDeclarations;
  ProcessMainProgramBlock;
  CheckForPeriodAndModuleEnd;
  ImportObjectFiles;
  CheckForUndefined_FORWARD_Or_EXTERNAL (Ptr (SymbolTable [stProcedures].Segment, SizeOf (TProceduresBlockRecord)));
  JoinSymbolTablesAndCreateUnit;
  CopySegmentsOfSymbolTablesToUnitHeader;
end;

Units have Interface and implementation part. When used units are compiled only used units that are used in the Interface part (of those units that are compiled) are processed and compiled (if necessary). Used units and declarations are processed twice – in the interface and implementation section. The unit’s initialization part is optional. Once the unit is compiled the symbol tables are compacted and saved as unit file.

Procedure CompileUnit;
Var CurrentUnitInstance: PUnitHeader;
begin
  SourceType := stUnitInterface;
  ExpectTokenAndGetNext (Token_UNIT);
  ExpectIdentifier;
  CreateUnitIdentiferSymbolTableAndStoreMainUnitName;
  GetNextToken;
  ExpectTokenAndGetNext (Token_Semicolon);
  ExpectTokenAndGetNext (Token_INTERFACE);
  SetModuleFlagsAndProcessUsedUnits;
  If not (cmoCompileOnlyInterfaceUsedUnits in CompilerModeOptions) or not IsLastUnitAlreadyLoaded (CurrentUnitInstance) then
    begin
      ProcessDeclarations;
      CalculateIdentifiersChecksum;
      SourceType := stUnitImplementation;
      ExpectTokenAndGetNext (Token_IMPLEMENTATION);
      ProcessUsedUnits;
      ProcessDeclarations;
      If Token = Token_BEGIN then ProcessMainProgramBlock         { Initialization part }
        else ExpectTokenAndGetNext (Token_END);
      CheckForPeriodAndModuleEnd;
      ImportObjectFiles;
      CheckForUndefined_FORWARD_Or_EXTERNAL (Ptr (SymbolTable [stProcedures].Segment, 8));
      RemovePrivateIdentifiersFromUnit;
      JoinSymbolTablesAndCreateUnit;
      CreateUnitFile;
    end else JoinSymbolTablesAndCreateUnit;
  CopySegmentsOfSymbolTablesToUnitHeader;
end;

This procedure creates main symbol table and stores module name. If System unit is compiled it loads boot-strap symbol table SYSTEM.TPS.

Procedure CreateUnitIdentiferSymbolTableAndStoreMainUnitName;
begin
  If CompareIdentifierToWord (_System) then
    begin
      SystemUnitCompilation := True;
      StrPCopy (@Identifier, 'SYSTEM.TPS');
      FindFilePath (@Identifier, Dir_Unit or Ext_Original);
      ReadUnit (@Identifier);
      StoreMainModuleNameAndCreateProcedureRecord;
    end else CreateSymbolTableAndStoreMainModuleNameIdentifier;
end;

This procedure stores module name into symbol table and creates record for the main procedure (unit’s initialization or main program).

Procedure StoreMainModuleNameAndCreateProcedureRecord;
Var UnitIdentifierData: PUnitIdentifierData;
    UnitIdentifier: PIdentifier;
begin
  PUnitHeader (Ptr (MainSymbolTable.Segment, 0))^.UnitNameIdentifierOffset := MainSymbolTable.UsedSize;
  UnitIdentifierData := StoreNewIdentifierToSymbolTable (SizeOf (TUnitIdentifierData), UnitIdentifier);
  UnitIdentifier^.Token := Token_UnitIdentifier;
  UnitIdentifierData^.UnitSegment := Seg (UnitIdentifierData^);
  LastUnitIdentifierData := Ofs (UnitIdentifierData^);
  With PProceduresBlockRecord (IncreaseSymbolTable (stProcedures, SizeOf (TProceduresBlockRecord)))^ do
    begin
      OverlayedProcedureOffset := 0;
      prW2 := 0;
      ProgramCodeBlockRecordOffset := $FFFF;
      SizeOfConstants := $FFFF;
    end;
end;
Procedure CreateSymbolTableAndStoreMainModuleNameIdentifier;
begin
  With PUnitHeader (Ptr (MainSymbolTable.Segment, 0))^ do
    begin
      PublicIdentifiersListOffset := MainSymbolTable.NextRecordOffset;
      PrivateIdentifiersListOffset := MainSymbolTable.NextRecordOffset;
    end;
  CreateSymbolTable ($40);
  StoreMainModuleNameAndCreateProcedureRecord;
end;

Once the unit is compiled, private identifiers are removed from the main symbol table if local debugging symbols are not needed.

Procedure RemovePrivateIdentifiersFromUnit;
Var UnitHeader: PUnitHeader;
begin
  If not (LocalDebugSymbols in ModuleCompilerSwitches) then
    begin
      UnitHeader := Ptr (SymbolTable [stMain].Segment, 0);
      SymbolTable [stMain].UsedSize := UnitHeader^.PrivateIdentifiersListOffset;
      UnitHeader^.PrivateIdentifiersListOffset := UnitHeader^.PublicIdentifiersListOffset;
    end;
end;

This procedure expects period, checks if source file is last, writes compilation progress and creates source file record.

Procedure CheckForPeriodAndModuleEnd;
begin
  If Token <> Token_Period then Error (PeriodExpected);
  If CurrentSourceFile <> FirstSourceFile then Error (UnexpectedEndOfFile);
  WriteCompilationProgress (CurrentSourceFile);
  CreateSourceFilesRecord;
end;

This procedure checks all procedures and reports error if there is some undefined Forward or External procedure.

Procedure CheckForUndefined_FORWARD_Or_EXTERNAL (CurrentProcedureToCheck: PProceduresBlockRecord);
Var ProcedureIdentifier, UnitIdentifier: PIdentifier;
    ProcedureIdentifierData: PProcedureIdentifierData;
    ErrorIdentifier: PChar;
begin
  While CurrentProcedureToCheck <> SymbolTable [stProcedures].Ptr do
    begin
      If CurrentProcedureToCheck^.ProgramCodeBlockRecordOffset = $FFFF then
        begin
          ProcedureIdentifier := Ptr (SymbolTable [stMain].Segment, CurrentProcedureToCheck^.ProcIdentifierOffset);
          ErrorIdentifier := @CurrentIdentifier;
          ProcedureIdentifierData := Pointer (PChar (ProcedureIdentifier) + ProcedureIdentifier^.Name.Len + 4);
          If pfMethod in ProcedureIdentifierData^.Flags then
            begin
              UnitIdentifier := Ptr (SymbolTable [stMain].Segment, ProcedureIdentifierData^.OuterBlockProcedureIdentifier + 4);
              StrPCopy (ErrorIdentifier, UnitIdentifier^.Name.Str);
              Inc (ErrorIdentifier, UnitIdentifier^.Name.Len);
              ErrorIdentifier^ := '.';
              Inc (ErrorIdentifier);
            end;
          StrPCopy (ErrorIdentifier, ProcedureIdentifier^.Name.Str);
          If pfExternal in ProcedureIdentifierData^.Flags then FileError (@CurrentIdentifier, UndefinedExternal)
            else FileError (@CurrentIdentifier, UndefinedForward)
        end;
      Inc (CurrentProcedureToCheck);
    end;
end;

This procedure checks if referenced module references loaded module. If such module is found its address is stored in reference record otherwise error is reported.

Procedure FindReferencedModules;
Var UnitSegment, PossibleReferencedUnitSegment: Word;
    ReferencedUnitRecord: Word;
    UnitPtr, ReferencedUnit: PUnitHeader;
    PossibleReferencedUnitName: PString;
begin
  UnitSegment := LastLoadedUsedUnit;
  Repeat
    UnitPtr := Ptr (UnitSegment, 0);
    ReferencedUnitRecord := UnitPtr^.BlockOffset [stReferencedModules];
    While ReferencedUnitRecord <> UnitPtr^.BlockOffset [Succ (stReferencedModules)] do
      begin
        PossibleReferencedUnitSegment := LastLoadedUsedUnit;
        PossibleReferencedUnitName := @PReferencedModulesBlockRecord (Ptr (UnitSegment, ReferencedUnitRecord))^.UnitName;
        Repeat
          ReferencedUnit := Ptr (PossibleReferencedUnitSegment, 0);
          If IdentifiersEqual (@PIdentifier (Ptr (PossibleReferencedUnitSegment,
                                             ReferencedUnit^.UnitNameIdentifierOffset))^.Name.Str,
                               PossibleReferencedUnitName) then Break;
          PossibleReferencedUnitSegment := ReferencedUnit^.PreviousUnitSegment;
        until PossibleReferencedUnitSegment = 0;
        If PossibleReferencedUnitSegment = 0 then Error (InvalidIndirectReference);
        PReferencedModulesBlockRecord (Ptr (UnitSegment, ReferencedUnitRecord))^.ModuleSegment :=
                                                                                                PossibleReferencedUnitSegment;
        ReferencedUnitRecord := Ofs (PossibleReferencedUnitName^);
      end;
    UnitSegment := UnitPtr^.PreviousUnitSegment;
  until UnitSegment = 0;
end;