⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usinglefileparser.pas

📁 DelphiDoc is a program for automatic generation of documentation on a Delphi-Project. At the momen
💻 PAS
📖 第 1 页 / 共 2 页
字号:
{  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 + -