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

📄 uconditionalparser.pas

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

{Contains the class ~[link TConditionalParser] for parsing compiler
 commands/comments in pascal files. The main reason is to recognize conditional
 compiling and other compiler directives. }

interface

uses SysUtils,
     UBaseIdents, UCodeParser;


type


   { * *  ***  * * *  ***   TCompilerDirectivesParser   ***  * * *  ***  * *  }



  {The possible conditional commands.}
  TConditionalCompilingCommand = (
                                  //else part of an cccifdef, cccifndef,
                                  //cccifopt, cccif or cccelseif
                                  cccelse,

                                  //old conditional commands
                                  cccifdef, cccifndef, cccifopt,
                                  //end of an cccifdef, cccifndef or cccifopt
                                  cccendif,

                                  //new conditional command
                                  cccif,
                                  //else part of an cccif with another
                                  cccelseif,                       // condition
                                  //end of an cccif (and cccelseif or cccelse)
                                  cccifend);

  //the possible/expected commands
  TConditionalCompilingCommands = packed set of TConditionalCompilingCommand;


{$UNDEF ListTemplateItemIsObject}
{$UNDEF ListTemplateItemMayBeFreed}
{$UNDEF ListTemplateItemFreedByDefault}

  //the items for the template list to use
  TListTemplateListItem = TConditionalCompilingCommands;

  //forward declaration to be able to define an alias so the template can
  //reference itself
  TCCCStack = class;

  //alias for the list to be used by the implementations of the methods
  TListTemplate = TCCCStack;

{$INCLUDE ..\General\Templates\ListTemplate.inc}

  {A stack of conditional compiling commands, i.e. the list of nested
   conditional compiling commands the parser is currently in.
   The list is ended with an empty set ([]-terminated) in the stack. }
  TCCCStack

{$INCLUDE ..\General\Templates\ListTemplate.inc}







  {Extends its base class by the possibility to recognize conditional
   compiling and other compiler options. This is mostly done by overridding the
   method ~[link DoGetToken]. }
  TConditionalParser = class(TCodeParser)
  private
    //the stack of the expected conditional compiling commands
    FConditionalCompiling: TCCCStack;



    //the currently parsed $INCLUDEd file; opposite of ~[link FIncludedByFile]
    FIncludedFile: TConditionalParser;

    //the file that $INCLUDEd this file (for searching of identifiers, in
    //conditional compiling expressions); opposite of ~[link FIncludedFile]
    FIncludedByFile: TConditionalParser;
    //if a parser of an included file these are the defines used instead of
    //~[link FThisFile.Defines] because the file might be included
    //simultaneously by several files, each parser has to have its own working
    //copy of the defines, is nil when not included
    FDefinesIncluded: TDefines;




    //Returns the main parser including this file (even indirectly) or itself.
    function GetMainParser: TConditionalParser;


    //Evaluates the expression and returns it's boolean result/value.
    //Only simple expressions are parsable!
    function GetConditionalCompilingDecision(const Expression: String):
                                                                       Boolean;





    //Handles a compiler option.
    function HandleCompilerOption(Command: String): Boolean;

    //Handles the inclusion of a file.
    function HandleInclude(FileName: String): Boolean;




  protected

    //Checks if the "Extended Syntax" is enabled at the moment.
    function IsExtendedSyntaxEnabled: Boolean; override;


    //Returns the next token (after the current position).
    function DoGetToken(var Token: String): Boolean; override;

    //Adds position in the including file to the exception object.
    function InitExceptionObject(ExcpObj: Exception): Exception; override;
  public
    //Creates the parser object and the stack of compiler commands.
    constructor Create(TheFile: TPascalFile); override;
    //Frees the stack of compiler commands.
    destructor Destroy; override;




    property IncludedByFile: TConditionalParser read FIncludedByFile
                                                write FIncludedByFile;
//    property CurrentlyIncludedFile: TConditionalParser read FIncludedFile;
  end;




  //another test for this program
  //(and to show the sick syntax allowed by Delphi;
  // although I use this feature now extensively for templates)
  xTConditionalParser = TConditionalParser;




implementation

uses Windows,
     General,
     UFilePaths,
     UPascalConsts,
     UExtIdents,
     UTokenParser;






{$INCLUDE ..\General\Templates\ListTemplate.inc}






   { * *  ***  * * *  ***   TCompilerDirectivesParser   ***  * * *  ***  * *  }



//Mind the x before the name of the class! It's another test for this program
//(and to show the sick syntax allowed by Delphi;
// although I use this feature now extensively for templates).

{Creates the parser object and the stack of compiler commands.
~param TheFile the file to parse and save the parsed data to }
constructor xTConditionalParser.Create(TheFile: TPascalFile);
begin
 inherited Create(TheFile);           //create the parser object

 //create stack for conditional compiling commands
 FConditionalCompiling := TCCCStack.Create;
end;


{Frees the stack of compiler commands. }
destructor TConditionalParser.Destroy;
begin
 FDefinesIncluded.Free;               //free defines of included file

 FIncludedFile.Free;                  //free the currently parsed included file
 FConditionalCompiling.Free;          //free the conditional compiling commands

 inherited Destroy;                   //free the object
end;











{Returns the main parser including this file (even indirectly) or itself.
~result the main parser of the pascal file (unit etc.) }
function TConditionalParser.GetMainParser: TConditionalParser;
begin
 Result := Self;                             //start with this file
 while Assigned(Result.IncludedByFile) do    //if included
  Result := Result.IncludedByFile;             //get the file including it
end;








{Evaluates the constant expression and returns it's boolean value. The
 expressions can be used after $IF or $ELSEIF for conditional compiling (D6+).
 Only simple expressions are parsable:
 Boolean operators (not, xor, or, and) and
 relational operators (=, <>, <, <=, >, >=) and
 braces ((, )) are supported, no arithmetic operators, no address-operator and
 no "in", "is" or "as". Operator-priorities are adhered to.
 No built-in functions are supported (like: SizeOf, Ord, Succ, Sin, ...), but
 the special compiler functions DEFINED and DECLARED are supported
 Constants are only allowed to have one simple value, either a string, a number
 or another constant or True or False.~[br]
 This may be quite restrictive, but most times only really simple expression
 are used. Heavy expressions would be nice, but things like:
 "~[code SizeOf(Integer) = SizeOf(LongInt)]" are hard to parse, and it's hard
 to calculate the size and similar things.~[br]
 False is assumed for any expression that can't be parsed.
~param Expression the expression to evaluate
~result the boolean value of the expression }
function TConditionalParser.GetConditionalCompilingDecision(const Expression:
                                                              String): Boolean;
var      TheDefines   :TDefines;        //defines of current file to be used
         Parser       :TTokenParser;    //the parser of the expression
         Position     :TPosition;       //position of the expression


         //the priorities of the operators
type     TPriority = (
                      pAll,        //parse the expression (or up to next ")")
                      pRelational, //with operators higher than relational op.
                      pOrXor,      //with operators higher than "or/xor"
                      pAnd,        //with operators higher than "and"
                      pFactor      //parse factors (value and "not" factor)
                      {, pBrace});

         //a value in an expression
         TValue = record
                  //value, if it is a string (not allowed in variant part)
                  str: String;
                  //the type of the value
                  case kind: (
                              vkNone,         //no value set yet
                              vkUnknownIdent, //unknown identifier used
                              vkBoolean,      //boolean value set
                              vkNumber,       //number value set
                              vkString        //string value set
                              ) of
                    vkBoolean: (bool: Boolean); //value, if it is boolean
                    vkNumber:  (num: Double)    //value, if it is a number
                  end;


 {Evaluates an expression, calls itself recursively to obtain values for
  operators (operands) and subexpressions and braces.~[br]
  Well, this is a horror-function, but it tries simply to evaluate the
  expression with the incomplete information the parsers have. It works only on
  simple expressions and even there it needs lots of luck, but it should work
  at least in those cases.
 ~param NextToken in: the first token of the expression or '';
                  out: the token after the (sub-) expression or ''
 ~param Priority  the priority that operators have to be at least to be parsed
                  as part of the expression
 ~param Value     out: the value of the expression }
 procedure ParseExpression(var NextToken: String;
                           Priority: TPriority; var Value: TValue);
 var      Token          :String;      //a token in the expression
          Finished       :Boolean;     //if parsing the expression is finished
          LC             :String;      //lower case variant of the token
          Second         :TValue;      //the second operand of binary operators
          Compare        :Integer;     //result of an comparison
          Ident          :TIdentifier; //the referenced constant

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -