mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-01 00:09:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2673 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			2673 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|  ***************************************************************************
 | |
|  *                                                                         *
 | |
|  *   This source is free software; you can redistribute it and/or modify   *
 | |
|  *   it under the terms of the GNU General Public License as published by  *
 | |
|  *   the Free Software Foundation; either version 2 of the License, or     *
 | |
|  *   (at your option) any later version.                                   *
 | |
|  *                                                                         *
 | |
|  *   This code 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.                              *
 | |
|  *                                                                         *
 | |
|  *   A copy of the GNU General Public License is available on the World    *
 | |
|  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 | |
|  *   obtain it by writing to the Free Software Foundation,                 *
 | |
|  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 | |
|  *                                                                         *
 | |
|  ***************************************************************************
 | |
| 
 | |
|   Author: Mattias Gaertner
 | |
| 
 | |
|   Abstract:
 | |
|     A simple C parser.
 | |
|     
 | |
|   Predefined C macros:
 | |
|     __LINE__  current source file line number as decimal
 | |
|     __FILE__  current source filename
 | |
|     __DATE__  current date: Apr 21 1990 or Jan  1 2008  (note the space in front of the 1)
 | |
|     __TIME__  current time "hh:mm:ss"
 | |
|     __STDC__  1
 | |
|     
 | |
| 
 | |
| }
 | |
| //  Predefined gcc macros:
 | |
| //    __attribute__((packed))
 | |
| //      Examples:
 | |
| //        typedef struct {
 | |
| //             uint8_t b[6];
 | |
| //        } __attribute__((packed)) bdaddr_t;
 | |
| //        struct __attribute__((packed)) {
 | |
| //                typeof(*(ptr)) __v;
 | |
| //        } *__p = (void *) (ptr);
 | |
| 
 | |
| //union _GFloatIEEE754
 | |
| //{
 | |
| //  gfloat v_float;
 | |
| //  struct {
 | |
| //    guint mantissa : 23;
 | |
| //    guint biased_exponent : 8;
 | |
| //    guint sign : 1;
 | |
| //  } mpn;
 | |
| //};
 | |
| 
 | |
| //ToDo:
 | |
| 
 | |
| // void * __strong _reserved0;
 | |
| 
 | |
| //struct {
 | |
| //       NSUInteger _isEmpty:1;
 | |
| //       NSUInteger _hasSingleRange:1;
 | |
| //       NSUInteger _cacheValid:1;
 | |
| //       NSUInteger _reservedArrayBinderController:29;
 | |
| //   } _indexSetFlags;
 | |
| //
 | |
| //_indexSetFlags: bitpacked record
 | |
| //  _isEmty: 0..1;
 | |
| //  _hasSingleRange: 0..1;
 | |
| //  _cacheValid: 0..1;
 | |
| //  _reservedArrayBinderController: 0..((1 shl 29)-1);
 | |
| //end;
 | |
| 
 | |
| //union {
 | |
| //       struct {
 | |
| //           NSRange _range;
 | |
| //       } _singleRange;
 | |
| //       struct {
 | |
| //           void *__strong _data;
 | |
| //           void *_reserved;
 | |
| //       } _multipleRanges;
 | |
| //   } _internal;
 | |
| //
 | |
| //_internal: record
 | |
| //  case byte of
 | |
| //    0: (_singleRange:
 | |
| //          record
 | |
| //            _range: NSRange;
 | |
| //          end;
 | |
| //       );
 | |
| //    1: (_multipleRanges:
 | |
| //          record
 | |
| //            _data: pointer;
 | |
| //            _reserved: pointer;
 | |
| //          end;
 | |
| //       );
 | |
| //end;
 | |
| 
 | |
| unit CCodeParserTool;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| {$I codetools.inc}
 | |
| 
 | |
| {off $DEFINE VerboseCCodeParser}
 | |
| {off $DEFINE VerboseCDirectives}
 | |
| 
 | |
| uses
 | |
|   {$IFDEF MEM_CHECK}
 | |
|   MemCheck,
 | |
|   {$ENDIF}
 | |
|   // RTL + FCL
 | |
|   Classes, SysUtils, Laz_AVL_Tree,
 | |
|   // CodeTools
 | |
|   FileProcs, BasicCodeTools, KeywordFuncLists, CodeCache, CodeTree,
 | |
|   NonPascalCodeTools, CodeToolsStructs,
 | |
|   // LazUtils
 | |
|   LazFileUtils, AvgLvlTree;
 | |
| 
 | |
| type
 | |
|   TCCodeNodeDesc = word;
 | |
| 
 | |
| const
 | |
|   // descriptors
 | |
|   ccnBase           = 1000;
 | |
|   ccnNone           =  0+ccnBase;
 | |
|   
 | |
|   ccnRoot           =  1+ccnBase;
 | |
|   ccnDirective      =  2+ccnBase;// e.g. "#define a" ,can be multiple lines, EndPos at line end
 | |
|   ccnExternBlock    =  3+ccnBase;// e.g. extern "C" {}
 | |
|   ccnEnumBlock      =  4+ccnBase;// e.g. enum {};
 | |
|   ccnEnumID         =  5+ccnBase;// e.g. name = value;
 | |
|   ccnConstant       =  6+ccnBase;// e.g. 1
 | |
|   ccnTypedef        =  7+ccnBase;// e.g. typedef int TInt;  last child is ccnName
 | |
|   ccnStruct         =  8+ccnBase;// e.g. struct{}, child is ccnTypeName or ccnTypeName+ccnSubDefs or ccnSubDefs
 | |
|   ccnUnion          =  9+ccnBase;// e.g. union{}
 | |
|   ccnDefinition     = 10+ccnBase;// e.g. variable: int i, type: struct typename {} or both: union typename {} varname
 | |
|   ccnFunction       = 11+ccnBase;// e.g. int i()
 | |
|   ccnName           = 12+ccnBase;// e.g. i
 | |
|   ccnTypeName       = 13+ccnBase;// e.g. i
 | |
|   ccnSubDefs        = 14+ccnBase;// e.g. the {} of a struct
 | |
|   ccnFuncParamList  = 15+ccnBase;// e.g. ()
 | |
|   ccnFuncParameter  = 16+ccnBase;// e.g. ()
 | |
|   ccnStatementBlock = 17+ccnBase;// e.g. {}
 | |
|   ccnBitCount       = 18+ccnBase;// e.g. int i:3
 | |
|   ccnExternDef      = 19+ccnBase;// e.g. extern int i;
 | |
| 
 | |
|   // values for Node.SubDesc
 | |
|   ccnsNone             =  0;
 | |
|   // values for Node.SubDesc if Node.Desc=ccnDirective
 | |
|   ccnsDirectiveIf      =  1;
 | |
|   ccnsDirectiveIfDef   =  2;
 | |
|   ccnsDirectiveIfNDef  =  3;
 | |
|   ccnsDirectiveElIf    =  4;
 | |
|   ccnsDirectiveElse    =  5;
 | |
|   ccnsDirectiveEndIf   =  6;
 | |
|   ccnsDirectiveDefine  =  7;
 | |
|   ccnsDirectiveUndef   =  8;
 | |
|   ccnsDirectiveInclude =  9;
 | |
|   ccnsDirectiveLine    = 10;
 | |
|   ccnsDirectiveError   = 11;
 | |
|   ccnsDirectivePragma  = 12;
 | |
| 
 | |
| type
 | |
|   TCCodeParserTool = class;
 | |
| 
 | |
|   { ECCodeParserException }
 | |
| 
 | |
|   ECCodeParserException = class(Exception)
 | |
|   public
 | |
|     Sender: TCCodeParserTool;
 | |
|     constructor Create(ASender: TCCodeParserTool; const AMessage: string);
 | |
|   end;
 | |
|   
 | |
|   TCCodeParserIfStackItem = record
 | |
|     StartPos: integer;
 | |
|   end;
 | |
|   PCCodeParserIfStackItem = ^TCCodeParserIfStackItem;
 | |
| 
 | |
|   { TCCodeParserTool }
 | |
| 
 | |
|   TCCodeParserTool = class
 | |
|   private
 | |
|     FChangeStep: integer;
 | |
|     FDefaultTokenList: TKeyWordFunctionList;
 | |
|     FIfStack: PCCodeParserIfStackItem;
 | |
|     FIfStackCapacity: integer;
 | |
| 
 | |
|     function OtherToken: boolean;
 | |
|     function DirectiveToken: boolean;
 | |
|     function ExternToken: boolean;
 | |
|     function CurlyBracketCloseToken: boolean;
 | |
|     procedure InitKeyWordList;
 | |
| 
 | |
|     procedure InitParser;
 | |
|     procedure CreateChildNode(Desc: TCCodeNodeDesc);
 | |
|     procedure EndChildNode;
 | |
|     procedure CloseNodes;
 | |
|     
 | |
|     procedure ReadDefinition(AsParameter: boolean);
 | |
|     procedure ReadParameterList;
 | |
|     procedure ReadEnum;
 | |
|     procedure ReadUnionStruct(IsStruct: boolean);
 | |
|     procedure ReadConstant;
 | |
|     procedure Read__attribute__;
 | |
|     
 | |
|     procedure IncIfLevel(StartPos: integer);
 | |
| 
 | |
|     procedure RaiseException(const AMessage: string; ReportPos: integer = 0);
 | |
|     procedure RaiseExpectedButAtomFound(const AToken: string; ReportPos: integer = 0);
 | |
|   public
 | |
|     Code: TCodeBuffer;
 | |
|     Src: string;
 | |
|     SrcLen: integer;
 | |
|     Tree: TCodeTree;
 | |
|     CurNode: TCodeTreeNode;
 | |
|     SrcPos: Integer;
 | |
|     AtomStart: integer;
 | |
|     IfLevel: integer;
 | |
|     ParseChangeStep: integer;// = Code.ChangeStep at the time of last Parse
 | |
| 
 | |
|     VisibleEditorLines: integer;
 | |
|     JumpCentered: boolean;
 | |
|     CursorBeyondEOL: boolean;
 | |
|     ParseDirectives: boolean;// default is true
 | |
| 
 | |
|     LastSrcPos: integer;
 | |
|     LastAtomStart: integer;
 | |
|     
 | |
|     LastErrorMsg: string;
 | |
|     LastErrorPos: integer;  // the position where the code does no make sense
 | |
|     LastErrorReportPos: integer; // if the position that gives a human a clue what went wrong
 | |
|                              // normally LastErrorReportPos=LastErrorPos
 | |
|                              // but if a closing bracket is missing LastErrorReportPos points
 | |
|                              // to ( and ErrorPos to next atom
 | |
| 
 | |
|     constructor Create;
 | |
|     destructor Destroy; override;
 | |
|     procedure Clear;
 | |
| 
 | |
|     procedure Parse;
 | |
|     procedure Parse(aCode: TCodeBuffer);
 | |
|     function UpdateNeeded: boolean;
 | |
| 
 | |
|     function FindDeepestNodeAtPos(P: integer;
 | |
|       ExceptionOnNotFound: boolean): TCodeTreeNode; inline;
 | |
|     function FindDeepestNodeAtPos(StartNode: TCodeTreeNode; P: integer;
 | |
|       ExceptionOnNotFound: boolean): TCodeTreeNode;
 | |
|     function CaretToCleanPos(Caret: TCodeXYPosition;
 | |
|         out CleanPos: integer): integer;  // 0=valid CleanPos
 | |
|                           //-1=CursorPos was skipped, CleanPos between two links
 | |
|                           // 1=CursorPos beyond scanned code
 | |
|                           //-2=X,Y beyond source
 | |
|     function CleanPosToCodePos(CleanPos: integer;
 | |
|         out CodePos:TCodePosition): boolean; // true=ok, false=invalid CleanPos
 | |
|     function CleanPosToCaret(CleanPos: integer;
 | |
|         out Caret:TCodeXYPosition): boolean; // true=ok, false=invalid CleanPos
 | |
|     function CleanPosToCaretAndTopLine(CleanPos: integer;
 | |
|         out Caret:TCodeXYPosition; out NewTopLine: integer): boolean; // true=ok, false=invalid CleanPos
 | |
|     function CleanPosToStr(CleanPos: integer): string;
 | |
|     function MainFilename: string;
 | |
| 
 | |
|     procedure MoveCursorToPos(p: integer);
 | |
|     procedure MoveCursorToNode(Node: TCodeTreeNode);
 | |
|     procedure ReadNextAtom;
 | |
|     procedure ReadNextAtomSkipDirectives;
 | |
|     procedure ReadRawNextAtom;
 | |
|     procedure UndoReadNextAtom;
 | |
|     function ReadTilBracketClose(ExceptionOnNotFound: boolean): boolean;
 | |
|     function AtomIs(const s: shortstring): boolean;
 | |
|     function AtomIsChar(const c: char): boolean;
 | |
|     function AtomIsCharOfSet(const s: shortstring): boolean;
 | |
|     function UpAtomIs(const s: shortstring): boolean;
 | |
|     function AtomIsIdentifier: boolean;
 | |
|     function AtomIsStringConstant: boolean;
 | |
|     function AtomIsNumber: boolean;
 | |
|     function GetAtom: string;
 | |
|     function LastAtomIs(const s: shortstring): boolean;
 | |
|     function GetLastAtom: string;
 | |
|     function ExtractCode(StartPos, EndPos: integer;
 | |
|                          WithDirectives: boolean = false): string;// extract code without comments
 | |
| 
 | |
|     function GetFirstNameNode(Node: TCodeTreeNode): TCodeTreeNode;
 | |
|     function ExtractName(NameNode: TCodeTreeNode): string;
 | |
|     function ExtractDefinitionName(VarNode: TCodeTreeNode): string;
 | |
|     function ExtractDefinitionType(VarNode: TCodeTreeNode;
 | |
|                                    WithDirectives: boolean = false): string;
 | |
|     function ExtractTypeDefType(TypeDefNode: TCodeTreeNode;
 | |
|                                 WithDirectives: boolean = false): string;
 | |
|     function ExtractFunctionName(FuncNode: TCodeTreeNode): string;
 | |
|     function GetFunctionParamListNode(Node: TCodeTreeNode): TCodeTreeNode;
 | |
|     function ExtractFunctionParamList(FuncNode: TCodeTreeNode): string;
 | |
|     function ExtractFunctionType(FuncNode: TCodeTreeNode;
 | |
|                                  WithDirectives: boolean = false): string;
 | |
|     function ExtractFunctionResultType(FuncNode: TCodeTreeNode;
 | |
|                                        WithDirectives: boolean = false;
 | |
|                                        WithBrackets: boolean = true): string;
 | |
|     function IsPointerToFunction(FuncNode: TCodeTreeNode): boolean;
 | |
|     function ExtractParameterName(ParamNode: TCodeTreeNode): string;
 | |
|     function ExtractParameterType(ParamNode: TCodeTreeNode;
 | |
|                                   WithDirectives: boolean = false): string;
 | |
|     function ExtractEnumBlockName(EnumBlockNode: TCodeTreeNode): string;
 | |
|     function ExtractEnumIDName(EnumIDNode: TCodeTreeNode): string;
 | |
|     function ExtractEnumIDValue(EnumIDNode: TCodeTreeNode;
 | |
|                                 WithDirectives: boolean = false): string;
 | |
|     function ExtractStructName(StructNode: TCodeTreeNode): string;
 | |
|     function ExtractUnionName(UnionNode: TCodeTreeNode): string;
 | |
|     function ExtractTypedefName(TypedefNode: TCodeTreeNode): string;
 | |
|     function IsDirective(DirectiveNode: TCodeTreeNode; const Action: shortstring): boolean;
 | |
|     function ExtractDirectiveAction(DirectiveNode: TCodeTreeNode): string;
 | |
|     function ExtractDirectiveFirstAtom(DirectiveNode: TCodeTreeNode): string;
 | |
|     function ExtractDirectiveParams(DirectiveNode: TCodeTreeNode): string;
 | |
|     function ExtractDefine(DefineNode: TCodeTreeNode;
 | |
|                            out MacroName, MacroParamList, MacroValue: string): boolean;
 | |
| 
 | |
|     function FindDirectiveBlockEnd(InnerNode: TCodeTreeNode): TCodeTreeNode;
 | |
|     function FindElseOrEndIf(IfOrElseNode: TCodeTreeNode): TCodeTreeNode;// find corresponding #EndIf or #Else or #ElIf
 | |
|     function FindEndIf(IfOrElseNode: TCodeTreeNode): TCodeTreeNode;// find corresponding #EndIf
 | |
|     function FindEnclosingIFNDEF: TCodeTreeNode;// finds the typical IFNDEF that encloses the whole header source
 | |
| 
 | |
|     procedure Replace(FromPos, ToPos: integer; const NewSrc: string);
 | |
| 
 | |
|     procedure IncreaseChangeStep;
 | |
|     procedure WriteDebugReport;
 | |
|     procedure CheckNodeTool(Node: TCodeTreeNode);
 | |
|     function NodeAsString(Node: TCodeTreeNode): string;
 | |
| 
 | |
|     property ChangeStep: integer read FChangeStep;
 | |
|   end;
 | |
| 
 | |
|   { TCHeaderFileMerger }
 | |
| 
 | |
|   TCHFileLink = record
 | |
|     MergedPos: integer;
 | |
|     Code: TCodeBuffer; // can be nil
 | |
|     SrcPos: integer;
 | |
|   end;
 | |
|   PCHFileLink = ^TCHFileLink;
 | |
| 
 | |
|   TCHFileMergeFlag = (
 | |
|     chfmfIgnoreIncludes, // do not merge at includes, but simply append
 | |
|     chfmfAll // merge all files, otherwise merge only the first
 | |
|     );
 | |
|   TCHFileMergeFlags = set of TCHFileMergeFlag;
 | |
| 
 | |
|   TCHeaderFileMerger = class
 | |
|   private
 | |
|     procedure AddLink(aMergedPos: integer; aCode: TCodeBuffer; aSrcPos: integer);
 | |
|   public
 | |
|     CombinedSource: TCodeBuffer;
 | |
|     LinkCount: integer;
 | |
|     Links: PCHFileLink;
 | |
|     LinksCapacity: integer;
 | |
|     Macros: TIdentStringToStringTree;
 | |
|     constructor Create;
 | |
|     destructor Destroy; override;
 | |
|     procedure Clear;
 | |
|     procedure Merge(SourceFiles: TStrings; CodeCache: TCodeCache;
 | |
|                     const Flags: TCHFileMergeFlags);
 | |
|     procedure Merge(SourceBuffers: TFPList { list of TCodeBuffer };
 | |
|                     const Flags: TCHFileMergeFlags);
 | |
|     function MergedPosToOriginal(MergedPos: integer;
 | |
|                           out Code: TCodeBuffer; out CodePos: integer): boolean;
 | |
|     function MergedPosToOriginal(MergedX, MergedY: integer;
 | |
|                      out Code: TCodeBuffer; out X, Y: integer): boolean;
 | |
|     function LinkIndexOfMergedPos(MergedPos: integer): integer;
 | |
|     function GetLinkLength(Index: integer): integer;
 | |
| 
 | |
|     procedure WriteDebugReport;
 | |
|   end;
 | |
| 
 | |
| function CCNodeDescAsString(Desc: TCCodeNodeDesc; SubDesc: TCCodeNodeDesc = 0): string;
 | |
| procedure InitCCodeKeyWordLists;
 | |
| 
 | |
| function GetNodeIndent(Node: TCodeTreeNode): string;
 | |
| 
 | |
| var
 | |
|   IsCCodeFunctionModifier: TKeyWordFunctionList = nil;
 | |
|   IsCCodeCustomOperator: TKeyWordFunctionList = nil;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| var
 | |
|   KeyWordLists: TFPList;
 | |
| 
 | |
| function CCNodeDescAsString(Desc: TCCodeNodeDesc; SubDesc: TCCodeNodeDesc): string;
 | |
| begin
 | |
|   case Desc of
 | |
|   ccnNone          : Result:='None';
 | |
|   ccnRoot          : Result:='Root';
 | |
|   ccnDirective     :
 | |
|     begin
 | |
|       Result:='Directive';
 | |
|       case SubDesc of
 | |
|       ccnsDirectiveIf      : Result:=Result+'.If';
 | |
|       ccnsDirectiveIfDef   : Result:=Result+'.IfDef';
 | |
|       ccnsDirectiveIfNDef  : Result:=Result+'.IfNDef';
 | |
|       ccnsDirectiveElIf    : Result:=Result+'.ElIf';
 | |
|       ccnsDirectiveElse    : Result:=Result+'.Else';
 | |
|       ccnsDirectiveEndIf   : Result:=Result+'.EndIf';
 | |
|       ccnsDirectiveDefine  : Result:=Result+'.Define';
 | |
|       ccnsDirectiveUndef   : Result:=Result+'.Undef';
 | |
|       ccnsDirectiveInclude : Result:=Result+'.Include';
 | |
|       ccnsDirectiveLine    : Result:=Result+'.Line';
 | |
|       ccnsDirectiveError   : Result:=Result+'.Error';
 | |
|       ccnsDirectivePragma  : Result:=Result+'.Pragma';
 | |
|       end;
 | |
|     end;
 | |
|   ccnExternBlock   : Result:='extern-block';
 | |
|   ccnEnumBlock     : Result:='enum-block';
 | |
|   ccnEnumID        : Result:='enum-ID';
 | |
|   ccnConstant      : Result:='constant';
 | |
|   ccnTypedef       : Result:='typedef';
 | |
|   ccnStruct        : Result:='struct';
 | |
|   ccnUnion         : Result:='union';
 | |
|   ccnDefinition    : Result:='definition(var/type/const)';
 | |
|   ccnFunction      : Result:='function';
 | |
|   ccnName          : Result:='name';
 | |
|   ccnTypeName      : Result:='type-name';
 | |
|   ccnSubDefs       : Result:='sub-defs';
 | |
|   ccnFuncParamList : Result:='function-param-list';
 | |
|   ccnFuncParameter : Result:='function-parameter';
 | |
|   ccnStatementBlock: Result:='statement-block';
 | |
|   ccnBitCount      : Result:='bit-count';
 | |
|   ccnExternDef     : Result:='extern-def';
 | |
|   else          Result:='?('+IntToStr(Desc)+')';
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure InitCCodeKeyWordLists;
 | |
| begin
 | |
|   if KeyWordLists<>nil then exit;
 | |
|   KeyWordLists:=TFPList.Create;
 | |
|   
 | |
|   IsCCodeFunctionModifier:=TKeyWordFunctionList.Create('IsCCodeFunctionModifier');
 | |
|   KeyWordLists.Add(IsCCodeFunctionModifier);
 | |
|   with IsCCodeFunctionModifier do begin
 | |
|     Add('static'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('inline'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|   end;
 | |
| 
 | |
|   IsCCodeCustomOperator:=TKeyWordFunctionList.Create('IsCCodeCustomOperator');
 | |
|   KeyWordLists.Add(IsCCodeCustomOperator);
 | |
|   with IsCCodeCustomOperator do begin
 | |
|     Add('+'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('-'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('*'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('/'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('|'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('&'     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('='     ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('++'    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('--'    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('+='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('-='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('*='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('/='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('&='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('|='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('=='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|     Add('!='    ,{$ifdef FPC}@{$endif}AllwaysTrue);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function GetNodeIndent(Node: TCodeTreeNode): string;
 | |
| begin
 | |
|   if Node=nil then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=GetIndentStr(Node.GetLevel*2);
 | |
| end;
 | |
| 
 | |
| { TCHeaderFileMerger }
 | |
| 
 | |
| procedure TCHeaderFileMerger.AddLink(aMergedPos: integer; aCode: TCodeBuffer;
 | |
|   aSrcPos: integer);
 | |
| begin
 | |
|   if LinkCount=LinksCapacity then begin
 | |
|     if LinksCapacity<4 then
 | |
|       LinksCapacity:=4
 | |
|     else
 | |
|       LinksCapacity:=LinksCapacity*2;
 | |
|     ReAllocMem(Links,LinksCapacity*SizeOf(TCHFileLink));
 | |
|   end;
 | |
|   with Links[LinkCount] do begin
 | |
|     MergedPos:=aMergedPos;
 | |
|     Code:=aCode;
 | |
|     SrcPos:=aSrcPos;
 | |
|   end;
 | |
|   inc(LinkCount);
 | |
| end;
 | |
| 
 | |
| constructor TCHeaderFileMerger.Create;
 | |
| begin
 | |
|   Macros:=TIdentStringToStringTree.Create(true);
 | |
| end;
 | |
| 
 | |
| destructor TCHeaderFileMerger.Destroy;
 | |
| begin
 | |
|   Clear;
 | |
|   FreeAndNil(Macros);
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TCHeaderFileMerger.Clear;
 | |
| begin
 | |
|   FreeAndNil(CombinedSource);
 | |
|   ReAllocMem(Links,0);
 | |
|   LinkCount:=0;
 | |
|   LinksCapacity:=0;
 | |
| end;
 | |
| 
 | |
| procedure TCHeaderFileMerger.Merge(SourceFiles: TStrings;
 | |
|   CodeCache: TCodeCache; const Flags: TCHFileMergeFlags);
 | |
| var
 | |
|   SourceBuffers: TFPList;
 | |
|   i: Integer;
 | |
|   Buf: TCodeBuffer;
 | |
| begin
 | |
|   Clear;
 | |
|   SourceBuffers:=TFPList.Create;
 | |
|   try
 | |
|     for i:=0 to SourceFiles.Count-1 do begin
 | |
|       Buf:=CodeCache.LoadFile(SourceFiles[i]);
 | |
|       if Buf=nil then
 | |
|         raise Exception.Create('TCHeaderFileCombine.Combine: ERROR loading file '+SourceFiles[i]);
 | |
|       SourceBuffers.Add(Buf);
 | |
|     end;
 | |
|     Merge(SourceBuffers,Flags);
 | |
|   finally
 | |
|     SourceBuffers.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCHeaderFileMerger.Merge(SourceBuffers: TFPList;
 | |
|   const Flags: TCHFileMergeFlags);
 | |
| var
 | |
|   MergedBuffers: TFPList; // list of TCodeBuffer
 | |
|   StrStream: TStringStream;
 | |
| 
 | |
|   procedure Append(Code: TCodeBuffer; FromPos, EndPos: integer);
 | |
| 
 | |
|     procedure Error(p: integer; const Msg: string);
 | |
|     var
 | |
|       Line: integer;
 | |
|       Column: integer;
 | |
|       s: String;
 | |
|     begin
 | |
|       Code.AbsoluteToLineCol(p,Line,Column);
 | |
|       s:=Code.Filename+'('+IntToStr(Line)+','+IntToStr(Column)+') Error: '+Msg;
 | |
|       raise Exception.Create(s);
 | |
|     end;
 | |
| 
 | |
|   var
 | |
|     StartP: PChar;
 | |
|     AtomStart: integer;
 | |
|     p: LongInt;
 | |
|     MacroValue: string;
 | |
|     MacroNode: TAVLTreeNode;
 | |
|     MacroItem: PStringToStringItem;
 | |
|     Src: String;
 | |
|     BracketLvl: Integer;
 | |
|   begin
 | |
|     if FromPos<1 then FromPos:=1;
 | |
|     if EndPos>Code.SourceLength then EndPos:=Code.SourceLength+1;
 | |
|     if (EndPos<=FromPos) or (FromPos>Code.SourceLength) then exit;
 | |
|     p:=FromPos;
 | |
|     Src:=Code.Source;
 | |
|     while p<EndPos do begin
 | |
|       ReadRawNextCAtom(Src,p,AtomStart);
 | |
|       if AtomStart>=EndPos then break;
 | |
|       StartP:=@Src[AtomStart];
 | |
|       if (StartP^='#') and IsFirstNonSpaceCharInLine(Code.Source,AtomStart) then
 | |
|       begin
 | |
|         // directive
 | |
|         ReadRawNextCAtom(Src,p,AtomStart);
 | |
|         if AtomStart>=EndPos then break;
 | |
|         StartP:=@Src[AtomStart];
 | |
|         if (CompareIdentifiersCaseSensitive(StartP,'define')=0)
 | |
|         or (CompareIdentifiersCaseSensitive(StartP,'undef')=0)
 | |
|         or (CompareIdentifiersCaseSensitive(StartP,'ifdef')=0)
 | |
|         or (CompareIdentifiersCaseSensitive(StartP,'ifndef')=0)
 | |
|         then begin
 | |
|           // a ifdef/ifndef/define/undefine directive
 | |
|           // the next identifier must not be replaced as macro
 | |
|           ReadRawNextCAtom(Src,p,AtomStart);
 | |
|           if AtomStart>=EndPos then break;
 | |
|         end;
 | |
|       end
 | |
|       else if IsIdentStartChar[StartP^] then begin
 | |
|         MacroNode:=Macros.FindNodeWithIdentifierAsPrefix(StartP);
 | |
|         if MacroNode<>nil then begin
 | |
|           // macro found
 | |
|           MacroItem:=PStringToStringItem(MacroNode.Data);
 | |
|           MacroValue:=MacroItem^.Value;
 | |
|           //debugln(['Append MacroName=',MacroItem^.Name,' Src=',GetIdentifier(@Src[AtomStart]),' Value=',dbgstr(MacroValue)]);
 | |
|           // write source in front of macro
 | |
|           if AtomStart>FromPos then begin
 | |
|             //debugln(['Append source in front of macro=',dbgstr(copy(Src,FromPos,AtomStart-FromPos))]);
 | |
|             AddLink(StrStream.Position+1,Code,FromPos);
 | |
|             StrStream.Write(Src[FromPos],AtomStart-FromPos);
 | |
|           end;
 | |
|           FromPos:=p;
 | |
|           if System.Pos('(',MacroItem^.Name)>0
 | |
|           then begin
 | |
|             // a macro function
 | |
|             // => read parameters
 | |
|             ReadRawNextCAtom(Src,p,AtomStart);
 | |
|             if AtomStart>=EndPos then begin
 | |
|               Error(p+GetIdentLen(@Src[FromPos]),
 | |
|                  'missing ( after macro function "'+GetIdentifier(@Src[FromPos])+'"');
 | |
|             end;
 | |
|             if Src[AtomStart]<>'(' then begin
 | |
|               Error(AtomStart,'expected ( after macro function "'+GetIdentifier(@Src[FromPos])+'"'
 | |
|                 +' but found "'+copy(Src,AtomStart,p-AtomStart)+'"');
 | |
|             end;
 | |
|             BracketLvl:=1;
 | |
|             repeat
 | |
|               ReadRawNextCAtom(Src,p,AtomStart);
 | |
|               if AtomStart>=EndPos then begin
 | |
|                 Error(p+GetIdentLen(@Src[FromPos]),
 | |
|                    'missing ) for macro function "'+GetIdentifier(@Src[FromPos])+'"');
 | |
|               end;
 | |
|               case Src[AtomStart] of
 | |
|               '(': inc(BracketLvl);
 | |
|               ')':
 | |
|                 begin
 | |
|                   dec(BracketLvl);
 | |
|                   if BracketLvl=0 then break;
 | |
|                 end;
 | |
|               end;
 | |
|             until false;
 | |
|             FromPos:=p;
 | |
|           end;
 | |
|           //debugln(['Append Macro found: ',MacroItem^.Name]);
 | |
|           if MacroValue<>'' then begin
 | |
|             // write macro value
 | |
|             AddLink(StrStream.Position+1,nil,0);
 | |
|             //debugln(['Append macrovalue=',dbgstr(MacroValue)]);
 | |
|             StrStream.Write(MacroValue[1],length(MacroValue));
 | |
|           end;
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|     if FromPos<EndPos then begin
 | |
|       AddLink(StrStream.Position+1,Code,FromPos);
 | |
|       //debugln(['Append source after macro=',dbgstr(copy(Src,FromPos,EndPos-FromPos))]);
 | |
|       StrStream.Write(Src[FromPos],EndPos-FromPos);
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   procedure Append(const s: string);
 | |
|   begin
 | |
|     if s='' then exit;
 | |
|     AddLink(StrStream.Position+1,nil,0);
 | |
|     StrStream.Write(s[1],length(s));
 | |
|   end;
 | |
| 
 | |
|   procedure Parse(Code: TCodeBuffer);
 | |
|   var
 | |
|     i: Integer;
 | |
|     Line: String;
 | |
|     IncludeParam: String;
 | |
|     j: Integer;
 | |
|     IncCode: TCodeBuffer;
 | |
|     IncFilename: String;
 | |
|     MergePos: Integer;
 | |
|   begin
 | |
|     if MergedBuffers.IndexOf(Code)>=0 then exit;
 | |
|     MergedBuffers.Add(Code);
 | |
|     MergePos:=1;
 | |
|     if not (chfmfIgnoreIncludes in Flags) then begin
 | |
|       for i:=0 to Code.LineCount-1 do begin
 | |
|         Line:=Code.GetLine(i,false);
 | |
|         if length(Line)<length('#include')+2 then continue;
 | |
|         if copy(Line,1,length('#include'))<>'#include' then continue;
 | |
|         if not (Line[length('#include')+1] in [' ',#9]) then continue;
 | |
|         IncludeParam:=Trim(copy(Line,length('#include')+1,length(Line)));
 | |
|         if (IncludeParam<>'') and (IncludeParam[1] in ['<','"']) then
 | |
|           IncludeParam:=copy(IncludeParam,2,length(IncludeParam)-2);
 | |
|         if IncludeParam<>'' then begin
 | |
|           // for example: glib/gutils.h
 | |
|           {$IFDEF VerboseCCodeParser}
 | |
|           debugln(['TCHeaderFileMerger.Merge Param=',IncludeParam]);
 | |
|           {$ENDIF}
 | |
|           // search file in list
 | |
|           for j:=1 to SourceBuffers.Count-1 do begin
 | |
|             IncCode:=TCodeBuffer(SourceBuffers[j]);
 | |
|             IncFilename:=IncCode.Filename;
 | |
|             if CompareFilenames(IncludeParam,
 | |
|                 RightStr(IncFilename,length(IncludeParam)))<>0 then continue;
 | |
|             if (length(IncFilename)=length(IncludeParam))
 | |
|             or (IncFilename[length(IncFilename)-length(IncludeParam)]=PathDelim)
 | |
|             then begin
 | |
|               // include file found
 | |
|               if MergedBuffers.IndexOf(IncCode)<0 then begin
 | |
|                 {$IFDEF VerboseCCodeParser}
 | |
|                 debugln(['TCHeaderFileMerger.Merge file '+IncFilename+' into '+Code.Filename]);
 | |
|                 {$ENDIF}
 | |
|                 Append(Code,MergePos,Code.GetLineStart(i));
 | |
|                 MergePos:=Code.GetLineStart(i+1);
 | |
|                 Append('/* h2pas: merged '+IncludeParam+' into '+ExtractFileName(Code.Filename)+' */'+LineEnding);
 | |
|                 Parse(IncCode);
 | |
|                 Append('/* h2pas: end of merged '+IncludeParam+' into '+ExtractFileName(Code.Filename)+' */'+LineEnding+LineEnding);
 | |
|               end;
 | |
|             end;
 | |
|           end;
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|     if MergePos<=Code.SourceLength then
 | |
|       Append(Code,MergePos,Code.SourceLength+1);
 | |
|     Append(LineEnding);
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   Clear;
 | |
|   MergedBuffers:=TFPList.Create;
 | |
|   StrStream:=TStringStream.Create('');
 | |
|   try
 | |
|     for i:=0 to SourceBuffers.Count-1 do begin
 | |
|       Parse(TCodeBuffer(SourceBuffers[i]));
 | |
|       if not (chfmfAll in Flags) then break;
 | |
|     end;
 | |
|   finally
 | |
|     CombinedSource:=TCodeBuffer.Create;
 | |
|     CombinedSource.Source:=StrStream.DataString;
 | |
|     StrStream.Free;
 | |
|     MergedBuffers.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCHeaderFileMerger.MergedPosToOriginal(MergedPos: integer;
 | |
|   out Code: TCodeBuffer; out CodePos: integer): boolean;
 | |
| var
 | |
|   Link: PCHFileLink;
 | |
|   LinkIndex: LongInt;
 | |
| begin
 | |
|   Code:=nil;
 | |
|   CodePos:=0;
 | |
|   LinkIndex:=LinkIndexOfMergedPos(MergedPos);
 | |
|   if LinkIndex<0 then exit(false);
 | |
|   Link:=@Links[LinkIndex];
 | |
|   Code:=Link^.Code;
 | |
|   //dbgout(['TCHeaderFileMerger.MergedPosToOriginal LinkIndex=',LinkIndex,
 | |
|   //        ' SearchPos=',MergedPos,' LinkMergePos=',Link^.MergedPos,' LinkSrcPos=',Link^.SrcPos]);
 | |
|   //if Code<>nil then dbgout(' File=',ExtractFileName(Code.Filename));
 | |
|   CodePos:=MergedPos-Link^.MergedPos+Link^.SrcPos;
 | |
|   //debugln([' CodePos=',CodePos]);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCHeaderFileMerger.MergedPosToOriginal(MergedX, MergedY: integer;
 | |
|   out Code: TCodeBuffer; out X, Y: integer): boolean;
 | |
| var
 | |
|   MergedPos: integer;
 | |
|   CodePos: integer;
 | |
| begin
 | |
|   Result:=false;
 | |
|   Code:=nil;
 | |
|   X:=0;
 | |
|   Y:=0;
 | |
|   CombinedSource.LineColToPosition(MergedY,MergedX,MergedPos);
 | |
|   //debugln(['TCHeaderFileMerger.MergedPosToOriginal MergedX=',MergedX,' MergedY=',MergedY,' MergedPos=',MergedPos,' ',dbgstr(copy(CombinedSource.Source,MergedPos-20,20)),'|',dbgstr(copy(CombinedSource.Source,MergedPos,20))]);
 | |
|   if not MergedPosToOriginal(MergedPos,Code,CodePos) then exit;
 | |
|   Code.AbsoluteToLineCol(CodePos,Y,X);
 | |
|   Result:=Y>=1;
 | |
| end;
 | |
| 
 | |
| function TCHeaderFileMerger.LinkIndexOfMergedPos(MergedPos: integer): integer;
 | |
| var
 | |
|   l: Integer;
 | |
|   r: Integer;
 | |
| begin
 | |
|   Result:=-1;
 | |
|   if LinkCount=0 then exit;
 | |
|   if (MergedPos<1) then exit;
 | |
|   if MergedPos>CombinedSource.SourceLength+1 then
 | |
|     exit;
 | |
|   // MergedPos is in source
 | |
|   if MergedPos>=Links[LinkCount-1].MergedPos then
 | |
|     exit(LinkCount-1);
 | |
|   // MergedPos is in one of the regular links (that have a successor)
 | |
|   l:=0;
 | |
|   r:=LinkCount-2;
 | |
|   while (l<=r) do begin
 | |
|     Result:=(l+r) div 2;
 | |
|     if MergedPos<Links[Result].MergedPos then
 | |
|       r:=Result-1
 | |
|     else if MergedPos<Links[Result+1].MergedPos then
 | |
|       exit
 | |
|     else
 | |
|       l:=Result+1;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCHeaderFileMerger.GetLinkLength(Index: integer): integer;
 | |
| begin
 | |
|   if (Index<0) or (Index>=LinkCount) then
 | |
|     Result:=0
 | |
|   else if Index=LinkCount-1 then
 | |
|     Result:=CombinedSource.SourceLength-Links[Index].MergedPos
 | |
|   else
 | |
|     Result:=Links[Index+1].MergedPos-Links[Index].MergedPos;
 | |
| end;
 | |
| 
 | |
| procedure TCHeaderFileMerger.WriteDebugReport;
 | |
| var
 | |
|   i: Integer;
 | |
|   Link: PCHFileLink;
 | |
|   Line: integer;
 | |
|   Column: integer;
 | |
|   l: LongInt;
 | |
| begin
 | |
|   debugln(['TCHeaderFileMerger.WriteDebugReport LinkCount=',LinkCount]);
 | |
|   debugln(' # MergePos Line');
 | |
|   for i:=0 to LinkCount-1 do begin
 | |
|     Link:=@Links[i];
 | |
|     CombinedSource.AbsoluteToLineCol(Link^.MergedPos,Line,Column);
 | |
|     dbgout(['  ',i,' ',Link^.MergedPos,' y=',Line]);
 | |
|     if Link^.Code<>nil then begin
 | |
|       Link^.Code.AbsoluteToLineCol(Link^.SrcPos,Line,Column);
 | |
|       DbgOut([' ',ExtractFilename(Link^.Code.Filename),' Y=',Line,' X=',Column,' SrcPos=',Link^.SrcPos]);
 | |
|     end else begin
 | |
|       DbgOut(' no source');
 | |
|     end;
 | |
|     l:=GetLinkLength(i);
 | |
|     if l<45 then
 | |
|       debugln(['  [ ',dbgstr(copy(CombinedSource.Source,Link^.MergedPos,l)),' ]'])
 | |
|     else
 | |
|       debugln(['  [ ',dbgstr(copy(CombinedSource.Source,Link^.MergedPos,20)),' ... ',
 | |
|                       dbgstr(copy(CombinedSource.Source,Link^.MergedPos+GetLinkLength(i)-20,20)),' ]']);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { ECCodeParserException }
 | |
| 
 | |
| constructor ECCodeParserException.Create(ASender: TCCodeParserTool;
 | |
|   const AMessage: string);
 | |
| begin
 | |
|   inherited Create(AMessage);
 | |
|   Sender:=ASender;
 | |
| end;
 | |
| 
 | |
| { TCCodeParserTool }
 | |
| 
 | |
| function TCCodeParserTool.OtherToken: boolean;
 | |
| begin
 | |
|   Result:=true;
 | |
|   if AtomIsChar(';') then
 | |
|     // ignore
 | |
|   else if AtomIsIdentifier then begin
 | |
|     ReadDefinition(false);
 | |
|     if CurNode.LastChild.Desc<>ccnFunction then begin
 | |
|       ReadNextAtom;
 | |
|       if not AtomIsChar(';') then
 | |
|         RaiseExpectedButAtomFound(';');
 | |
|     end;
 | |
|   end else
 | |
|     RaiseException('unexpected token '+GetAtom);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.DirectiveToken: boolean;
 | |
| 
 | |
|   procedure ReadExpression;
 | |
|   var
 | |
|     BracketLevel: Integer;
 | |
|     NeedBracket: Boolean;
 | |
|   begin
 | |
|     BracketLevel:=0;
 | |
|     repeat
 | |
|       ReadRawNextAtom;
 | |
|       {$IFDEF VerboseCCodeParser}
 | |
|       debugln([GetNodeIndent(CurNode),'ReadExpression Atom ',GetAtom]);
 | |
|       {$ENDIF}
 | |
|       if AtomStart>SrcLen then
 | |
|         RaiseException('missing expression');
 | |
|       if Src[AtomStart] in [#10,#13] then begin
 | |
|         if BracketLevel>0 then
 | |
|           RaiseException('missing )');
 | |
|         break;
 | |
|       end;
 | |
|       if AtomIsChar('(') then begin
 | |
|         // in front of a ( there can
 | |
|         inc(BracketLevel);
 | |
|       end else if AtomIsChar(')') then begin
 | |
|         if BracketLevel=0 then
 | |
|           RaiseException(') without (');
 | |
|         dec(BracketLevel);
 | |
|       end else if AtomIsCharOfSet('!+-*/><')
 | |
|       or AtomIs('!=') or AtomIs('==') or AtomIs('<=') or AtomIs('>=')
 | |
|       or AtomIs('&&') or AtomIs('||') or AtomIs('<<') or AtomIs('>>')
 | |
|       then begin
 | |
|         // valid operator
 | |
|       end else if IsIdentChar[Src[AtomStart]] then begin
 | |
|         if AtomIs('defined') then begin
 | |
|           //    read  defined(macro)
 | |
|           // or read  defined macro
 | |
|           ReadRawNextAtom;
 | |
|           if AtomIsChar('(') then begin
 | |
|             NeedBracket:=true;
 | |
|             ReadRawNextAtom;
 | |
|           end else begin
 | |
|             NeedBracket:=false;
 | |
|           end;
 | |
|           if not AtomIsIdentifier then
 | |
|             RaiseExpectedButAtomFound('macro');
 | |
|           if NeedBracket then begin
 | |
|             ReadRawNextAtom;
 | |
|             if not AtomIsChar(')') then
 | |
|               RaiseExpectedButAtomFound(')');
 | |
|           end;
 | |
|         end else begin
 | |
|           // constant
 | |
|         end;
 | |
|       end else begin
 | |
|         RaiseExpectedButAtomFound('constant');
 | |
|       end;
 | |
|     until false;
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   StartPos: LongInt;
 | |
| begin
 | |
|   Result:=true;
 | |
|   CreateChildNode(ccnDirective);
 | |
|   if ParseDirectives then begin
 | |
|     // read directive
 | |
|     ReadRawNextAtom;
 | |
|     if AtomIs('include') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectiveInclude;
 | |
|       ReadRawNextAtom;
 | |
|       if AtomIsChar('<') then begin
 | |
|         // #include <filename>  // search independent of source position
 | |
|         StartPos:=SrcPos;
 | |
|         repeat
 | |
|           ReadRawNextAtom;
 | |
|           if AtomStart>SrcLen then begin
 | |
|             MoveCursorToPos(StartPos);
 | |
|             RaiseExpectedButAtomFound('>');
 | |
|           end;
 | |
|         until AtomIsChar('>');
 | |
|       end else if AtomIsStringConstant then begin
 | |
|         // #include "filename"  // search dependent on source position
 | |
|       end else begin
 | |
|         RaiseExpectedButAtomFound('< or "');
 | |
|       end;
 | |
|     end else if AtomIs('define') then begin
 | |
|       // #define FMAC(a,b) a here, then b
 | |
|       // #define NONFMAC some text here
 | |
|       CurNode.SubDesc:=ccnsDirectiveDefine;
 | |
|       ReadRawNextAtom;
 | |
|       if not AtomIsIdentifier then
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|       // if a ( follows immediately (without spaces) then it is a macro function
 | |
|       if (SrcPos<=SrcLen) and (Src[SrcPos]='(') then begin
 | |
|         AtomStart:=SrcPos;
 | |
|         SrcPos:=AtomStart+1;
 | |
|         ReadTilBracketClose(true);
 | |
|       end;
 | |
|     end else if AtomIs('undef') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectiveUndef;
 | |
|       ReadRawNextAtom;
 | |
|       if not AtomIsIdentifier then
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|     end else if AtomIs('if') then begin
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|       CurNode.SubDesc:=ccnsDirectiveIf;
 | |
|       IncIfLevel(AtomStart);
 | |
|       ReadExpression;
 | |
|     end else if AtomIs('ifdef') then begin
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|       CurNode.SubDesc:=ccnsDirectiveIfDef;
 | |
|       IncIfLevel(AtomStart);
 | |
|       ReadRawNextAtom;
 | |
|       if not AtomIsIdentifier then
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|     end else if AtomIs('ifndef') then begin
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|       CurNode.SubDesc:=ccnsDirectiveIfNDef;
 | |
|       IncIfLevel(AtomStart);
 | |
|       ReadRawNextAtom;
 | |
|       if not AtomIsIdentifier then
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|     end else if AtomIs('elif') then begin
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2-2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|       CurNode.SubDesc:=ccnsDirectiveElIf;
 | |
|       if IfLevel=0 then
 | |
|         RaiseException('elif without if');
 | |
|       ReadExpression;
 | |
|     end else if AtomIs('else') then begin
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2-2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|       CurNode.SubDesc:=ccnsDirectiveElse;
 | |
|       if IfLevel=0 then
 | |
|         RaiseException('else without if');
 | |
|     end else if AtomIs('endif') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectiveEndIf;
 | |
|       if IfLevel=0 then
 | |
|         RaiseException('endif without if');
 | |
|       dec(IfLevel);
 | |
|       {$IFDEF VerboseCDirectives}
 | |
|       DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.DirectiveToken ',GetIndentStr(IfLevel*2),GetAtom]);
 | |
|       {$ENDIF}
 | |
|     end else if AtomIs('line') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectiveLine;
 | |
|     end else if AtomIs('error') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectiveError;
 | |
|     end else if AtomIs('pragma') then begin
 | |
|       CurNode.SubDesc:=ccnsDirectivePragma;
 | |
|     end else begin
 | |
|       RaiseExpectedButAtomFound('directive')
 | |
|     end;
 | |
|   end;
 | |
|   // read til end of line
 | |
|   ReadTilCLineEnd(Src,AtomStart);
 | |
|   SrcPos:=AtomStart;
 | |
|   //DebugLn(['TCCodeParserTool.DirectiveToken ',copy(Src,CurNode.StartPos,AtomStart-CurNode.Startpos)]);
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExternToken: boolean;
 | |
| begin
 | |
|   Result:=true;
 | |
|   CreateChildNode(ccnExternBlock);
 | |
|   ReadNextAtom;
 | |
|   if AtomIsStringConstant then begin
 | |
|     // for example: extern "C" {
 | |
|     ReadNextAtom;
 | |
|     if not AtomIsChar('{') then
 | |
|       RaiseExpectedButAtomFound('{');
 | |
|   end else if AtomIsIdentifier then begin
 | |
|     // for example: extern void a();
 | |
|     CurNode.Desc:=ccnExternDef;
 | |
|     ReadDefinition(false);
 | |
|     EndChildNode;
 | |
|   end else
 | |
|     RaiseExpectedButAtomFound('extern definition');
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CurlyBracketCloseToken: boolean;
 | |
| // examples:
 | |
| //  end of 'extern "C" {'
 | |
| begin
 | |
|   Result:=true;
 | |
|   if CurNode.Desc=ccnExternBlock then
 | |
|     EndChildNode
 | |
|   else
 | |
|     RaiseException('} without {');
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadEnum;
 | |
| (* For example:
 | |
|   enum {
 | |
|     TEST_ENUM1 = 1, /* Enum starts at 1 */
 | |
|     TEST_ENUM2,
 | |
|     TEST_ENUM3
 | |
|   };
 | |
|   enum e1{dark, light};
 | |
| 
 | |
| *)
 | |
| begin
 | |
|   CreateChildNode(ccnEnumBlock);
 | |
|   ReadNextAtom;
 | |
|   // read optional name
 | |
|   if AtomIsIdentifier then begin
 | |
|     CreateChildNode(ccnName);
 | |
|     EndChildNode;
 | |
|     ReadNextAtom;
 | |
|   end;
 | |
|   if not AtomIsChar('{') then
 | |
|     RaiseExpectedButAtomFound('{');
 | |
|   // read enums. Examples
 | |
|   // name,
 | |
|   // name = constant,
 | |
|   ReadNextAtom;
 | |
|   repeat
 | |
|     if AtomIsIdentifier then begin
 | |
|       // read enum
 | |
|       CreateChildNode(ccnEnumID);
 | |
|       CurNode.EndPos:=SrcPos;
 | |
|       ReadNextAtom;
 | |
|       if AtomIsChar('=') then begin
 | |
|         // read value
 | |
|         ReadNextAtom;
 | |
|         ReadConstant;
 | |
|         CurNode.EndPos:=SrcPos;
 | |
|         ReadNextAtom;
 | |
|       end;
 | |
|       EndChildNode;
 | |
|     end;
 | |
|     if AtomIsChar(',') then begin
 | |
|       // next enum
 | |
|       ReadNextAtom;
 | |
|       if not AtomIsIdentifier then
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|     end else if AtomIsChar('}') then begin
 | |
|       break;
 | |
|     end else
 | |
|       RaiseExpectedButAtomFound('}');
 | |
|   until false;
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadUnionStruct(IsStruct: boolean);
 | |
| (*  Examples:
 | |
| 
 | |
|   union sign   /* A definition and a declaration */
 | |
|   {
 | |
|       int svar;
 | |
|       unsigned uvar;
 | |
|   } number;
 | |
| 
 | |
| 
 | |
|   As typedef:
 | |
|     typedef struct {
 | |
|       uint8_t b[6]; // implicit type
 | |
|     } __attribute__((packed)) bdaddr_t;
 | |
|     
 | |
|     typedef struct _sdp_list sdp_list_t;
 | |
| 
 | |
|   As implicit typedef:
 | |
|     struct hidp_connadd_req {
 | |
|       int ctrl_sock;
 | |
|     }
 | |
|     struct SwsContext; // no content
 | |
| 
 | |
|   As variable:
 | |
|     struct hidp_conninfo *ci;
 | |
| *)
 | |
| //
 | |
| //  As typecast in macros:
 | |
| //    struct __attribute__((packed)) {
 | |
| //            typeof(*(ptr)) __v;
 | |
| //    } *__p = (void *) (ptr);
 | |
| //
 | |
| begin
 | |
|   if IsStruct then
 | |
|     CreateChildNode(ccnStruct)
 | |
|   else
 | |
|     CreateChildNode(ccnUnion);
 | |
|   
 | |
|   ReadNextAtom;
 | |
|   if AtomIsIdentifier then begin
 | |
|     CreateChildNode(ccnTypeName);
 | |
|     EndChildNode;
 | |
|     ReadNextAtom;
 | |
|   end;
 | |
|   
 | |
|   // read front attributes
 | |
|   if AtomIs('__attribute__') then begin
 | |
|     Read__attribute__;
 | |
|     ReadNextAtom;
 | |
|   end;
 | |
|   if AtomIsChar('{') then begin
 | |
|     // read block {}
 | |
|     CreateChildNode(ccnSubDefs);
 | |
|     repeat
 | |
|       ReadNextAtom;
 | |
|       // read variables
 | |
|       if AtomIsIdentifier then begin
 | |
|         ReadDefinition(false);
 | |
|         ReadNextAtom;
 | |
|         if AtomIsChar('}') then
 | |
|           break
 | |
|         else if AtomIsChar(';') then begin
 | |
|           // next identifier
 | |
|         end else
 | |
|           RaiseExpectedButAtomFound('}');
 | |
|       end else if AtomIsChar('}') then
 | |
|         break
 | |
|       else if AtomIsChar('#') then
 | |
|         DirectiveToken
 | |
|       else
 | |
|         RaiseExpectedButAtomFound('identifier');
 | |
|     until false;
 | |
|     EndChildNode;
 | |
|     // read after attributes
 | |
|     ReadNextAtom;
 | |
|     if AtomIs('__attribute__') then begin
 | |
|       Read__attribute__;
 | |
|     end else begin
 | |
|       UndoReadNextAtom;
 | |
|     end;
 | |
|   end else
 | |
|     UndoReadNextAtom;
 | |
| 
 | |
|   // close node
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.InitKeyWordList;
 | |
| begin
 | |
|   if FDefaultTokenList=nil then begin
 | |
|     FDefaultTokenList:=TKeyWordFunctionList.Create('TCCodeParserTool.DefaultTokenList');
 | |
|     with FDefaultTokenList do begin
 | |
|       Add('#',{$ifdef FPC}@{$endif}DirectiveToken);
 | |
|       Add('extern',{$ifdef FPC}@{$endif}ExternToken);
 | |
|       Add('}',{$ifdef FPC}@{$endif}CurlyBracketCloseToken);
 | |
|       DefaultKeyWordFunction:={$ifdef FPC}@{$endif}OtherToken;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.InitParser;
 | |
| begin
 | |
|   ParseChangeStep:=Code.ChangeStep;
 | |
|   IncreaseChangeStep;
 | |
|   InitKeyWordList;
 | |
|   Src:=Code.Source;
 | |
|   SrcLen:=length(Src);
 | |
|   if Tree=nil then
 | |
|     Tree:=TCodeTree.Create
 | |
|   else
 | |
|     Tree.Clear;
 | |
|   SrcPos:=1;
 | |
|   AtomStart:=1;
 | |
|   CurNode:=nil;
 | |
|   CreateChildNode(ccnRoot);
 | |
|   IfLevel:=0;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.CreateChildNode(Desc: TCCodeNodeDesc);
 | |
| var
 | |
|   NewNode: TCodeTreeNode;
 | |
| begin
 | |
|   NewNode:=TCodeTreeNode.Create;
 | |
|   Tree.AddNodeAsLastChild(CurNode,NewNode);
 | |
|   NewNode.Desc:=Desc;
 | |
|   CurNode:=NewNode;
 | |
|   CurNode.StartPos:=AtomStart;
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.CreateChildNode ',CCNodeDescAsString(Desc)]);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.EndChildNode;
 | |
| begin
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.EndChildNode ',CCNodeDescAsString(CurNode.Desc)]);
 | |
|   {$ENDIF}
 | |
|   if CurNode.EndPos<=0 then
 | |
|     CurNode.EndPos:=SrcPos;
 | |
|   CurNode:=CurNode.Parent;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.CloseNodes;
 | |
| var
 | |
|   Node: TCodeTreeNode;
 | |
| begin
 | |
|   Node:=CurNode;
 | |
|   while Node<>nil do begin
 | |
|     Node.EndPos:=AtomStart;
 | |
|     Node:=Node.Parent;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadConstant;
 | |
| // ends on last atom of constant
 | |
| begin
 | |
|   if AtomIsChar(',') or AtomIsChar(';') or AtomIsChar(')') then
 | |
|     RaiseExpectedButAtomFound('identifier');
 | |
|   CreateChildNode(ccnConstant);
 | |
|   repeat
 | |
|     if AtomIsChar('(') or AtomIsChar('[') then
 | |
|       ReadTilBracketClose(true);
 | |
|     CurNode.EndPos:=SrcPos;
 | |
|     ReadNextAtom;
 | |
|     if AtomIsChar(',') or AtomIsChar(';')
 | |
|     or AtomIsChar(')') or AtomIsChar(']') or AtomIsChar('}')
 | |
|     then
 | |
|       break;
 | |
|   until false;
 | |
|   UndoReadNextAtom;
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.Read__attribute__;
 | |
| begin
 | |
|   ReadNextAtom;
 | |
|   if not AtomIsChar('(') then
 | |
|     RaiseExpectedButAtomFound('(');
 | |
|   ReadTilBracketClose(true);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.IncIfLevel(StartPos: integer);
 | |
| begin
 | |
|   inc(IfLevel);
 | |
|   if FIfStackCapacity<IfLevel then begin
 | |
|     FIfStackCapacity:=5+FIfStackCapacity*2;
 | |
|     ReAllocMem(FIfStack,FIfStackCapacity*SizeOf(TCCodeParserIfStackItem));
 | |
|   end;
 | |
|   FIfStack[IfLevel-1].StartPos:=StartPos;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadDefinition(AsParameter: boolean);
 | |
| (* Read  type name [specifiers]
 | |
| 
 | |
|   if AsParameter=true then name can be omitted.
 | |
| 
 | |
|   Examples:
 | |
| 
 | |
|   int i
 | |
|   uint8_t b[6]
 | |
|   uint8_t lap[MAX_IAC_LAP][3];
 | |
|   int y = 7;
 | |
| 
 | |
|   static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
 | |
|   {
 | |
|         return memcmp(ba1, ba2, sizeof(bdaddr_t));
 | |
|   }
 | |
|   bdaddr_t *strtoba(const char *str);
 | |
| 
 | |
|   void av_log(void*, int level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4)));
 | |
| 
 | |
| *)
 | |
| {
 | |
|   const char* (*item_name)(void* ctx);
 | |
|   int (*fp)(char*); // pointer to function taking a char* argument; returns an int
 | |
|   int * f(char*); // function taking a char* argument; returns a pointer to int
 | |
| 
 | |
|   complex operator+(complex, complex);
 | |
| }
 | |
| var
 | |
|   IsFunction: Boolean;
 | |
|   NeedEnd: Boolean;
 | |
|   LastIsName: Boolean;
 | |
|   MainNode: TCodeTreeNode;
 | |
| begin
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.ReadVariable START ',GetAtom]);
 | |
|   {$ENDIF}
 | |
|   if AtomIs('typedef') then begin
 | |
|     if AsParameter then
 | |
|       RaiseException('typedef not allowed as function parameter');
 | |
|     CreateChildNode(ccnTypedef);
 | |
|     ReadNextAtom;
 | |
|   end else if AsParameter then begin
 | |
|     CreateChildNode(ccnFuncParameter);
 | |
|     if AtomIs('...') then begin
 | |
|       EndChildNode;
 | |
|       exit;
 | |
|     end;
 | |
|   end else
 | |
|     CreateChildNode(ccnDefinition);
 | |
|   MainNode:=CurNode;
 | |
|   IsFunction:=false;
 | |
|   if AtomIs('volatile') then ReadNextAtom;
 | |
|   if AtomIs('const') then ReadNextAtom;
 | |
| 
 | |
|   if AtomIs('struct') then begin
 | |
|     ReadUnionStruct(true);
 | |
|     ReadNextAtom;
 | |
|   end else if AtomIs('union') then begin
 | |
|     ReadUnionStruct(false);
 | |
|     ReadNextAtom;
 | |
|   end else if AtomIs('enum') then begin
 | |
|     ReadEnum;
 | |
|     ReadNextAtom;
 | |
|   end else begin
 | |
|     if IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
 | |
|     then begin
 | |
|       // read function modifiers
 | |
|       if AsParameter then
 | |
|         RaiseException('function modifier not allowed in parameter');
 | |
|       repeat
 | |
|         IsFunction:=true;
 | |
|         MainNode.Desc:=ccnFunction;
 | |
|         ReadNextAtom;
 | |
|         if not AtomIsIdentifier then
 | |
|           RaiseExpectedButAtomFound('identifier');
 | |
|       until not IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart);
 | |
|     end;
 | |
|     if AtomIs('const') then ReadNextAtom;
 | |
| 
 | |
|     // prefixes: signed, unsigned
 | |
|     // prefixes and/or names long, short
 | |
| 
 | |
|     // int, short int, short signed int
 | |
|     // char, signed char, unsigned char
 | |
|     // singed short, unsigned short, short
 | |
|     // long, long long, signed long, signed long long, unsigned long, unsigned long long
 | |
|     LastIsName:=false;
 | |
|     repeat
 | |
|       if AtomIs('signed') or AtomIs('unsigned') then begin
 | |
|         LastIsName:=false;
 | |
|         ReadNextAtom;
 | |
|       end else if AtomIs('short') or AtomIs('long') then begin
 | |
|         LastIsName:=true;
 | |
|         ReadNextAtom;
 | |
|       end else
 | |
|         break;
 | |
|     until false;
 | |
|     if not LastIsName then
 | |
|       ReadNextAtom;
 | |
|   end;
 | |
| 
 | |
|   while AtomIsChar('*') or AtomIs('const') do begin
 | |
|     // pointer or const
 | |
|     ReadNextAtom;
 | |
|   end;
 | |
|   if AtomIs('operator') then begin
 | |
|     if AsParameter then
 | |
|       RaiseException('operator not allowed as parameter');
 | |
|     IsFunction:=true;
 | |
|     MainNode.Desc:=ccnFunction;
 | |
|     // read operator
 | |
|     ReadNextAtom;
 | |
|     if not IsCCodeCustomOperator.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
 | |
|     then
 | |
|       RaiseExpectedButAtomFound('operator');
 | |
|     CreateChildNode(ccnName);
 | |
|     CurNode.StartPos:=AtomStart;
 | |
|     CurNode.EndPos:=SrcPos;
 | |
|   end else if AtomIsChar('(') then begin
 | |
|     IsFunction:=true;
 | |
|     MainNode.Desc:=ccnFunction;
 | |
|     // example: int (*fp)(char*);
 | |
|     //   pointer to function taking a char* argument; returns an int
 | |
|     ReadNextAtom;
 | |
|     while AtomIsChar('*') or AtomIs('const') do begin
 | |
|       // pointer or const
 | |
|       ReadNextAtom;
 | |
|     end;
 | |
|     {$IFDEF VerboseCCodeParser}
 | |
|     DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.ReadVariable name=',GetAtom]);
 | |
|     {$ENDIF}
 | |
|     if AtomIsIdentifier then begin
 | |
|       CreateChildNode(ccnName);
 | |
|       CurNode.StartPos:=AtomStart;
 | |
|       CurNode.EndPos:=SrcPos;
 | |
|       ReadNextAtom;
 | |
|     end else if not AsParameter then
 | |
|       RaiseExpectedButAtomFound('identifier');
 | |
|     if not AtomIsChar(')') then
 | |
|       RaiseExpectedButAtomFound(')');
 | |
|   end else begin
 | |
|     {$IFDEF VerboseCCodeParser}
 | |
|     DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.ReadVariable name=',GetAtom]);
 | |
|     {$ENDIF}
 | |
|     if AtomIsIdentifier then begin
 | |
|       CreateChildNode(ccnName);
 | |
|       CurNode.StartPos:=AtomStart;
 | |
|       CurNode.EndPos:=SrcPos;
 | |
|     end else begin
 | |
|       UndoReadNextAtom;
 | |
|     end;
 | |
|   end;
 | |
|   // end of name
 | |
|   if CurNode.Desc=ccnName then
 | |
|     EndChildNode;
 | |
| 
 | |
|   ReadNextAtom;
 | |
|   if IsFunction and (not AtomIsChar('(')) then
 | |
|     RaiseExpectedButAtomFound('(');
 | |
|   NeedEnd:=true;
 | |
|   if AtomIsChar('(') then begin
 | |
|     // this is a function => read parameter list
 | |
|     IsFunction:=true;
 | |
|     MainNode.Desc:=ccnFunction;
 | |
|     ReadParameterList;
 | |
|     ReadNextAtom;
 | |
|     if AtomIs('__attribute__') then begin
 | |
|       Read__attribute__;
 | |
|       ReadNextAtom;
 | |
|     end;
 | |
|     if (CurNode.Parent.Desc=ccnTypedef) then begin
 | |
|       if AtomIsChar('{') then
 | |
|         RaiseException('a typedef can not have a statement block');
 | |
|     end else if AsParameter then begin
 | |
|       if AtomIsChar('{') then
 | |
|         RaiseException('a parameter can not have a statement block');
 | |
|     end else begin
 | |
|       if AtomIsChar('{') then begin
 | |
|         // read statements {}
 | |
|         CreateChildNode(ccnStatementBlock);
 | |
|         ReadTilBracketClose(true);
 | |
|         CurNode.EndPos:=SrcPos;
 | |
|         EndChildNode;
 | |
|         ReadNextAtom;
 | |
|       end else if not AtomIsChar(';') then begin
 | |
|         // functions without statements are external and must end with a semicolon
 | |
|         RaiseExpectedButAtomFound(';');
 | |
|       end;
 | |
|       NeedEnd:=false;
 | |
|     end;
 | |
|   end else if AtomIsChar('[') then begin
 | |
|     // read array brackets
 | |
|     while AtomIsChar('[') do begin
 | |
|       ReadTilBracketClose(true);
 | |
|       ReadNextAtom;
 | |
|     end;
 | |
|   end else if AtomIsChar(':') then begin
 | |
|     // bits
 | |
|     ReadNextAtom;
 | |
|     if not AtomIsNumber then
 | |
|       RaiseExpectedButAtomFound('bit count number');
 | |
|     CreateChildNode(ccnBitCount);
 | |
|     EndChildNode;
 | |
|     ReadNextAtom;
 | |
|   end;
 | |
|   
 | |
|   // read initial constant
 | |
|   if NeedEnd and AtomIsChar('=') then begin
 | |
|     if CurNode.HasParentOfType(ccnTypedef) then
 | |
|       RaiseException('typedef can not have an initial value');
 | |
|     ReadNextAtom;
 | |
|     ReadConstant;
 | |
|     ReadNextAtom;
 | |
|     NeedEnd:=true;
 | |
|   end;
 | |
|   
 | |
|   // sanity check
 | |
|   if (SrcPos<=SrcLen) and NeedEnd
 | |
|   and not (AtomIsChar(';') or AtomIsChar(',') or AtomIsChar(')')) then
 | |
|     RaiseExpectedButAtomFound('"end of variable"');
 | |
|     
 | |
|   UndoReadNextAtom;
 | |
| 
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadParameterList;
 | |
| // start on (, end on )
 | |
| var
 | |
|   StartPos: LongInt;
 | |
| begin
 | |
|   CreateChildNode(ccnFuncParamList);
 | |
|   StartPos:=AtomStart;
 | |
|   repeat
 | |
|     ReadNextAtom;
 | |
|     if AtomStart>SrcLen then begin
 | |
|       // missing closing bracket
 | |
|       AtomStart:=StartPos;
 | |
|       SrcPos:=AtomStart+1;
 | |
|       RaiseException('closing bracket not found');
 | |
|     end;
 | |
|     if AtomIsChar(')') then break;
 | |
|     if AtomIsChar(',') then
 | |
|       RaiseExpectedButAtomFound('parameter type');
 | |
|     // read parameter
 | |
|     ReadDefinition(true);
 | |
|     // read next
 | |
|     ReadNextAtom;
 | |
|     if AtomIsChar(')') then break;
 | |
|     if not AtomIsChar(',') then
 | |
|       RaiseExpectedButAtomFound(',');
 | |
|   until false;
 | |
|   CurNode.EndPos:=SrcPos;
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.RaiseException(const AMessage: string; ReportPos: integer);
 | |
| begin
 | |
|   LastErrorMsg:=AMessage;
 | |
|   LastErrorPos:=AtomStart;
 | |
|   LastErrorReportPos:=LastErrorPos;
 | |
|   if ReportPos>0 then
 | |
|     LastErrorReportPos:=ReportPos;
 | |
|   CloseNodes;
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   CTDumpStack;
 | |
|   {$ENDIF}
 | |
|   raise ECCodeParserException.Create(Self,AMessage);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.RaiseExpectedButAtomFound(const AToken: string;
 | |
|   ReportPos: integer);
 | |
| begin
 | |
|   RaiseException(AToken+' expected, but '+GetAtom+' found',ReportPos);
 | |
| end;
 | |
| 
 | |
| constructor TCCodeParserTool.Create;
 | |
| begin
 | |
|   Tree:=TCodeTree.Create;
 | |
|   InitCCOdeKeyWordLists;
 | |
|   VisibleEditorLines:=25;
 | |
|   JumpCentered:=true;
 | |
|   CursorBeyondEOL:=true;
 | |
|   ParseDirectives:=true;
 | |
| end;
 | |
| 
 | |
| destructor TCCodeParserTool.Destroy;
 | |
| begin
 | |
|   Clear;
 | |
|   FreeAndNil(Tree);
 | |
|   ReAllocMem(FIfStack,0);
 | |
|   FIfStackCapacity:=0;
 | |
|   FreeAndNil(FDefaultTokenList);
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.Clear;
 | |
| begin
 | |
|   IfLevel:=0;
 | |
|   if FIfStackCapacity>10 then begin
 | |
|     ReAllocMem(FIfStack,0);
 | |
|     FIfStackCapacity:=0;
 | |
|   end;
 | |
|   Tree.Clear;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.Parse;
 | |
| begin
 | |
|   Parse(Code);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.Parse(aCode: TCodeBuffer);
 | |
| begin
 | |
|   if (Code=aCode) and (not UpdateNeeded) then
 | |
|     exit;
 | |
|   Code:=aCode;
 | |
|   InitParser;
 | |
|   repeat
 | |
|     ReadNextAtom;
 | |
|     if SrcPos<=SrcLen then begin
 | |
|       FDefaultTokenList.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart);
 | |
|     end else begin
 | |
|       break;
 | |
|     end;
 | |
|   until false;
 | |
|   if (CurNode=nil) or (CurNode.Desc<>ccnRoot) then
 | |
|     RaiseException('TCCodeParserTool.Parse: internal parser error');
 | |
|   EndChildNode;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.UpdateNeeded: boolean;
 | |
| begin
 | |
|   Result:=true;
 | |
|   if (Code=nil) or (Tree=nil) or (Tree.Root=nil) then exit;
 | |
|   if Code.ChangeStep<>ParseChangeStep then exit;
 | |
|   Result:=false;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindDeepestNodeAtPos(P: integer;
 | |
|   ExceptionOnNotFound: boolean): TCodeTreeNode; inline;
 | |
| begin
 | |
|   Result:=FindDeepestNodeAtPos(Tree.Root,P,ExceptionOnNotFound);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindDeepestNodeAtPos(StartNode: TCodeTreeNode;
 | |
|   P: integer; ExceptionOnNotFound: boolean): TCodeTreeNode;
 | |
| 
 | |
|   procedure RaiseNoNodeFoundAtCursor;
 | |
|   var
 | |
|     Msg: String;
 | |
|     Node: TCodeTreeNode;
 | |
|     LastPos: Integer;
 | |
|   begin
 | |
|     //DebugLn('RaiseNoNodeFoundAtCursor ',MainFilename);
 | |
|     MoveCursorToPos(P);
 | |
|     // check if p is in parsed code
 | |
|     if (Tree=nil) or (Tree.Root=nil) then begin
 | |
|       RaiseException('no pascal code or not yet parsed');
 | |
|     end;
 | |
|     if p<Tree.Root.StartPos then begin
 | |
|       Msg:='In front of code.';
 | |
|       if Tree.Root.StartPos<=SrcLen then begin
 | |
|         MoveCursorToPos(Tree.Root.StartPos);
 | |
|         ReadNextAtom;
 | |
|         Msg:=Msg+' The pascal code starts with "'+GetAtom+'" at '
 | |
|                 +CleanPosToStr(Tree.Root.StartPos)+'. No code';
 | |
|       end;
 | |
|       MoveCursorToPos(P);
 | |
|       RaiseException(Msg);
 | |
|     end;
 | |
|     Node:=Tree.Root;
 | |
|     while Node.NextBrother<>nil do Node:=Node.NextBrother;
 | |
|     if (Node.EndPos>0) and (p>Node.EndPos) then
 | |
|       LastPos:=Node.EndPos
 | |
|     else
 | |
|       LastPos:=Node.StartPos;
 | |
|     if p>LastPos then begin
 | |
|       Msg:='Behind code. The last valid pascal code is at '+CleanPosToStr(LastPos)+'. No code';
 | |
|       RaiseException(Msg);
 | |
|     end;
 | |
| 
 | |
|     // p is in parsed code, the StartNode is wrong
 | |
|     CTDumpStack;
 | |
|     if (StartNode<>nil) then
 | |
|       RaiseException('Invalid search. The search for pascal started at '
 | |
|                      +CleanPosToStr(StartNode.StartPos)+'. Invalid search')
 | |
|     else
 | |
|       RaiseException('Inconsistency error in TCustomCodeTool.FindDeepestNodeAtPos');
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   ChildNode: TCodeTreeNode;
 | |
|   Brother: TCodeTreeNode;
 | |
| begin
 | |
|   {$IFDEF CheckNodeTool}CheckNodeTool(StartNode);{$ENDIF}
 | |
|   Result:=nil;
 | |
|   while StartNode<>nil do begin
 | |
|     //DebugLn('SearchInNode ',NodeDescriptionAsString(ANode.Desc),
 | |
|     //',',ANode.StartPos,',',ANode.EndPos,', p=',p,
 | |
|     //' "',copy(Src,ANode.StartPos,4),'" - "',copy(Src,ANode.EndPos-5,4),'"');
 | |
|     if (StartNode.StartPos<=P)
 | |
|     and ((StartNode.EndPos>P) or (StartNode.EndPos<1)) then begin
 | |
|       // StartNode contains P
 | |
|       Result:=StartNode;
 | |
|       // -> search for a child that contains P
 | |
|       Brother:=StartNode;
 | |
|       while (Brother<>nil)
 | |
|       and (Brother.StartPos<=P) do begin
 | |
|         // brother also contains P
 | |
|         if Brother.FirstChild<>nil then begin
 | |
|           ChildNode:=FindDeepestNodeAtPos(Brother.FirstChild,P,false);
 | |
|           if ChildNode<>nil then begin
 | |
|             Result:=ChildNode;
 | |
|             exit;
 | |
|           end;
 | |
|         end;
 | |
|         Brother:=Brother.NextBrother;
 | |
|       end;
 | |
|       break;
 | |
|     end else begin
 | |
|       // search in next node
 | |
|       StartNode:=StartNode.NextBrother;
 | |
|     end;
 | |
|   end;
 | |
|   if (Result=nil) and ExceptionOnNotFound then begin
 | |
|     MoveCursorToPos(P);
 | |
|     RaiseNoNodeFoundAtCursor;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CaretToCleanPos(Caret: TCodeXYPosition; out
 | |
|   CleanPos: integer): integer;
 | |
| begin
 | |
|   CleanPos:=0;
 | |
|   if Caret.Code<>Code then
 | |
|     exit(-1);
 | |
|   Code.LineColToPosition(Caret.Y,Caret.X,CleanPos);
 | |
|   if (CleanPos>=1) then
 | |
|     Result:=0
 | |
|   else
 | |
|     Result:=-2; // x,y beyond source
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CleanPosToCodePos(CleanPos: integer; out
 | |
|   CodePos: TCodePosition): boolean;
 | |
| begin
 | |
|   CodePos.Code:=Code;
 | |
|   CodePos.P:=CleanPos;
 | |
|   Result:=(Code<>nil) and (CleanPos>0) and (CleanPos<Code.SourceLength);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CleanPosToCaret(CleanPos: integer; out
 | |
|   Caret: TCodeXYPosition): boolean;
 | |
| begin
 | |
|   Caret.Code:=Code;
 | |
|   Code.AbsoluteToLineCol(CleanPos,Caret.Y,Caret.X);
 | |
|   Result:=CleanPos>0;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CleanPosToCaretAndTopLine(CleanPos: integer; out
 | |
|   Caret: TCodeXYPosition; out NewTopLine: integer): boolean;
 | |
| begin
 | |
|   Caret:=CleanCodeXYPosition;
 | |
|   NewTopLine:=0;
 | |
|   Result:=CleanPosToCaret(CleanPos,Caret);
 | |
|   if Result then begin
 | |
|     if JumpCentered then begin
 | |
|       NewTopLine:=Caret.Y-(VisibleEditorLines shr 1);
 | |
|       if NewTopLine<1 then NewTopLine:=1;
 | |
|     end else
 | |
|       NewTopLine:=Caret.Y;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.CleanPosToStr(CleanPos: integer): string;
 | |
| var
 | |
|   CodePos: TCodeXYPosition;
 | |
| begin
 | |
|   if CleanPosToCaret(CleanPos,CodePos) then
 | |
|     Result:='y='+IntToStr(CodePos.Y)+',x='+IntToStr(CodePos.X)
 | |
|   else
 | |
|     Result:='y=?,x=?';
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.MainFilename: string;
 | |
| begin
 | |
|   Result:=Code.Filename;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.MoveCursorToPos(p: integer);
 | |
| begin
 | |
|   SrcPos:=p;
 | |
|   AtomStart:=p;
 | |
|   LastAtomStart:=0;
 | |
|   LastSrcPos:=0;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.MoveCursorToNode(Node: TCodeTreeNode);
 | |
| begin
 | |
|   MoveCursorToPos(Node.StartPos);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadNextAtom;
 | |
| begin
 | |
|   //DebugLn(['TCCodeParserTool.ReadNextAtom START ',AtomStart,'-',SrcPos,' ',Src[SrcPos]]);
 | |
|   LastSrcPos:=SrcPos;
 | |
|   LastAtomStart:=AtomStart;
 | |
|   repeat
 | |
|     ReadRawNextCAtom(Src,SrcPos,AtomStart);
 | |
|   until (SrcPos>SrcLen) or (not (Src[AtomStart] in [#10,#13]));
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.ReadNextAtom END ',AtomStart,'-',SrcPos,' "',copy(Src,AtomStart,SrcPos-AtomStart),'"']);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadNextAtomSkipDirectives;
 | |
| begin
 | |
|   //DebugLn(['TCCodeParserTool.ReadNextAtom START ',AtomStart,'-',SrcPos,' ',Src[SrcPos]]);
 | |
|   LastSrcPos:=SrcPos;
 | |
|   LastAtomStart:=AtomStart;
 | |
|   repeat
 | |
|     ReadRawNextCAtom(Src,SrcPos,AtomStart);
 | |
|     if (AtomStart>SrcLen) then break;
 | |
|     if Src[AtomStart]='#' then begin
 | |
|       ReadTilCLineEnd(Src,SrcPos);
 | |
|       if (SrcPos>SrcLen) then break;
 | |
|     end;
 | |
|   until (not (Src[AtomStart] in [#10,#13]));
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.ReadNextAtom END ',AtomStart,'-',SrcPos,' "',copy(Src,AtomStart,SrcPos-AtomStart),'"']);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.ReadRawNextAtom;
 | |
| begin
 | |
|   LastSrcPos:=SrcPos;
 | |
|   LastAtomStart:=AtomStart;
 | |
|   ReadRawNextCAtom(Src,SrcPos,AtomStart);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.UndoReadNextAtom;
 | |
| begin
 | |
|   if LastSrcPos>0 then begin
 | |
|     SrcPos:=LastSrcPos;
 | |
|     AtomStart:=LastAtomStart;
 | |
|     LastSrcPos:=0;
 | |
|     LastAtomStart:=0;
 | |
|   end else begin
 | |
|     SrcPos:=AtomStart;
 | |
|   end;
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn([GetNodeIndent(CurNode),'TCCodeParserTool.UndoReadNextAtom END ',AtomStart,'-',SrcPos,' "',copy(Src,AtomStart,SrcPos-AtomStart),'"']);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| {$IFOPT R+}{$DEFINE RangeChecking}{$ENDIF}
 | |
| {$R-}
 | |
| function TCCodeParserTool.ReadTilBracketClose(
 | |
|   ExceptionOnNotFound: boolean): boolean;
 | |
| // AtomStart must be on bracket open
 | |
| // after reading AtomStart is on closing bracket
 | |
| var
 | |
|   CloseBracket: Char;
 | |
|   StartPos: LongInt;
 | |
| begin
 | |
|   Result:=false;
 | |
|   case Src[AtomStart] of
 | |
|   '{': CloseBracket:='}';
 | |
|   '[': CloseBracket:=']';
 | |
|   '(': CloseBracket:=')';
 | |
|   '<': CloseBracket:='>';
 | |
|   else
 | |
|     if ExceptionOnNotFound then
 | |
|       RaiseExpectedButAtomFound('(');
 | |
|     exit;
 | |
|   end;
 | |
|   StartPos:=AtomStart;
 | |
|   repeat
 | |
|     ReadRawNextCAtom(Src,SrcPos,AtomStart);
 | |
|     if AtomStart>SrcLen then begin
 | |
|       AtomStart:=StartPos;
 | |
|       SrcPos:=AtomStart+1;
 | |
|       if ExceptionOnNotFound then
 | |
|         RaiseException('closing bracket not found');
 | |
|       exit;
 | |
|     end;
 | |
|     case Src[AtomStart] of
 | |
|     '{','(','[':
 | |
|       // skip nested bracketss
 | |
|       begin
 | |
|         if not ReadTilBracketClose(ExceptionOnNotFound) then
 | |
|           exit;
 | |
|       end;
 | |
|     else
 | |
|       if Src[AtomStart]=CloseBracket then exit(true);
 | |
|     end;
 | |
|   until false;
 | |
| end;
 | |
| {$IFDEF RangeChecking}{$R+}{$UNDEF RangeChecking}{$ENDIF}
 | |
| 
 | |
| function TCCodeParserTool.AtomIs(const s: shortstring): boolean;
 | |
| var
 | |
|   len: Integer;
 | |
|   i: Integer;
 | |
| begin
 | |
|   len:=length(s);
 | |
|   if (len<>SrcPos-AtomStart) then exit(false);
 | |
|   if SrcPos>SrcLen then exit(false);
 | |
|   for i:=1 to len do
 | |
|     if Src[AtomStart+i-1]<>s[i] then exit(false);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.AtomIsChar(const c: char): boolean;
 | |
| begin
 | |
|   if SrcPos-AtomStart<>1 then exit(false);
 | |
|   if AtomStart>SrcLen then exit(false);
 | |
|   if Src[AtomStart]<>c then exit(false);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.AtomIsCharOfSet(const s: shortstring): boolean;
 | |
| var
 | |
|   i: Integer;
 | |
|   c: Char;
 | |
| begin
 | |
|   if SrcPos-AtomStart<>1 then exit(false);
 | |
|   if SrcPos>SrcLen then exit(false);
 | |
|   c:=Src[AtomStart];
 | |
|   for i:=1 to length(s) do
 | |
|     if s[i]=c then exit(true);
 | |
|   Result:=false;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.UpAtomIs(const s: shortstring): boolean;
 | |
| var
 | |
|   len: Integer;
 | |
|   i: Integer;
 | |
| begin
 | |
|   len:=length(s);
 | |
|   if (len<>SrcPos-AtomStart) then exit(false);
 | |
|   if SrcPos>SrcLen then exit(false);
 | |
|   for i:=1 to len do
 | |
|     if UpChars[Src[AtomStart+i-1]]<>s[i] then exit(false);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.AtomIsIdentifier: boolean;
 | |
| var
 | |
|   p: Integer;
 | |
| begin
 | |
|   if (AtomStart>=SrcPos) then exit(false);
 | |
|   if (SrcPos>SrcLen) or (SrcPos-AtomStart>255) then exit(false);
 | |
|   if not IsIdentStartChar[Src[AtomStart]] then exit(false);
 | |
|   p:=AtomStart+1;
 | |
|   while (p<SrcPos) do begin
 | |
|     if not IsIdentChar[Src[p]] then exit(false);
 | |
|     inc(p);
 | |
|   end;
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.AtomIsStringConstant: boolean;
 | |
| begin
 | |
|   Result:=(AtomStart<SrcLen) and (Src[AtomStart]='"');
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.AtomIsNumber: boolean;
 | |
| begin
 | |
|   Result:=(AtomStart<SrcLen) and (Src[AtomStart] in ['0'..'9']);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.LastAtomIs(const s: shortstring): boolean;
 | |
| var
 | |
|   len: Integer;
 | |
|   i: Integer;
 | |
| begin
 | |
|   if LastAtomStart<=LastSrcPos then exit(false);
 | |
|   len:=length(s);
 | |
|   if (len<>LastSrcPos-LastAtomStart) then exit(false);
 | |
|   if LastSrcPos>SrcLen then exit(false);
 | |
|   for i:=1 to len do
 | |
|     if Src[LastAtomStart+i-1]<>s[i] then exit(false);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.GetLastAtom: string;
 | |
| begin
 | |
|   Result:=copy(Src,LastAtomStart,LastSrcPos-LastAtomStart);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractCode(StartPos, EndPos: integer;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   s: string;
 | |
|   p: integer;
 | |
| 
 | |
|   procedure ReadIt;
 | |
|   var
 | |
|     l: Integer;
 | |
|     NextChar: Char;
 | |
|     LastChar: Char;
 | |
|   begin
 | |
|     MoveCursorToPos(StartPos);
 | |
|     p:=1;
 | |
|     LastChar:=' ';
 | |
|     repeat
 | |
|       // read next token
 | |
|       if WithDirectives then
 | |
|         ReadNextAtom
 | |
|       else
 | |
|         ReadNextAtomSkipDirectives;
 | |
|       if (AtomStart>=EndPos) then break;
 | |
|       
 | |
|       NextChar:=Src[AtomStart];
 | |
|       if IsIdentChar[LastChar] and IsIdentStartChar[NextChar] then begin
 | |
|         // add space
 | |
|         if s<>'' then
 | |
|           s[p]:=' ';
 | |
|         inc(p);
 | |
|       end;
 | |
|       // add token
 | |
|       l:=SrcPos-AtomStart;
 | |
|       if s<>'' then begin
 | |
|         // copy token
 | |
|         System.Move(Src[AtomStart],s[p],SrcPos-AtomStart);
 | |
|       end;
 | |
|       inc(p,l);
 | |
|       // remember last char
 | |
|       LastChar:=Src[SrcPos-1];
 | |
|     until false;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if EndPos>SrcLen then EndPos:=SrcLen+1;
 | |
|   // first read and compute needed length
 | |
|   ReadIt;
 | |
|   // allocate space and copy tokens
 | |
|   SetLength(s,p-1);
 | |
|   ReadIt;
 | |
|   Result:=s;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.GetFirstNameNode(Node: TCodeTreeNode): TCodeTreeNode;
 | |
| begin
 | |
|   Result:=Node.FirstChild;
 | |
|   while (Result<>nil) and (Result.Desc<>ccnName) do Result:=Result.NextBrother;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractName(NameNode: TCodeTreeNode): string;
 | |
| begin
 | |
|   Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDefinitionName(VarNode: TCodeTreeNode): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(VarNode);
 | |
|   if (NameNode=nil) then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDefinitionType(VarNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
|   s: String;
 | |
|   AfterNameNode: TCodeTreeNode;
 | |
|   StartPos: Integer;
 | |
| begin
 | |
|   Result:='';
 | |
|   NameNode:=GetFirstNameNode(VarNode);
 | |
|   if (NameNode<>nil) then begin
 | |
|     Result:=ExtractCode(VarNode.StartPos,NameNode.StartPos,WithDirectives);
 | |
|     AfterNameNode:=NameNode.NextBrother;
 | |
|     StartPos:=NameNode.EndPos;
 | |
|   end else begin
 | |
|     AfterNameNode:=VarNode.FirstChild;
 | |
|     StartPos:=VarNode.StartPos;
 | |
|   end;
 | |
|   if (AfterNameNode<>nil)
 | |
|   and (AfterNameNode.Desc=ccnConstant) then begin
 | |
|     // omit constant
 | |
|     s:=ExtractCode(StartPos,AfterNameNode.StartPos,WithDirectives);
 | |
|     if (s<>'') and (s[length(s)]='=') then
 | |
|       s:=Trim(copy(s,1,length(s)-1));
 | |
|     Result:=Result+s;
 | |
|   end else begin
 | |
|     Result:=Result+ExtractCode(StartPos,VarNode.EndPos,WithDirectives);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractTypeDefType(TypeDefNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
|   StartPos: Integer;
 | |
| begin
 | |
|   Result:='';
 | |
|   StartPos:=TypeDefNode.StartPos+length('typedef');
 | |
|   NameNode:=GetFirstNameNode(TypeDefNode);
 | |
|   if (NameNode<>nil) then begin
 | |
|     Result:=ExtractCode(StartPos,NameNode.StartPos,WithDirectives);
 | |
|   end else
 | |
|     Result:=ExtractCode(StartPos,TypeDefNode.EndPos,WithDirectives);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractFunctionName(FuncNode: TCodeTreeNode): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(FuncNode);
 | |
|   if (NameNode=nil) then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.GetFunctionParamListNode(Node: TCodeTreeNode
 | |
|   ): TCodeTreeNode;
 | |
| begin
 | |
|   Result:=Node.FirstChild;
 | |
|   while (Result<>nil) and (Result.Desc<>ccnFuncParamList) do
 | |
|     Result:=Result.NextBrother;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractFunctionParamList(FuncNode: TCodeTreeNode
 | |
|   ): string;
 | |
| var
 | |
|   ParamsNode: TCodeTreeNode;
 | |
| begin
 | |
|   ParamsNode:=GetFunctionParamListNode(FuncNode);
 | |
|   if (ParamsNode=nil) then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=ExtractCode(ParamsNode.StartPos,ParamsNode.EndPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractFunctionType(FuncNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(FuncNode);
 | |
|   if (NameNode=nil) then begin
 | |
|     Result:='';
 | |
|     exit;
 | |
|   end;
 | |
|   Result:=ExtractCode(FuncNode.StartPos,NameNode.StartPos,WithDirectives);
 | |
|   if (NameNode.NextBrother<>nil)
 | |
|   and (NameNode.NextBrother.Desc=ccnFuncParamList) then begin
 | |
|     // The name is in between.
 | |
|     // The type is result type + parameter list
 | |
|     Result:=Result+ExtractCode(NameNode.EndPos,NameNode.NextBrother.EndPos,
 | |
|                                WithDirectives);
 | |
|   end else begin
 | |
|     Result:=Result+ExtractCode(NameNode.EndPos,FuncNode.EndPos,
 | |
|                                WithDirectives);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractFunctionResultType(FuncNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean; WithBrackets: boolean): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
|   p: Integer;
 | |
|   CurAtomStart: integer;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(FuncNode);
 | |
|   if (NameNode=nil) then begin
 | |
|     Result:='';
 | |
|     exit;
 | |
|   end;
 | |
|   
 | |
|   // skip function modifiers
 | |
|   MoveCursorToNode(FuncNode);
 | |
|   repeat
 | |
|     ReadNextAtom;
 | |
|     if AtomStart>=NameNode.StartPos then break;
 | |
|     if not IsCCodeFunctionModifier.DoItCaseSensitive(Src,AtomStart,SrcPos-AtomStart)
 | |
|     then
 | |
|       break;
 | |
|   until false;
 | |
|   
 | |
|   Result:=ExtractCode(AtomStart,NameNode.StartPos,WithDirectives);
 | |
|   if (NameNode.NextBrother<>nil)
 | |
|   and (NameNode.NextBrother.Desc=ccnFuncParamList) then begin
 | |
|     // The name is in between.
 | |
|     Result:=Result+ExtractCode(NameNode.EndPos,NameNode.NextBrother.StartPos,
 | |
|                                WithDirectives);
 | |
|   end else begin
 | |
|     Result:=Result+ExtractCode(NameNode.EndPos,FuncNode.EndPos,
 | |
|                                WithDirectives);
 | |
|   end;
 | |
|   
 | |
|   if not WithBrackets then begin
 | |
|     p:=1;
 | |
|     repeat
 | |
|       ReadRawNextCAtom(Result,p,CurAtomStart);
 | |
|       if CurAtomStart>length(Result) then break;
 | |
|       if Result[CurAtomStart]='(' then begin
 | |
|         p:=CurAtomStart;
 | |
|         if ReadTilCBracketClose(Result,p) then begin
 | |
|           Result:=copy(Result,1,CurAtomStart-1)
 | |
|                  +copy(Result,p,length(Result));
 | |
|         end;
 | |
|         break;
 | |
|       end;
 | |
|     until false;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.IsPointerToFunction(FuncNode: TCodeTreeNode
 | |
|   ): boolean;
 | |
| // for example: int *(*fp)();
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=FuncNode.FirstChild;
 | |
|   if (NameNode=nil) or (NameNode.Desc<>ccnName) then exit(false);
 | |
|   MoveCursorToNode(FuncNode);
 | |
|   repeat
 | |
|     ReadNextAtom;
 | |
|     if AtomStart>SrcLen then exit(false);
 | |
|     if AtomIs('(') then exit(true);
 | |
|     if (IsIdentStartChar[Src[AtomStart]])
 | |
|     or (AtomIs('*')) then begin
 | |
|       // skip words and *
 | |
|     end else begin
 | |
|       break;
 | |
|     end;
 | |
|   until AtomStart>=NameNode.StartPos;
 | |
|   Result:=false;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractParameterName(ParamNode: TCodeTreeNode
 | |
|   ): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(ParamNode);
 | |
|   if (NameNode=nil) then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractParameterType(ParamNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
|   s: String;
 | |
|   ConstantNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=GetFirstNameNode(ParamNode);
 | |
|   if (ParamNode.LastChild<>nil)
 | |
|   and (ParamNode.LastChild.Desc=ccnConstant) then
 | |
|     ConstantNode:=ParamNode.LastChild
 | |
|   else
 | |
|     ConstantNode:=nil;
 | |
|   if (NameNode=nil) then begin
 | |
|     if ConstantNode<>nil then begin
 | |
|       // a parameter with an initial value
 | |
|       // omit the constant
 | |
|       Result:=ExtractCode(ParamNode.StartPos,ConstantNode.StartPos,WithDirectives);
 | |
|       Result:=copy(Result,1,length(Result)-1);
 | |
|     end else begin
 | |
|       Result:=ExtractCode(ParamNode.StartPos,ParamNode.EndPos,WithDirectives);
 | |
|     end;
 | |
|   end else begin
 | |
|     Result:=ExtractCode(ParamNode.StartPos,NameNode.StartPos,WithDirectives);
 | |
|     if (NameNode.NextBrother<>nil)
 | |
|     and (NameNode.NextBrother.Desc=ccnConstant) then begin
 | |
|       // a parameter with an initial value
 | |
|       // omit the constant
 | |
|       s:=ExtractCode(NameNode.EndPos,NameNode.NextBrother.StartPos,
 | |
|                      WithDirectives);
 | |
|       s:=copy(s,1,length(s)-1);
 | |
|       Result:=Result+s;
 | |
|     end else begin
 | |
|       Result:=Result+ExtractCode(NameNode.EndPos,ParamNode.EndPos,
 | |
|                                  WithDirectives);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractEnumBlockName(EnumBlockNode: TCodeTreeNode
 | |
|   ): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   if (EnumBlockNode.FirstChild<>nil)
 | |
|   and (EnumBlockNode.FirstChild.Desc=ccnName) then begin
 | |
|     NameNode:=EnumBlockNode.FirstChild;
 | |
|     Result:=copy(Src,NameNode.StartPos,NameNode.EndPos-NameNode.StartPos);
 | |
|   end else begin
 | |
|     Result:='';
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractEnumIDName(EnumIDNode: TCodeTreeNode): string;
 | |
| begin
 | |
|   Result:=GetIdentifier(@Src[EnumIDNode.StartPos]);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractEnumIDValue(EnumIDNode: TCodeTreeNode;
 | |
|   WithDirectives: boolean): string;
 | |
| var
 | |
|   ConstantNode: TCodeTreeNode;
 | |
| begin
 | |
|   ConstantNode:=EnumIDNode.FirstChild;
 | |
|   if (ConstantNode=nil)
 | |
|   or (ConstantNode.Desc<>ccnConstant) then
 | |
|     Result:=''
 | |
|   else
 | |
|     Result:=ExtractCode(ConstantNode.StartPos,ConstantNode.EndPos,
 | |
|                         WithDirectives);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractStructName(StructNode: TCodeTreeNode): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=StructNode.FirstChild;
 | |
|   if (NameNode<>nil) and (NameNode.Desc=ccnTypeName) then
 | |
|     Result:=GetIdentifier(@Src[NameNode.StartPos])
 | |
|   else
 | |
|     Result:='';
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractUnionName(UnionNode: TCodeTreeNode): string;
 | |
| var
 | |
|   NameNode: TCodeTreeNode;
 | |
| begin
 | |
|   NameNode:=UnionNode.FirstChild;
 | |
|   if (NameNode<>nil) and (NameNode.Desc=ccnName) then
 | |
|     Result:=GetIdentifier(@Src[NameNode.StartPos])
 | |
|   else
 | |
|     Result:='';
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractTypedefName(TypedefNode: TCodeTreeNode
 | |
|   ): string;
 | |
| var
 | |
|   Node: TCodeTreeNode;
 | |
| begin
 | |
|   Node:=TypedefNode.LastChild;
 | |
|   while (Node<>nil) and (Node.Desc<>ccnName) do
 | |
|     Node:=Node.PriorBrother;
 | |
|   if Node=nil then begin
 | |
|     if (TypedefNode.FirstChild<>nil)
 | |
|     and (TypedefNode.FirstChild.Desc=ccnFunction) then
 | |
|       Result:=ExtractFunctionName(TypedefNode.FirstChild)
 | |
|     else
 | |
|       Result:='';
 | |
|   end else
 | |
|     Result:=GetIdentifier(@Src[Node.StartPos]);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.IsDirective(DirectiveNode: TCodeTreeNode;
 | |
|   const Action: shortstring): boolean;
 | |
| begin
 | |
|   Result:=false;
 | |
|   if (DirectiveNode=nil) or (DirectiveNode.Desc<>ccnDirective) then exit;
 | |
|   MoveCursorToPos(DirectiveNode.StartPos+1);
 | |
|   ReadNextAtom;
 | |
|   Result:=AtomIs(Action);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDirectiveAction(DirectiveNode: TCodeTreeNode
 | |
|   ): string;
 | |
| begin
 | |
|   if DirectiveNode.StartPos<SrcLen then
 | |
|     Result:=GetIdentifier(@Src[DirectiveNode.StartPos+1])
 | |
|   else
 | |
|     Result:='';
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDirectiveFirstAtom(DirectiveNode: TCodeTreeNode
 | |
|   ): string;
 | |
| begin
 | |
|   MoveCursorToPos(DirectiveNode.StartPos+1);
 | |
|   // read action
 | |
|   ReadRawNextAtom;
 | |
|   // read first atom
 | |
|   ReadRawNextAtom;
 | |
|   Result:=GetAtom;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDirectiveParams(DirectiveNode: TCodeTreeNode
 | |
|   ): string;
 | |
| begin
 | |
|   MoveCursorToPos(DirectiveNode.StartPos+1);
 | |
|   // read action
 | |
|   ReadRawNextAtom;
 | |
|   // read first atom
 | |
|   ReadRawNextAtom;
 | |
|   Result:=ExtractCode(AtomStart,DirectiveNode.EndPos);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.ExtractDefine(DefineNode: TCodeTreeNode; out
 | |
|   MacroName, MacroParamList, MacroValue: string): boolean;
 | |
| var
 | |
|   StartPos: LongInt;
 | |
|   EndPos: LongInt;
 | |
| begin
 | |
|   Result:=false;
 | |
|   MacroName:='';
 | |
|   MacroParamList:='';
 | |
|   MacroValue:='';
 | |
|   MoveCursorToPos(DefineNode.StartPos+1);
 | |
|   // read action
 | |
|   ReadRawNextAtom;
 | |
|   if not AtomIs('define') then exit;
 | |
|   // read first atom
 | |
|   ReadRawNextAtom;
 | |
|   MacroName:=GetAtom;
 | |
|   StartPos:=SrcPos;
 | |
|   // read param list
 | |
|   if (SrcPos<=SrcLen) and (Src[SrcPos]='(') then begin
 | |
|     StartPos:=SrcPos;
 | |
|     AtomStart:=SrcPos;
 | |
|     SrcPos:=AtomStart+1;
 | |
|     if not ReadTilBracketClose(false) then exit;
 | |
|     EndPos:=SrcPos;
 | |
|     MacroParamList:=ExtractCode(StartPos,EndPos);
 | |
|     StartPos:=EndPos;
 | |
|   end;
 | |
|   // read value
 | |
|   while (StartPos<=SrcLen) and (IsSpaceChar[Src[StartPos]]) do
 | |
|     inc(StartPos);
 | |
|   EndPos:=DefineNode.EndPos;
 | |
|   while (EndPos>StartPos) and (IsSpaceChar[Src[EndPos-1]]) do
 | |
|     dec(EndPos);
 | |
|   MacroValue:=copy(Src,StartPos,EndPos-StartPos);
 | |
|   Result:=true;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindDirectiveBlockEnd(InnerNode: TCodeTreeNode
 | |
|   ): TCodeTreeNode;
 | |
| // find the end of block that contains InnerNode
 | |
| begin
 | |
|   Result:=InnerNode;
 | |
|   if Result=nil then exit;
 | |
|   Result:=Result.Next;
 | |
|   IfLevel:=0;
 | |
|   while Result<>nil do begin
 | |
|     if Result.Desc=ccnDirective then begin
 | |
|       //DebugLn(['TCCodeParserTool.FindDirectiveBlockEnd ',NodeAsString(Result),' ',IfLevel]);
 | |
|       case Result.SubDesc of
 | |
|       ccnsDirectiveIf,ccnsDirectiveIfDef,ccnsDirectiveIfNDef:
 | |
|         IncIfLevel(Result.StartPos);
 | |
|       ccnsDirectiveElse,ccnsDirectiveElIf:
 | |
|         begin
 | |
|           if IfLevel=0 then
 | |
|             exit;
 | |
|           dec(IfLevel);
 | |
|           IncIfLevel(Result.StartPos);
 | |
|         end;
 | |
|       ccnsDirectiveEndIf:
 | |
|         begin
 | |
|           if IfLevel=0 then
 | |
|             exit;
 | |
|           dec(IfLevel);
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|     Result:=Result.Next;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindElseOrEndIf(IfOrElseNode: TCodeTreeNode
 | |
|   ): TCodeTreeNode;
 | |
| // find corresponding #EndIf or #Else or #ElIf
 | |
| begin
 | |
|   Result:=IfOrElseNode;
 | |
|   // check if IfOrElseNode is valid
 | |
|   if (Result=nil) or (Result.Desc<>ccnDirective)
 | |
|   or (not (Result.SubDesc in [ccnsDirectiveIf,ccnsDirectiveIfDef,
 | |
|                       ccnsDirectiveIfNDef,ccnsDirectiveElIf,ccnsDirectiveElse]))
 | |
|   then
 | |
|     exit(nil);
 | |
|   // check if next node is the end of the block
 | |
|   Result:=Result.Next;
 | |
|   if Result=nil then exit;
 | |
|   if (Result.Desc=ccnDirective)
 | |
|   and (Result.SubDesc in [ccnsDirectiveElIf,ccnsDirectiveElse,ccnsDirectiveEndIf])
 | |
|   then
 | |
|     exit;
 | |
|   // Result is a node in the block => search end of block
 | |
|   Result:=FindDirectiveBlockEnd(Result);
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindEndIf(IfOrElseNode: TCodeTreeNode): TCodeTreeNode;
 | |
| // find corresponding #EndIf
 | |
| begin
 | |
|   Result:=IfOrElseNode;
 | |
|   repeat
 | |
|     Result:=FindElseOrEndIf(Result);
 | |
|     if (Result=nil)
 | |
|     or ((Result.Desc=ccnDirective) and (Result.SubDesc=ccnsDirectiveEndIf))
 | |
|     then
 | |
|       exit;
 | |
|   until false;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.FindEnclosingIFNDEF: TCodeTreeNode;
 | |
| { Search for the typical enclosing IFNDEF of c header file:
 | |
|    - No code in front
 | |
|    - #IFNDEF NAME
 | |
|    - #DEFINE NAME
 | |
|    - ...
 | |
|    - #ENDIF
 | |
|    - No code behind
 | |
| }
 | |
| var
 | |
|   IfNDefNode: TCodeTreeNode;
 | |
|   MacroName: String;
 | |
|   DefNode: TCodeTreeNode;
 | |
|   DefMacroName: String;
 | |
|   EndIfNode: TCodeTreeNode;
 | |
|   Node: TCodeTreeNode;
 | |
| begin
 | |
|   Result:=nil;
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   WriteDebugReport;
 | |
|   {$ENDIF}
 | |
|   IfNDefNode:=Tree.Root;
 | |
|   // skip root and extern nodes
 | |
|   while (IfNDefNode<>nil) do begin
 | |
|     if (IfNDefNode.Desc<>ccnRoot)
 | |
|     and (IfNDefNode.Desc<>ccnExternBlock) then
 | |
|       break;
 | |
|     IfNDefNode:=IfNDefNode.Next;
 | |
|   end;
 | |
|   if IfNDefNode=nil then exit;
 | |
|   // check if IfNDefNode is #IFNDEF name
 | |
|   if (IfNDefNode.Desc<>ccnDirective)
 | |
|   or (IfNDefNode.SubDesc<>ccnsDirectiveIfNDef) then
 | |
|     exit;
 | |
|   MacroName:=ExtractDirectiveFirstAtom(IfNDefNode);
 | |
|   // check if next node is #DEFINE name
 | |
|   DefNode:=IfNDefNode.Next;
 | |
|   if (DefNode=nil) or (DefNode.Desc<>ccnDirective)
 | |
|   or (DefNode.SubDesc<>ccnsDirectiveDefine) then exit;
 | |
|   DefMacroName:=ExtractDirectiveFirstAtom(DefNode);
 | |
|   if CompareIdentifiers(PChar(MacroName),PChar(DefMacroName))<>0 then exit;
 | |
|   // find #endif
 | |
|   EndIfNode:=FindEndIf(IfNDefNode);
 | |
|   if EndIfNode=nil then exit;
 | |
|   // check that no code comes after the #EndIf
 | |
|   Node:=EndIfNode;
 | |
|   while (Node<>nil) do begin
 | |
|     if (Node.Desc=ccnExternBlock)
 | |
|     or ((Node.Desc=ccnDirective)
 | |
|         and (Node.SubDesc in [ccnsDirectiveIf,ccnsDirectiveIfDef,
 | |
|           ccnsDirectiveIfNDef,ccnsDirectiveElIf,ccnsDirectiveEndIf]))
 | |
|     then
 | |
|       Node:=Node.Next
 | |
|     else
 | |
|       exit;
 | |
|   end;
 | |
|   Result:=IfNDefNode;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.GetAtom: string;
 | |
| begin
 | |
|   Result:=copy(Src,AtomStart,SrcPos-AtomStart);
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.Replace(FromPos, ToPos: integer; const NewSrc: string);
 | |
| var
 | |
|   Node: TCodeTreeNode;
 | |
|   DiffPos: Integer;
 | |
| begin
 | |
|   {$IFDEF VerboseCCodeParser}
 | |
|   DebugLn(['TCCodeParserTool.Replace ',FromPos,'-',ToPos,' Old="',copy(Src,FromPos,ToPos-FromPos),'" New="',NewSrc,'"']);
 | |
|   {$ENDIF}
 | |
|   IncreaseChangeStep;
 | |
|   Code.Replace(FromPos,ToPos-FromPos,NewSrc);
 | |
|   Src:=Code.Source;
 | |
|   SrcLen:=length(Src);
 | |
|   // update positions
 | |
|   DiffPos:=length(NewSrc)-(ToPos-FromPos);
 | |
|   if DiffPos<>0 then begin
 | |
|     Node:=Tree.Root;
 | |
|     while Node<>nil do begin
 | |
|       AdjustPositionAfterInsert(Node.StartPos,true,FromPos,ToPos,DiffPos);
 | |
|       AdjustPositionAfterInsert(Node.EndPos,false,FromPos,ToPos,DiffPos);
 | |
|       Node:=Node.Next;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.IncreaseChangeStep;
 | |
| begin
 | |
|   if FChangeStep<>$7fffffff then
 | |
|     inc(FChangeStep)
 | |
|   else
 | |
|     FChangeStep:=-$7fffffff;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.WriteDebugReport;
 | |
| var
 | |
|   Node: TCodeTreeNode;
 | |
| begin
 | |
|   DebugLn(['TCCodeParserTool.WriteDebugReport ']);
 | |
|   if Tree<>nil then begin
 | |
|     Node:=Tree.Root;
 | |
|     while Node<>nil do begin
 | |
|       DebugLn([GetNodeIndent(Node)+NodeAsString(Node)]);
 | |
|       Node:=Node.Next;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCCodeParserTool.CheckNodeTool(Node: TCodeTreeNode);
 | |
| 
 | |
|   procedure RaiseForeignNode;
 | |
|   begin
 | |
|     RaiseCatchableException('TCCodeParserTool.CheckNodeTool '+DbgSName(Self)+' '+CCNodeDescAsString(Node.Desc));
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if Node=nil then exit;
 | |
|   while Node.Parent<>nil do Node:=Node.Parent;
 | |
|   while Node.PriorBrother<>nil do Node:=Node.PriorBrother;
 | |
|   if (Tree=nil) or (Tree.Root<>Node) then
 | |
|     RaiseForeignNode;
 | |
| end;
 | |
| 
 | |
| function TCCodeParserTool.NodeAsString(Node: TCodeTreeNode): string;
 | |
| begin
 | |
|   if Node=nil then begin
 | |
|     Result:='nil';
 | |
|     exit;
 | |
|   end;
 | |
|   case Node.Desc of
 | |
|   ccnName, ccnTypeName: Result:=copy(Src,Node.StartPos,Node.EndPos-Node.StartPos);
 | |
|   else Result:='';
 | |
|   end;
 | |
|   Result:=Result+'('+CCNodeDescAsString(Node.Desc,Node.SubDesc)
 | |
|                     +' at '+CleanPosToStr(Node.StartPos)+')';
 | |
| end;
 | |
| 
 | |
| procedure InternalFinal;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   if KeyWordLists<>nil then begin
 | |
|     for i:=0 to KeyWordLists.Count-1 do
 | |
|       TObject(KeyWordLists[i]).Free;
 | |
|     FreeAndNil(KeyWordLists);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| finalization
 | |
|   InternalFinal;
 | |
| 
 | |
| end.
 | |
| 
 | 
