📄 usinglefileparser.pas
字号:
{ JADD - Just Another DelphiDoc: Documentation from Delphi Source Code
Copyright (C) 2003-2008 Gerold Veith
This file is part of JADD - Just Another DelphiDoc.
DelphiDoc is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as
published by the Free Software Foundation.
DelphiDoc 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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
}
unit USingleFileParser;
{Contains the class ~[link TSingleFileParser]. The pascal data is treated as a
whole file in this class.
}
interface
uses Classes,
UBaseIdents, UExtIdents, UIdentParser;
type
{ * * * *** * * * *** TSingleFileParser *** * * * *** * * * }
//the state while parsing a pascal file
TParseState = (
//object created, nothing parsed so far
psUninitialized,
//the file has been read
// psFileRead,
//first uses clause read (but not parsed yet)
psInterfaceUsesClauseParsed,
//first uses clause parsed
psInterfaceUnitsParsed,
//interface of unit parsed (if it is a unit)
psInterfaceParsed,
//(second) uses clause parsed
psImplementationUnitsParsed,
//inheritance hierarchy of classes checked
psInterfaceClassHierarchyChecked,
//implementation of unit or of other files parsed
psImplementationParsed,
//final consistency checks passed
psFinalConsistency,
//cross references created
psCrossReferences,
//portability issues inherited (first pass)
psPortabilityIssuesInheritedFirst,
//portability issues inherited (second pass for enumeration
psPortabilityIssuesInheritedEnumeration); //items)
{Extends its base class to parse the pascal data as a file. First the kind of
the file and the first uses-clause should be parsed by ~[link
ParseFirstUsesClause]. Then the interface sections should be parsed by
calling ~[link ParseInterface] and after all files have been parsed ~[link
ParseImplementations] should be called to parse the actual code. }
TSingleFileParser = class(TIdentifierParser)
private
//state while parsing the file
FParseState: TParseState;
//if the state of the parsing should be checked (and an error be raised, if
FCheckParseState: Boolean; //order of parsing is not correct)
protected
//Parses the final section of a file (begin, initialization, finalization).
procedure ParseEndOfFileSections(FirstToken: String);
{Will be called before parsing the interface of a unit. It is overridden in
~[linkClass TFileParser] where it will add the predefined identifiers if
it is the unit System. }
procedure PreParseThisUnit; virtual; abstract;
//Reads a uses-clause if present.
procedure TestReadUsesClause(FilePart: TFilePart);
//Parses an exports-clause.
procedure ParseExportsClause;
//Parses the package file.
procedure DoParsePackage;
//Parses the content of the file (not packages).
procedure DoParseFile(const InterfacePart: Boolean);
public
//Parses the type of the file and the first uses-clause.
procedure ParseFirstUsesClause;
//Parses the interface if it is a unit.
procedure ParseInterface;
//Parses the implementation section of units or the contents of program or
//library files.
procedure ParseImplementations; virtual;
//Checks if the parser is in the correct state and raises an error if not.
procedure DoCheckParseState(DemandedState: TParseState);
property ParseState: TParseState read FParseState write FParseState;
property CheckParseState: Boolean read FCheckParseState
write FCheckParseState;
end;
implementation
uses SysUtils,
UPascalConsts,
UTokenParser;
{ * * * *** * * * *** TSingleFileParser *** * * * *** * * * }
{Parses the final section of a file (begin, initialization and finalization).
An identifier like a function is created for each section.
~param FirstToken the first token ('begin', 'initialization' or 'finalization')
}
procedure TSingleFileParser.ParseEndOfFileSections(FirstToken: String);
var Ident :TProgramMainFunction; //the identifiers of the sections
begin
FirstToken := LowerCase(FirstToken); //for faster comparison
if FirstToken = 'begin' then //main program?
Ident := TProgramMainFunction.Create //create identifier
else
begin
Ident := TUnitInitFinalFunction.Create; //create initialization ident
TUnitInitFinalFunction(Ident).IsInitialization := FirstToken =
'initialization';
end;
try
Ident.Name := FirstToken; //save other infos
SetPosition(Ident);
Ident.Scope := FScope;
if ParseDeclarationsAndBody(Ident) then //parse the section
begin //finalization section found?
assert((Ident is TUnitInitFinalFunction) and
TUnitInitFinalFunction(Ident).IsInitialization);
FActualIdents.AddIdent(Ident); //add initialization ident
Ident := nil;
Ident := TUnitInitFinalFunction.Create; //create finalization ident
TUnitInitFinalFunction(Ident).IsInitialization := False;
Ident.Name := 'finalization'; //save other infos
SetPosition(Ident);
Ident.Scope := FScope;
ParseDeclarationsAndBody(Ident); //parse finalization section
end;
FActualIdents.AddIdent(Ident); //add the section identifier
except
Ident.Free;
raise;
end;
end;
{Reads a uses-clause if present.
~param FilePart for which part of the file the uses-clause should be read }
procedure TSingleFileParser.TestReadUsesClause(FilePart: TFilePart);
var Token :String; //a token
Clause :String; //the whole uses clause
Both :TUsesClauses; //to assign the new string
begin
PushPosition; //save position in case no uses-clause follows
if GetToken(Token) and (LowerCase(Token) = 'uses') then //"uses" follows?
begin
LosePosition; //delete saved position
Clause := '';
while GetBalancedToken(Token) and (Token <> ';') do
//read list of used units
Clause := Clause + ' ' + Token;
Both := FThisFile.UsesClauses; //set/add the uses clause
Both[FilePart] := Both[FilePart] + ' ' + Clause;
FThisFile.UsesClauses := Both;
end
else
PopPosition; //restore old position
end;
{Parses an exports-clause. }
procedure TSingleFileParser.ParseExportsClause;
var Token :String; //a token
Identifier :TExportIdentifier; //the identifier for the export
ExportValue :String; //values of the directives to export
begin
repeat //parse all exported identifiers
if not GetIdentWithPointsToken(Token) then //get the identifier
Exception(etSyntax, 'Unexpected end in "exports"-clause!');
Identifier := TExportIdentifier.Create; //create the identifier
try
Identifier.Name := Token; //set its name
SetPosition(Identifier); //and the position
GetToken(Token); //get next token
if LowerCase(Token) = 'index' then //index defined for export?
begin
ExportValue := ''; //parse the index
Token := ParseConstantExpression(ExportValue,
[aedIndexName, aedResident]);
Identifier.ExportIndex := ExportValue;
if Token = '' then //and get the next token
GetToken(Token);
end;
if LowerCase(Token) = 'name' then //name defined for export?
begin
ExportValue := ''; //parse the name
Token := ParseConstantExpression(ExportValue, [aedResident]);
Identifier.ExportName := ExportValue;
if Token = '' then //and get the next token
GetToken(Token);
end;
if LowerCase(Token) = 'resident' then //export is resident?
begin
Identifier.Resident := True; //save this
GetToken(Token); //and get the next token
end;
FThisFile.Exporteds.AddIdent(Identifier); //add the identifier
except
Identifier.Free;
raise;
end;
until Token <> ','; //until no further export-entries
if Token <> ';' then //a semicolon hast to follow
Exception(etSyntax, '";" expected after exports statement, failed');
end;
{Parses the package file. The content of a project file of a package is quite
simple, only two clauses can be in it besides the package name and the final
end.: contains, equals uses, and the optional requires, that is
followed by a list of needed packages. }
procedure TSingleFileParser.DoParsePackage;
const TopWords: array[0..2] of String = //the possible words in the file
('contains', 'end', 'requires');
var Token :String; //a token
Index :Integer; //index in the const array TopWords
CommaFound :Boolean; //if a comma has been found correctly
SumUp :String; //concatenated tokens
BothUses :TUsesClauses; //to set the uses clause
begin
if not GetToken(Token) or not IsWordIn(Token, TopWords, Index) then
Exception(etSyntax,
'Unexpected end or invalid token after "package"-Statement!');
if Index = 2 then //requires must be the first token
begin
//in case some packages have been read from the .cfg/.conf file, clear list
FThisFile.RequiredPackages.Clear;
CommaFound := true; //everything fine so far, resume parsing
//while resume reading name of package and a valid new package found
while CommaFound and GetToken(Token) and IsValidIdentifierName(Token) do
begin
FThisFile.RequiredPackages.Append(Token); //save required package
CommaFound := GetToken(Token) and (Token = ','); //read comma or semicolon
end;
if (Token <> ';') or not GetToken(Token) or
not IsWordIn(Token, TopWords, Index) then
Exception(etSyntax,
'Unexpected end or invalid token after/in "requires"-statement!');
end;
if Index = 0 then //contains follows next
begin
SumUp := '';
while GetBalancedToken(Token) and (Token <> ';') do //save contained units
SumUp := SumUp + ' ' + Token;
BothUses := FThisFile.UsesClauses; //set contained units
BothUses[fpMain] := BothUses[fpMain] + ' ' + SumUp;
FThisFile.UsesClauses := BothUses;
if (Token <> ';') or not GetToken(Token) or
not IsWordIn(Token, TopWords, Index) then
Exception(etSyntax,
'Unexpected end or invalid token after in or/after "contains"-statement!');
end;
if Index <> 1 then //now the file must end with end.
Exception(etSyntax, 'Expected "end" at end of "package"-file!');
if not GetToken(Token) or (Token <> '.') then
Exception(etSyntax, 'Expected "." after "end" at end of "package"-file!');
end;
{Parses the content of the file (not packages).
~param InterfacePart if only the interface section of a unit should be parsed }
procedure TSingleFileParser.DoParseFile(const InterfacePart: Boolean);
//all reserved words, that can be found global in a file
const TopWords: array[0..17] of String =
('begin', 'class', 'const', 'constructor', 'destructor',
'end', 'exports', 'finalization', 'function', 'implementation',
'initialization', 'interface', 'procedure', 'resourcestring', 'threadvar',
'type', 'uses', 'var');
//all reserved words, that set the kind of following declarations
//7: const, resourcestring, threadvar, type, var
const TopWordsDeclType = [2, 13, 14, 15, 17];
//all reserved words, that start the declarations of functions
//4: class, function, procedure, constructor, destructor
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -