📄 uconditionalparser.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 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 + -