Processing Procedure Declarations
Turbo Pascal uses few procedures to process declaration of procedures and functions. The same code is also used to process methods, constructors and destructors. The first procedure processes header.
Procedure ProcessProcedureDeclaration;
Var IdentifierDataPtr: Pointer;
ProcedureIdentifierDataPtr: PProcedureIdentifierData absolute IdentifierDataPtr;
InlineCodeSize: Word;
TypePointer: PTypeDefinition;
NewIdentifier: PIdentifier;
IdentifierOffset: Word absolute NewIdentifier;
IdentifierToken: TToken;
Saved_TemporarySymbolTablePosition: Word;
ProcTypeDef: PTypeDefinition;
Flags: TProcedureFlagsSet;
Label SetProcedureFlags;
begin
SavedToken := Token;
ProcedureStartLineNumber := CurrentSourceFile^.LineCounter;
GetNextToken;
ExpectIdentifier;
If CurrentIdentifierDeclaredInCurrentScope (IdentifierOffset, IdentifierDataPtr, IdentifierToken) then
begin
If SourceType = stUnitInterface then Error (DuplicateIdentifier);
If IdentifierToken = Token_ProcedureIdentifier then
begin
If pfMethod in ProcedureIdentifierDataPtr^.Flags then Error (DuplicateIdentifier);
ProcessAlreadyDeclaredProcedure;
Exit;
end;
If IdentifierToken = Token_TypeIdentifier then
begin
TypePointer := PointerFromOffsets (PTypeIdentifierData (ProcedureIdentifierDataPtr)^.UnitTypeOffsets);
If TypePointer^.BaseType = btObject then
begin
GetNextToken;
ExpectTokenAndGetNext (TOKEN_Period);
ExpectIdentifier;
If not IsIdentifierInSymbolTable (Ptr (Seg (TypePointer^),
PObjectTypeDefinition (TypePointer)^.FieldsListOffset),
IdentifierToken,
IdentifierDataPtr,
IdentifierOffset) then
Error (MethodIdentifierExpected);
If IdentifierToken <> Token_ProcedureIdentifier then Error (MethodIdentifierExpected);
ProcessAlreadyDeclaredProcedure;
Exit;
end;
end;
Case SavedToken of
Token_CONSTRUCTOR,
Token_DESTRUCTOR: Error (ObjectTypeExpected);
else Error (DuplicateIdentifier);
end;
end;
Case SavedToken of
Token_CONSTRUCTOR,
Token_DESTRUCTOR: Error (ObjectTypeExpected);
end;
ProcedureIdentifierDataPtr := StoreCurrentIdentifierToSymbolTable (CurrentScopeIdentifierTableAddress, 10, NewIdentifier);
NewIdentifier^.Token := Token_ProcedureIdentifier;
GetNextToken;
Saved_TemporarySymbolTablePosition := SymbolTable [stTemporary].NextRecordOffset;
ProcTypeDef := ProcessProcedureHeader (SavedToken);
ExpectTokenAndGetNext (TOKEN_Semicolon);
If CheckAndGetNextToken (TOKEN_INLINE) then
begin
Process_INLINE (InlineCodeSize);
Include (ProcedureIdentifierDataPtr^.Flags, pfInline);
ProcedureIdentifierDataPtr^.ProceduresRecordOffset := InlineCodeSize;
ExpectTokenAndGetNext (TOKEN_Semicolon);
Exit;
end;
ProcedureIdentifierDataPtr^.LocalIdentifiersList := Saved_TemporarySymbolTablePosition;
CreateProcedureRecord (NewIdentifier, ProcedureIdentifierDataPtr);
ProcedureIdentifierDataPtr^.OuterBlockProcedureIdentifier := CurrentProcedureIdentifier;
If CurrentProcedureIdentifier = 0 then
begin
Flags := [pfInterrupt];
If CompareIdentifierToDireciveAndSkipSemicolon (_INTERRUPT) then GoTo SetProcedureFlags;
end;
Flags := [pfFar];
If CompareIdentifierToDireciveAndSkipSemicolon (_FAR) then GoTo SetProcedureFlags;
If SourceType <> stUnitInterface then
begin
Flags := [];
If CompareIdentifierToDireciveAndSkipSemicolon (_NEAR) then GoTo SetProcedureFlags;
If ForceFarCalls in StatementCompilerSwitches then Flags := [pfFar];
end;
SetProcedureFlags:
ProcedureIdentifierDataPtr^.Flags := ProcedureIdentifierDataPtr^.Flags + Flags;
If SourceType = stUnitInterface then Exit;
If CompareIdentifierToDireciveAndSkipSemicolon (_FORWARD) then Exit;
ProcessProcedureDeclaractionsAndProgramBlock;
end;
This procedure processes declarations and program block.
Procedure ProcessProcedureDeclaractionsAndProgramBlock;
Var Saved_PushedParametersSize, Saved_OffsetAfterLastParameter, Saved_FunctionResultNegativeSize, Saved_MaxStackFrameOffset,
SavedProcedureIdentifierDataOffset: Word;
SavedCurrentProcedureIdentifier, SavedProceduresNextRecordOffset: Word;
Saved_ProcedureStartLineNumber: Word;
begin
With ProcedureIdentifierDataPtr^ do
begin
If (CurrentProcedureIdentifier = 0) and CompareIdentifierToDirectiveAndGetNextToken (_EXTERNAL) then
begin
Include (Flags, pfExternal);
LocalIdentifiersList := 0;
ExpectTokenAndGetNext (TOKEN_Semicolon);
Exit;
end;
If CompareIdentifierToDireciveAndSkipSemicolon (_ASSEMBLER) then Include (Flags, pfAssembler);
Saved_PushedParametersSize := PushedParametersSize;
Saved_OffsetAfterLastParameter := OffsetAfterLastParameter;
Saved_FunctionResultNegativeSize := FunctionResultNegativeSize;
Saved_MaxStackFrameOffset := ProgramBlockMaxStackFrameOffset;
SavedProcedureIdentifierDataOffset := ProcedureIdentifierDataOffset;
SavedCurrentProcedureIdentifier := CurrentProcedureIdentifier;
SavedProceduresNextRecordOffset := SymbolTable [stProcedures].UsedSize;
CurrentProcedureIdentifier := IdentifierOffset;
ProcedureIdentifierDataOffset := Ofs (ProcedureIdentifierDataPtr^);
TemporaryStoredParameters := LocalIdentifiersList;
LocalIdentifiersList := SymbolTable [stMain].UsedSize;
With PProceduresBlockRecord (Ptr (SymbolTable [stProcedures].Segment, ProceduresRecordOffset))^ do
ProgramCodeBlockRecordOffset := $FFFE;
CreateSymbolTable (4);
CreateParametersAsLocalVariables;
Saved_ProcedureStartLineNumber := ProcedureStartLineNumber;
ProcessDeclarations;
ProcedureStartLineNumber := Saved_ProcedureStartLineNumber;
PProceduresBlockRecord (Ptr (SymbolTable [stProcedures].Segment,
ProceduresRecordOffset))^.SizeOfConstants := ProcessProgramBlock;
{ SymbolTable [stProcedures].Segment might change }
PProceduresBlockRecord (Ptr (SymbolTable [stProcedures].Segment,
ProceduresRecordOffset))^.ProgramCodeBlockRecordOffset := SymbolTable [stCodeBlocks].UsedSize;
CreateProgramCodeBlockRecord;
CreateTypedConstantsBlockRecord;
CheckForUndefined_FORWARD_Or_EXTERNAL (Ptr (SymbolTable [stProcedures].Segment, SavedProceduresNextRecordOffset));
PushedParametersSize := Saved_PushedParametersSize;
OffsetAfterLastParameter := Saved_OffsetAfterLastParameter;
FunctionResultNegativeSize := Saved_FunctionResultNegativeSize;
ProgramBlockMaxStackFrameOffset := Saved_MaxStackFrameOffset;
ProcedureIdentifierDataOffset := SavedProcedureIdentifierDataOffset;
CurrentProcedureIdentifier := SavedCurrentProcedureIdentifier;
If not (LocalDebugSymbols in ModuleCompilerSwitches) then
begin
SymbolTable [stMain].UsedSize := LocalIdentifiersList;
LocalIdentifiersList := 0;
end;
ExpectTokenAndGetNext (TOKEN_Semicolon);
end;
end;
This procedure creates parameters as local variables. For methods also the implicit parameter Self
is added.
Procedure CreateParametersAsLocalVariables;
Var ProcedureIdentifierData: PProcedureIdentifierData;
ProcedureParameterData: PProcedureParameterData absolute ProcedureIdentifierData;
ProcedureParameterDataOfs: Word absolute ProcedureParameterData;
AssemblerProcedure: Boolean;
CurrentParameterOffset: Integer;
Saved_TemporaryStoredParameters, Parameter, NumberOfParameters: Word;
SelfIdentifier: PIdentifier;
SelfIdentifierData: PVariableIdentifierData;
begin
ProcedureIdentifierData := Ptr (SymbolTable [stMain].Segment, ProcedureIdentifierDataOffset);
AssemblerProcedure := pfAssembler in ProcedureIdentifierData^.Flags;
PushedParametersSize := SizeOfPushedParameters (ProcedureIdentifierData, OffsetAfterLastParameter);
CurrentParameterOffset := OffsetAfterLastParameter;
FunctionResultNegativeSize := FunctionResultStackFrameSize;
ProgramBlockMaxStackFrameOffset := FunctionResultNegativeSize;
Saved_TemporaryStoredParameters := TemporaryStoredParameters;
NumberOfParameters := ProcedureIdentifierData^.ProcedureTypeDefinition.NumberOfParameters;
Inc (ProcedureParameterDataOfs, 24);
For Parameter := 1 to NumberOfParameters do
begin
CreateParameterAsLocalVariable;
Inc (ProcedureParameterData);
end;
ProcedureParameterDataOfs := ProcedureIdentifierDataOffset;
If pfMethod in ProcedureIdentifierData^.Flags then
begin
VariableData_Flags := [vfVar, vf1];
RecordTypeDefinitionOffset.TypeOffset := $0006;
RecordTypeDefinitionOffset.UnitIdentifierData := CurrentProcedureIdentifier;
VariableData_NextMemberOffset := 0;
GetTypeAndUnitIdentifierOffsets (Ptr (Seg (ProcedureIdentifierData^),
ProcedureIdentifierData^.OuterBlockProcedureIdentifier), CurrentVarUnitTypeOffsets);
CopyStringToCurrentIdentifier ('Self');
SelfIdentifierData := StoreNewIdentifierToSymbolTable (11, SelfIdentifier);
SelfIdentifier^.Token := Token_VariableIdentifier;
Move (VariableData_Flags, SelfIdentifierData^, 11);
end;
If TemporaryStoredParameters = SymbolTable [stTemporary].UsedSize then
SymbolTable [stTemporary].UsedSize := Saved_TemporaryStoredParameters;
end;
Function FunctionResultStackFrameSize: Integer;
Var ResultSize: Integer;
begin
ResultSize := 0;
With ProcedureIdentifierData^ do
If (ProcedureTypeDefinition.ResultTypeOffset.UnitIdentifierData <> 0) and not (pfAssembler in Flags) then
With PTypeDefinition (PointerFromOffsets (ProcedureIdentifierData^.ProcedureTypeDefinition.ResultTypeOffset))^ do
If BaseType <> btString then Dec (ResultSize, Size);
FunctionResultStackFrameSize := ResultSize;
end;
Procedure CreateParameterAsLocalVariable;
Var ParameterTypeDef: PTypeDefinition;
ArrayTypeDefinition: PArrayTypeDefinition;
ProcedureParameterVarFlags: TVarFlagsSet;
ValueParameterCopySize, StackFrameSizeOfPassedParameter: Word;
ParameterIdentifier: PIdentifier;
ParameterIdentifierData: PVariableIdentifierData;
Offset: Integer;
begin
ParameterTypeDef := PointerFromOffsets (ProcedureParameterData^.UnitTypeOffsets);
ProcedureParameterVarFlags := ProcedureParameterData^.VarFlags;
If vfArray in ProcedureParameterVarFlags then
begin
ArrayTypeDefinition := CreateTypeDefinition (16, 0, [], btArray);
With ArrayTypeDefinition^ do
begin
GetTypeAndUnitIdentifierOffsets (ParameterTypeDef, ElementTypeOffset);
GetTypeAndUnitIdentifierOffsets (Ptr (SystemUnitSegment, Word_TypeOffset), IndexTypeOffset);
end;
ParameterTypeDef := ArrayTypeDefinition;
end;
StackFrameSizeOfPassedParameter :=
SizeOfPassedParameter (ParameterTypeDef, ProcedureParameterVarFlags, ValueParameterCopySize, AssemblerProcedure);
Include (ProcedureParameterVarFlags, vfArray);
VariableData_Flags := ProcedureParameterVarFlags;
GetTypeAndUnitIdentifierOffsets (ParameterTypeDef, CurrentVarUnitTypeOffsets);
Dec (CurrentParameterOffset, StackFrameSizeOfPassedParameter);
If ValueParameterCopySize <> 0 then
begin
Offset := ProgramBlockMaxStackFrameOffset - ValueParameterCopySize;
If (WordAlignment in ModuleCompilerSwitches) and (ValueParameterCopySize <> 1) then Offset := Offset and $FFFE;
ProgramBlockMaxStackFrameOffset := Offset;
end else begin
Offset := CurrentParameterOffset;
If vfOpenParameter in VariableData_Flags then Inc (Offset, 2);
end;
RecordTypeDefinitionOffset.TypeOffset := Offset;
RecordTypeDefinitionOffset.UnitIdentifierData := CurrentProcedureIdentifier;
VariableData_NextMemberOffset := 0;
CopyStringFromTemporaryBlockToCurrentIdentifier (TemporaryStoredParameters);
Inc (TemporaryStoredParameters, Length (CurrentIdentifier) + 3);
ParameterIdentifierData := StoreNewIdentifierToSymbolTable (11, ParameterIdentifier);
ParameterIdentifier^.Token := Token_VariableIdentifier;
Move (VariableData_Flags, ParameterIdentifierData^, 11);
end;
Procedures can be declared in the Interface
part of the unit, in the Object
declaration or with the Forward
directive. Such cases are handled with this procedure. Of course, header must match previous declaration.
Procedure ProcessAlreadyDeclaredProcedure;
Var SavedTempSymbolTableCurrentPointerOfs: Word;
ProcTypeDefAndParametersSize: Word;
N, NewTempSymbolTableCurrentPointerOfs: Word;
ExpectedToken: TToken;
DIPtr, SIPtr: PChar;
ProceduresRecord: PProceduresBlockRecord;
begin
ProceduresRecord := Ptr (SymbolTable [stProcedures].Segment, ProcedureIdentifierDataPtr^.ProceduresRecordOffset);
If ProceduresRecord^.ProgramCodeBlockRecordOffset <> $FFFF then Error (DuplicateIdentifier);
GetNextToken;
If Word (Ptr (Seg (ProcedureIdentifierDataPtr^), Ofs (ProcedureIdentifierDataPtr^) + 18)^) = 0 then
begin
If pfConstructor in ProcedureIdentifierDataPtr^.Flags then ExpectedToken := Token_CONSTRUCTOR else
If pfDestructor in ProcedureIdentifierDataPtr^.Flags then ExpectedToken := Token_DESTRUCTOR else
ExpectedToken := Token_PROCEDURE;
end else ExpectedToken := Token_FUNCTION;
If SavedToken <> ExpectedToken then Error (HeaderDoesNotMatchPreviousDefinition);
If (Token = Token_LeftParenthesis) or (Token = Token_Colon) then
begin
SavedTempSymbolTableCurrentPointerOfs := SymbolTable [stTemporary].NextRecordOffset;
ProcTypeDef := ProcessProcedureHeader (SavedToken);
ProcTypeDefAndParametersSize := SymbolTable [stMain].UsedSize - Ofs (ProcTypeDef^);
SymbolTable [stMain].UsedSize := Ofs (ProcTypeDef^);
ProcTypeDef^.Size := ProcedureIdentifierDataPtr^.ProcedureTypeDefinition.Size;
ProcTypeDef^.W06_ := ProcedureIdentifierDataPtr^.ProcedureTypeDefinition.W06_;
For N := 0 to ProcTypeDefAndParametersSize - 1 do
If PChar (ProcTypeDef)^ <> PChar (@ProcedureIdentifierDataPtr^.ProcedureTypeDefinition)^ then
Error (HeaderDoesNotMatchPreviousDefinition);
NewTempSymbolTableCurrentPointerOfs := SymbolTable [stTemporary].UsedSize;
SymbolTable [stTemporary].UsedSize := SavedTempSymbolTableCurrentPointerOfs;
DIPtr := Ptr (SymbolTable [stTemporary].Segment, ProcedureIdentifierDataPtr^.LocalIdentifiersList);
SIPtr := Ptr (SymbolTable [stTemporary].Segment, SavedTempSymbolTableCurrentPointerOfs);
While Ofs (SIPtr^) <> NewTempSymbolTableCurrentPointerOfs do
begin
Inc (SIPtr, 2);
Inc (DIPtr, 2);
If not IdentifiersEqual (PString (DIPtr), PString (SIPtr)) then Error (HeaderDoesNotMatchPreviousDefinition);
Inc (DIPtr, Length (PString (DIPtr)^) + 1);
end;
end;
ExpectTokenAndGetNext (TOKEN_Semicolon);
ProcessProcedureDeclaractionsAndProgramBlock;
end;