📄 uidentparser.pas
字号:
ExcpObj := inherited InitExceptionObject(ExcpObj);
if ExcpObj is EParseException then
begin
if not assigned(EParseException(ExcpObj).TheFile) then
EParseException(ExcpObj).TheFile := FThisFile;
if not assigned(EParseException(ExcpObj).EffectiveFile) then
EParseException(ExcpObj).EffectiveFile := FThisFile;
end;
Result := ExcpObj;
end;
{Returns the positions of the last returned token.
~result the positions of the last returned token }
function TIdentifierParser.GetLastTokenPositions: TTokenPosition;
begin
Result := inherited GetLastTokenPositions; //get the positions
if not assigned(Result.FEffectiveFile) then //read from this file?
Result.FEffectiveFile := FThisFile; //set this file
end;
{Assigns the position of the last token to the identifier.
~param Identifier the identifier to assign the position to
~param AlsoForwardPos if it is the forward position or only the
implementation }
procedure TIdentifierParser.SetPosition(Identifier: TIdentifier;
AlsoForwardPos: Boolean = True);
var Positions :TTokenPosition; //the position of the last token
begin
Positions := GetLastTokenPositions; //get positions of the last token
Identifier.InFile := FThisFile; //assign position in this file
Identifier.Position := Positions.FPosition;
Identifier.EffectiveFile := Positions.FEffectiveFile; //and effective position
Identifier.EffectivePosition := Positions.FEffectivePosition;
if AlsoForwardPos then //assign also forward positions?
begin
//assign the position of the forward declaration
Identifier.ForwardDefPos := Positions.FPosition;
//assign effective position
Identifier.EffectiveForwardFile := Positions.FEffectiveFile;
Identifier.EffectiveForwardPosition := Positions.FEffectivePosition;
end;
end;
{Assigns the position to the identifier.
~param Identifier the identifier to assign the position to
~param Pos the position to assign
~param AlsoForwardPos if it is the forward position or only the
implementation }
procedure TIdentifierParser.SetOldPosition(Identifier: TIdentifier;
const Pos: TTokenPosition;
AlsoForwardPos: Boolean = True);
begin
Identifier.InFile := FThisFile; //assign position in this file
Identifier.Position := Pos.FPosition;
Identifier.EffectiveFile := Pos.FEffectiveFile; //assign effective position
Identifier.EffectivePosition := Pos.FEffectivePosition;
if AlsoForwardPos then //assign also forward position?
begin
//assign the position of the forward declaration
Identifier.ForwardDefPos := Pos.FPosition;
//assign effective position
Identifier.EffectiveForwardFile := Pos.FEffectiveFile;
Identifier.EffectiveForwardPosition := Pos.FEffectivePosition;
end;
end;
{Skips the next token if it is a semicolon.
~result if the token was a semicolon }
function TIdentifierParser.SkipSemicolon: Boolean;
var Token :String; //the following token
begin
PushPosition; //save position in case of no semicolon
Result := GetToken(Token) and (Token = ';'); //test if it is a semicolon
if Result then
LosePosition //delete saved position
else
PopPosition; //restore position
end;
{Raises an exception, if it is not a valid name.
~param Name the name to check
~param Kind additional hint on the kind of the identifier with the invalid
name }
procedure TIdentifierParser.CheckIdentifierName(const Name: String;
const Kind: String = '');
var AddKind :String; //string to add for the kind
begin
if not IsValidIdentifierName(Name) then //is not a valid string for a name?
begin //that's an error!
if Kind <> '' then //get stirng to add for the kind
AddKind := ' (' + Kind + ')'
else
AddKind := '';
ExceptionFmt(etSyntax, 'Invalid string as a name "%s"%s!', [Name, AddKind]);
end;
end;
{Reads portability directives.
~param Token in: first Token to check for portability directives or '';
out: the next token after the portability directives or ''
~result the read issues }
function TIdentifierParser.ReadPortabilityIssues(var Token: String):
TIdentPortabilities;
var Tok :String; //a token
Index :Integer; //index of the directive
begin
Tok := Token;
if (Tok = '') and not GetToken(Tok) then //make sure one token is read
Exception(etSyntax,
'Unexpected end of file while reading portability directives!');
Result := [];
while IsWordIn(Tok, IdentPortability, Index) do //while directive available
begin
Include(Result, TIdentPortability(Index)); //add directive
if not GetToken(Tok) then //read next token
Exception(etSyntax,
'Unexpected end of file while reading portability directives!');
end;
Token := Tok; //return the last token
end;
{Reads portability directives of this file.
~result the next token after the portability directives or '' }
function TIdentifierParser.ReadOwnPortabilityIssues: String;
begin
Result := '';
FThisFile.Portability := FThisFile.Portability +
ReadPortabilityIssues(Result);
end;
{Will be called for every pointer and class reference type in the just ended
block of type definitions and checks for forward references.
~param Ident an identifier that names a named type
~param Parent the identifier that directly contains it
~param Data this parameter is not used and must be nil }
procedure TIdentifierParser.CallBackIdentTypeBlockEnd(Ident: TIdentType;
Parent: TIdentifier;
Data: TIdentifier);
var Identifier :TIdentifier; //identifier of the type
begin
assert(not assigned(Data));
assert(not assigned(Ident.TheType));
//for every pointer and class reference-type that doesn't reference
if ((Parent is TPointerType) or (Parent is TClassReferenceType)) and
(pos('.', Ident.DefIdent) = 0) then //an identifier in another unit
begin
//search identifier in the same scope (file/function)
Identifier := FActualIdents.GetIdentByName(Ident.DefIdent);
if assigned(Identifier) then //referenced identifier found?
begin
if not (Identifier is TType) then
ExceptionIdentFmt(Ident, etSyntax,
'Defined pointer-type/class-reference not on a type-identifier, "%s"!',
[Ident.DefIdent]);
if (Parent is TClassReferenceType) and
not (Identifier is TIdentType) and
(not (Identifier is TRecordType) or
(TRecordType(Identifier).Kind <> rkClass)) then
ExceptionIdentFmt(Ident, etSyntax,
'Class-reference not on a class, "%s"!',
[Ident.DefIdent]);
Ident.TheType := TType(Identifier); //save the found identifier
end;
end;
end;
{Will be called when a block of type definitions has been ended. For every
new pointer and class reference will be checked it is a forward reference to
an identifier in the same block. In the case it's referencing an identifier in
the same block the identifiers will be linked.
~param StartIndex the index of the first defined identifier in the just ended
block of type definitions }
procedure TIdentifierParser.TypeBlockHasEnded(StartIndex: Integer);
var i :Integer; //counter through all identifiers of the block
begin
//for all identifiers of the block check for forward references
for i := StartIndex to FActualIdents.Count - 1 do
FActualIdents[i].ForEachIdentType(CallBackIdentTypeBlockEnd, nil);
end;
{Parses the list of ancestors/parents of a class or interface.
~param TheRec the record-like type from which the list of ancestors should
be parsed
~param Ancestors the string of the definitions of the ancestors, this means the
parent and all implemented interfaces }
procedure TIdentifierParser.ParseRecordLikeParents(TheRec: TRecordType;
Ancestors: String);
var i :Integer; //index in the strings
S :String; //string to split the parent-list
ImplInterf :TIdentType; //an interface implemented by the class
begin
assert(not assigned(TheRec.IdentParent));
assert(TheRec.Implementing.IsEmpty);
//valid type for ancestors?
if not (TheRec.Kind in RecordKindCanInherit) then
ExceptionIdentFmt(TheRec, etSyntax,
'%s with a parent!', [RecordKindNames[TheRec.Kind]]);
S := Ancestors; //extract parent
i := pos(',', S);
if i <> 0 then
begin
S := copy(S, 1, i - 1);
Delete(Ancestors, 1, i);
Ancestors := Trim(Ancestors); //remove parent from list of ancestors
end
else
Ancestors := '';
S := Trim(S);
i := pos('.', S);
if i <> 0 then //check name of parent
begin
assert(copy(S, i - 1, 3) = ' . ');
Delete(S, i, 2);
if pos('.', S) <> 0 then
ExceptionIdentFmt(TheRec, etSyntax, 'Parent-Name of %s %s invalid!',
[RecordKindNames[TheRec.Kind], TheRec.Name]);
S[i - 1] := '.';
if pos(' ', S) <> 0 then
ExceptionIdentFmt(TheRec, etSyntax, 'Parent-Name of %s %s invalid!',
[RecordKindNames[TheRec.Kind], TheRec.Name]);
end;
TheRec.IdentParent := TIdentType.Create; //add identifier for the parent
TheRec.IdentParent.DefIdent := S;
if Ancestors <> '' then //further ancestors given?
begin
if TheRec.Kind <> rkClass then //must be a class
ExceptionIdentFmt(TheRec, etSyntax, '%s with multiple parents!',
[RecordKindNames[TheRec.Kind]]);
repeat //for all implemented interfaces
i := pos(',', Ancestors);
if i <> 0 then //extract the next interface
begin
S := copy(Ancestors, 1, i - 1);
Delete(Ancestors, 1, i);
end
else
begin
S := Ancestors;
Ancestors := '';
end;
S := Trim(S);
i := pos('.', S); //check if name of interface valid?
if i <> 0 then
begin
assert(copy(S, i - 1, 3) = ' . ');
Delete(S, i, 2);
if pos('.', S) <> 0 then
ExceptionIdent(TheRec, etSyntax,
'Name of implemented interface invalid!');
S[i - 1] := '.';
if pos(' ', S) <> 0 then
ExceptionIdent(TheRec, etSyntax,
'Name of implemented interface invalid!');
end;
ImplInterf := TIdentType.Create; //create identifier for interface
try
ImplInterf.DefIdent := S; //add implemented interface
TheRec.Implementing.AddIdent(ImplInterf);
except
ImplInterf.Free;
raise;
end;
until Ancestors = ''; //until all interfaces parsed
end;
end;
{Parses a constant expression, like used in types (sub ranges with ..) and as
initialisation of variables and constants.
~param ExprTokens the tokens of the expression
(in: the first token and
out: all tokens of the expression)
~param EndDirectives if equal signs, "absolute", portability directives and
"name"/"index" are not part of the expression:
~[preformatted
var a: 4 = 5..(5 = 5); //correct
b: 4 = 5..5 = 5; //error: incompatible, boolean (..) and integer, i.e.:
// (4 = 5..5) = 5;]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -