{ *************************************************************************** * * * 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 . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** Author: Mattias Gaertner Abstract: TPascalParserTool enhances TMultiKeyWordListCodeTool. This tool parses the pascal code, makes simple syntax checks and provides a lot of useful parsing functions. It can either parse complete sources or parts of it. } unit PascalParserTool; {$ifdef FPC}{$mode objfpc}{$endif}{$H+} interface {$I codetools.inc} { $DEFINE ShowIgnoreErrorAfter} { $DEFINE VerboseUpdateNeeded} uses {$IFDEF MEM_CHECK} MemCheck, {$ENDIF} Classes, SysUtils, FileProcs, CodeToolsStrConsts, CodeTree, CodeAtom, CustomCodeTool, MultiKeyWordListTool, KeywordFuncLists, BasicCodeTools, CodeToolsStructs, LinkScanner, CodeCache, AVL_Tree; type TProcHeadAttribute = ( // extract attributes: phpWithStart, // proc keyword e.g. 'function', 'class procedure' phpWithoutClassKeyword,// without 'class' proc keyword phpAddClassName, // extract/add 'ClassName.' phpWithoutClassName, // skip classname phpWithoutName, // skip function name phpWithoutParamList, // skip param list phpWithVarModifiers, // extract 'var', 'out', 'const' phpWithParameterNames, // extract parameter names phpWithoutParamTypes, // skip colon, param types and default values phpWithHasDefaultValues,// extract the equal sign of default values phpWithDefaultValues, // extract default values phpWithResultType, // extract colon + result type phpWithOfObject, // extract 'of object' phpWithCallingSpecs, // extract cdecl; extdecl; popstack; phpWithProcModifiers, // extract forward; alias; external; ... phpWithComments, // extract comments and spaces phpInUpperCase, // turn to uppercase phpCommentsToSpace, // replace comments with a single space // (default is to skip unnecessary space, // e.g 'Do ;' normally becomes 'Do;' // with this option you get 'Do ;') phpWithoutBrackets, // skip start- and end-bracket of parameter list phpWithoutSemicolon, // skip semicolon at end phpDoNotAddSemicolon, // do not add missing semicolon at end // search attributes: phpIgnoreForwards, // skip forward procs phpIgnoreProcsWithBody,// skip procs with begin..end phpIgnoreMethods, // skip method bodies and definitions phpOnlyWithClassname, // skip procs without the right classname phpFindCleanPosition, // read til ExtractSearchPos // parse attributes: phpCreateNodes // create nodes during reading ); TProcHeadAttributes = set of TProcHeadAttribute; TParseProcHeadAttribute = (pphIsMethod, pphIsFunction, pphIsType, pphIsOperator, pphCreateNodes); TParseProcHeadAttributes = set of TParseProcHeadAttribute; TProcHeadExtractPos = (phepNone, phepStart, phepName, phepParamList, phepResultType, phepSpecifiers); TSkipBracketCheck = ( sbcStopOnRecord, sbcStopOnSemicolon ); TSkipBracketChecks = set of TSkipBracketCheck; TTreeRange = (trInterface, trAll, trTillCursor, trTillCursorSection); TBuildTreeFlag = ( btSetIgnoreErrorPos, btKeepIgnoreErrorPos, btLoadDirtySource, btCursorPosOutAllowed ); TBuildTreeFlags = set of TBuildTreeFlag; { TPascalParserTool } TPascalParserTool = class(TMultiKeyWordListCodeTool) private protected ExtractMemStream: TMemoryStream; ExtractSearchPos: integer; ExtractFoundPos: integer; ExtractProcHeadPos: TProcHeadExtractPos; procedure RaiseCharExpectedButAtomFound(c: char); procedure RaiseStringExpectedButAtomFound(const s: string); procedure RaiseUnexpectedKeyWord; procedure RaiseIllegalQualifier; procedure RaiseEndOfSourceExpected; protected // code extraction procedure InitExtraction; function GetExtraction(InUpperCase: boolean): string; function ExtractStreamEndIsIdentChar: boolean; procedure ExtractNextAtom(AddAtom: boolean; Attr: TProcHeadAttributes); // sections function KeyWordFuncSection: boolean; function KeyWordFuncEndPoint: boolean; // type/var/const/resourcestring function KeyWordFuncType: boolean; function KeyWordFuncVar: boolean; function KeyWordFuncConst: boolean; function KeyWordFuncResourceString: boolean; function KeyWordFuncExports: boolean; function KeyWordFuncLabel: boolean; function KeyWordFuncProperty: boolean; // types procedure ReadEqualsType; function KeyWordFuncClass: boolean; function KeyWordFuncClassInterface: boolean; function KeyWordFuncTypePacked: boolean; function KeyWordFuncTypeBitPacked: boolean; function KeyWordFuncSpecialize: boolean; function KeyWordFuncTypeArray: boolean; function KeyWordFuncTypeProc: boolean; function KeyWordFuncTypeSet: boolean; function KeyWordFuncTypeLabel: boolean; function KeyWordFuncTypeType: boolean; function KeyWordFuncTypeFile: boolean; function KeyWordFuncTypePointer: boolean; function KeyWordFuncTypeRecord: boolean; function KeyWordFuncTypeRecordCase: boolean; function KeyWordFuncTypeDefault: boolean; // procedures/functions/methods function KeyWordFuncProc: boolean; function KeyWordFuncBeginEnd: boolean; // class/object elements function KeyWordFuncClassSection: boolean; function KeyWordFuncClassTypeSection: boolean; function KeyWordFuncClassVarSection: boolean; function KeyWordFuncClassClass: boolean; function KeyWordFuncClassMethod: boolean; function KeyWordFuncClassProperty: boolean; function KeyWordFuncClassIdentifier: boolean; function KeyWordFuncClassVarTypeClass: boolean; function KeyWordFuncClassVarTypePacked: boolean; function KeyWordFuncClassVarTypeBitPacked: boolean; function KeyWordFuncClassVarTypeRecord: boolean; function KeyWordFuncClassVarTypeArray: boolean; function KeyWordFuncClassVarTypeSet: boolean; function KeyWordFuncClassVarTypeProc: boolean; function KeyWordFuncClassVarTypeIdent: boolean; // keyword lists procedure BuildDefaultKeyWordFunctions; override; function ParseType(StartPos, WordLen: integer): boolean; function ParseInnerClass(StartPos, WordLen: integer): boolean; function ParseClassVarType(StartPos, WordLen: integer): boolean; function SkipInnerClassInterface(StartPos, WordLen: integer): boolean; function UnexpectedKeyWord: boolean; function EndOfSourceExpected: boolean; // read functions function ReadTilProcedureHeadEnd(ParseAttr: TParseProcHeadAttributes; var HasForwardModifier: boolean): boolean; function ReadConstant(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; function ReadParamType(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; function ReadParamList(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; function ReadUsesSection(ExceptionOnError: boolean): boolean; function ReadRequiresSection(ExceptionOnError: boolean): boolean; function ReadContainsSection(ExceptionOnError: boolean): boolean; function ReadSubRange(ExceptionOnError: boolean): boolean; function ReadTilBracketCloseOrUnexpected(ExceptionOnNotFound: boolean; Flags: TSkipBracketChecks): boolean; function ReadTilBlockEnd(StopOnBlockMiddlePart, CreateNodes: boolean): boolean; function ReadTilBlockStatementEnd(ExceptionOnNotFound: boolean): boolean; function ReadBackTilBlockEnd(StopOnBlockMiddlePart: boolean): boolean; function ReadTilVariableEnd(ExceptionOnError, WithAsOperator: boolean): boolean; function ReadTilStatementEnd(ExceptionOnError, CreateNodes: boolean): boolean; function ReadWithStatement(ExceptionOnError, CreateNodes: boolean): boolean; function ReadOnStatement(ExceptionOnError, CreateNodes: boolean): boolean; procedure ReadVariableType; function ReadTilTypeOfProperty(PropertyNode: TCodeTreeNode): boolean; procedure ReadGUID; procedure ReadClassInheritance(CreateChildNodes: boolean); procedure ReadSpecialize(CreateChildNodes: boolean); function WordIsPropertyEnd: boolean; public CurSection: TCodeTreeNodeDesc; InterfaceSectionFound: boolean; ImplementationSectionFound: boolean; EndOfSourceFound: boolean; procedure ValidateToolDependencies; virtual; procedure BuildTree(OnlyInterfaceNeeded: boolean); procedure BuildTreeAndGetCleanPos(TreeRange: TTreeRange; const CursorPos: TCodeXYPosition; out CleanCursorPos: integer; BuildTreeFlags: TBuildTreeFlags); procedure BuildSubTreeForClass(ClassNode: TCodeTreeNode); virtual; procedure BuildSubTreeForBeginBlock(BeginNode: TCodeTreeNode); virtual; procedure BuildSubTreeForProcHead(ProcNode: TCodeTreeNode); virtual; procedure BuildSubTreeForProcHead(ProcNode: TCodeTreeNode; out FunctionResult: TCodeTreeNode); procedure BuildSubTree(CleanCursorPos: integer); virtual; procedure BuildSubTree(ANode: TCodeTreeNode); virtual; function NodeNeedsBuildSubTree(ANode: TCodeTreeNode): boolean; virtual; function BuildSubTreeAndFindDeepestNodeAtPos( P: integer; ExceptionOnNotFound: boolean): TCodeTreeNode; function BuildSubTreeAndFindDeepestNodeAtPos(StartNode: TCodeTreeNode; P: integer; ExceptionOnNotFound: boolean): TCodeTreeNode; function DoAtom: boolean; override; function FindFirstNodeOnSameLvl(StartNode: TCodeTreeNode): TCodeTreeNode; function FindNextNodeOnSameLvl(StartNode: TCodeTreeNode): TCodeTreeNode; function FindPrevNodeOnSameLvl(StartNode: TCodeTreeNode): TCodeTreeNode; // sections function FindInterfaceNode: TCodeTreeNode; function FindImplementationNode: TCodeTreeNode; function FindInitializationNode: TCodeTreeNode; function FindFinalizationNode: TCodeTreeNode; function FindMainBeginEndNode: TCodeTreeNode; function FindFirstSectionChild: TCodeTreeNode; function NodeHasParentOfType(ANode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): boolean; constructor Create; destructor Destroy; override; procedure CalcMemSize(Stats: TCTMemStats); override; end; const ProcHeadAttributeNames: array[TProcHeadAttribute] of string = ( // extract attributes: 'phpWithStart', 'phpWithoutClassKeyword', 'phpAddClassName', 'phpWithoutClassName', 'phpWithoutName', 'phpWithoutParamList', 'phpWithVarModifiers', 'phpWithParameterNames', 'phpWithoutParamTypes', 'phpWithHasDefaultValues', 'phpWithDefaultValues', 'phpWithResultType', 'phpWithOfObject', 'phpWithCallingSpecs', 'phpWithProcModifiers', 'phpWithComments', 'phpInUpperCase', 'phpCommentsToSpace', 'phpWithoutBrackets', 'phpWithoutSemicolon', 'phpDoNotAddSemicolon', // search attributes: 'phpIgnoreForwards', 'phpIgnoreProcsWithBody', 'phpIgnoreMethods', 'phpOnlyWithClassname', 'phpFindCleanPosition', // parse attributes: 'phpCreateNodes' ); function ProcHeadAttributesToStr(Attr: TProcHeadAttributes): string; implementation type TEndBlockType = (ebtBegin, ebtAsm, ebtTry, ebtCase, ebtRepeat, ebtRecord, ebtClass, ebtObject); TTryType = (ttNone, ttFinally, ttExcept); function ProcHeadAttributesToStr(Attr: TProcHeadAttributes): string; var a: TProcHeadAttribute; begin Result:=''; for a:=Low(TProcHeadAttribute) to High(TProcHeadAttribute) do begin if a in Attr then begin if Result<>'' then Result:=Result+','; Result:=Result+ProcHeadAttributeNames[a]; end; end; end; { TPascalParserTool } constructor TPascalParserTool.Create; begin inherited Create; end; destructor TPascalParserTool.Destroy; begin if ExtractMemStream<>nil then ExtractMemStream.Free; inherited Destroy; end; procedure TPascalParserTool.CalcMemSize(Stats: TCTMemStats); begin inherited CalcMemSize(Stats); if ExtractMemStream<>nil then Stats.Add('TPascalParserTool.ExtractMemStream', ExtractMemStream.InstanceSize+ExtractMemStream.Size); end; procedure TPascalParserTool.BuildDefaultKeyWordFunctions; begin inherited BuildDefaultKeyWordFunctions; with KeyWordFuncList do begin Add('PROGRAM',@KeyWordFuncSection); Add('LIBRARY',@KeyWordFuncSection); Add('PACKAGE',@KeyWordFuncSection); Add('UNIT',@KeyWordFuncSection); Add('INTERFACE',@KeyWordFuncSection); Add('IMPLEMENTATION',@KeyWordFuncSection); Add('INITIALIZATION',@KeyWordFuncSection); Add('FINALIZATION',@KeyWordFuncSection); Add('END',@KeyWordFuncEndPoint); Add('.',@KeyWordFuncEndPoint); Add('TYPE',@KeyWordFuncType); Add('VAR',@KeyWordFuncVar); Add('THREADVAR',@KeyWordFuncVar); Add('CONST',@KeyWordFuncConst); Add('RESOURCESTRING',@KeyWordFuncResourceString); Add('EXPORTS',@KeyWordFuncExports); Add('LABEL',@KeyWordFuncLabel); Add('PROPERTY',@KeyWordFuncProperty); Add('PROCEDURE',@KeyWordFuncProc); Add('FUNCTION',@KeyWordFuncProc); Add('CONSTRUCTOR',@KeyWordFuncProc); Add('DESTRUCTOR',@KeyWordFuncProc); Add('OPERATOR',@KeyWordFuncProc); Add('CLASS',@KeyWordFuncProc); Add('BEGIN',@KeyWordFuncBeginEnd); Add('ASM',@KeyWordFuncBeginEnd); DefaultKeyWordFunction:=@EndOfSourceExpected; end; end; function TPascalParserTool.ParseType(StartPos, WordLen: integer): boolean; // KeyWordFunctions for parsing types var p: PChar; begin if StartPos>SrcLen then exit(false); p:=@Src[StartPos]; case UpChars[p^] of 'A': if CompareSrcIdentifiers('ARRAY',p) then exit(KeyWordFuncTypeArray); 'B': if CompareSrcIdentifiers('BITPACKED',p) then exit(KeyWordFuncTypeBitPacked); 'C': case UpChars[p[1]] of 'L': if CompareSrcIdentifiers('CLASS',p) then exit(KeyWordFuncClass); 'P': if CompareSrcIdentifiers('CPPCLASS',p) then exit(KeyWordFuncClass); end; 'D': if CompareSrcIdentifiers('DISPINTERFACE',p) then exit(KeyWordFuncClassInterface); 'F': case UpChars[p[1]] of 'I': if CompareSrcIdentifiers('FILE',p) then exit(KeyWordFuncTypeFile); 'U': if CompareSrcIdentifiers('FUNCTION',p) then exit(KeyWordFuncTypeProc); end; 'I': if CompareSrcIdentifiers('INTERFACE',p) then exit(KeyWordFuncClassInterface); 'L': if CompareSrcIdentifiers('LABEL',p) then exit(KeyWordFuncTypeLabel); 'O': if CompareSrcIdentifiers('OBJECT',p) or CompareSrcIdentifiers('OBJCCLASS',p) or CompareSrcIdentifiers('OBJCCATEGORY',p) then exit(KeyWordFuncClass) else if CompareSrcIdentifiers('OBJCPROTOCOL',p) then exit(KeyWordFuncClassInterface); 'P': case UpChars[p[1]] of 'A': if CompareSrcIdentifiers('PACKED',p) then exit(KeyWordFuncTypePacked); 'R': if CompareSrcIdentifiers('PROCEDURE',p) then exit(KeyWordFuncTypeProc); end; 'R': if CompareSrcIdentifiers('RECORD',p) then exit(KeyWordFuncTypeRecord); 'S': case UpChars[p[1]] of 'E': if CompareSrcIdentifiers('SET',p) then exit(KeyWordFuncTypeSet); 'P': if CompareSrcIdentifiers('SPECIALIZE',p) then exit(KeyWordFuncSpecialize); end; 'T': if CompareSrcIdentifiers('TYPE',p) then exit(KeyWordFuncTypeType); '^': if WordLen=1 then exit(KeyWordFuncTypePointer); end; Result:=KeyWordFuncTypeDefault; end; function TPascalParserTool.ParseInnerClass(StartPos, WordLen: integer ): boolean; // KeyWordFunctions for parsing in a class/object var p: PChar; begin if StartPos>SrcLen then exit(false); p:=@Src[StartPos]; case UpChars[p^] of 'C': case UpChars[p[1]] of 'L': if CompareSrcIdentifiers(p,'CLASS') then exit(KeyWordFuncClassClass); 'O': if CompareSrcIdentifiers(p,'CONSTRUCTOR') then exit(KeyWordFuncClassMethod); end; 'D': if CompareSrcIdentifiers(p,'DESTRUCTOR') then exit(KeyWordFuncClassMethod); 'E': if CompareSrcIdentifiers(p,'END') then exit(false); 'F': if CompareSrcIdentifiers(p,'FUNCTION') then exit(KeyWordFuncClassMethod); 'P': case UpChars[p[1]] of 'R': case UpChars[p[2]] of 'I': if CompareSrcIdentifiers(p,'PRIVATE') then exit(KeyWordFuncClassSection); 'O': case UpChars[p[3]] of 'C': if CompareSrcIdentifiers(p,'PROCEDURE') then exit(KeyWordFuncClassMethod); 'P': if CompareSrcIdentifiers(p,'PROPERTY') then exit(KeyWordFuncClassProperty); 'T': if CompareSrcIdentifiers(p,'PROTECTED') then exit(KeyWordFuncClassSection); end; end; 'U': if (UpChars[p[2]]='B') and (UpChars[p[3]]='L') and (UpChars[p[4]]='I') then case UpChars[p[5]] of 'C': if CompareSrcIdentifiers(p,'PUBLIC') then exit(KeyWordFuncClassSection); 'S': if CompareSrcIdentifiers(p,'PUBLISHED') then exit(KeyWordFuncClassSection); end; end; 'S': if CompareSrcIdentifiers(p,'STATIC') then exit(KeyWordFuncClassMethod) else if CompareSrcIdentifiers(p,'STRICT') then exit(KeyWordFuncClassSection); 'T': if CompareSrcIdentifiers(p,'TYPE') then exit(KeyWordFuncClassTypeSection); 'V': if CompareSrcIdentifiers(p,'VAR') then exit(KeyWordFuncClassVarSection); '(','[': begin ReadTilBracketClose(true); exit(true); end; ';': exit(true); end; Result:=KeyWordFuncClassIdentifier; end; function TPascalParserTool.ParseClassVarType(StartPos, WordLen: integer ): boolean; // KeywordFunctions for parsing the type of a variable in a class/object var p: PChar; begin if StartPos>SrcLen then exit(false); p:=@Src[StartPos]; case UpChars[p^] of 'A': if CompareSrcIdentifiers('ARRAY',p) then exit(KeyWordFuncClassVarTypeArray); 'B': if CompareSrcIdentifiers('BITPACKED',p) then exit(KeyWordFuncClassVarTypeBitPacked); 'C': if CompareSrcIdentifiers('CLASS',p) then exit(KeyWordFuncClassVarTypeClass); 'F': if CompareSrcIdentifiers('FUNCTION',p) then exit(KeyWordFuncClassVarTypeProc); 'O': if CompareSrcIdentifiers('OBJECT',p) then exit(KeyWordFuncClassVarTypeClass); 'P': case UpChars[p[1]] of 'A': if CompareSrcIdentifiers('PACKED',p) then exit(KeyWordFuncClassVarTypePacked); 'R': if CompareSrcIdentifiers('PROCEDURE',p) then exit(KeyWordFuncClassVarTypeProc); end; 'R': if CompareSrcIdentifiers('RECORD',p) then exit(KeyWordFuncClassVarTypeRecord); 'S': if CompareSrcIdentifiers('SET',p) then exit(KeyWordFuncClassVarTypeSet); end; Result:=KeyWordFuncClassVarTypeIdent; end; function TPascalParserTool.SkipInnerClassInterface(StartPos, WordLen: integer ): boolean; // KeyWordFunctions for skipping in a class interface, dispinterface var p: PChar; begin if StartPos>SrcLen then exit(false); p:=@Src[StartPos]; case UpChars[p^] of 'E': if CompareSrcIdentifiers(p,'END') then exit(false); 'F': if CompareSrcIdentifiers(p,'FUNCTION') then exit(KeyWordFuncClassMethod); 'P': if (UpChars[p[1]]='R') and (UpChars[p[2]]='O') then case UpChars[p[3]] of 'C': if CompareSrcIdentifiers(p,'PROCEDURE') then exit(KeyWordFuncClassMethod); 'P': if CompareSrcIdentifiers(p,'PROPERTY') then exit(KeyWordFuncClassProperty); end; '(','[': begin ReadTilBracketClose(true); exit(true); end; ';': exit(true); end; Result:=false; end; function TPascalParserTool.UnexpectedKeyWord: boolean; begin Result:=false; SaveRaiseExceptionFmt(ctsUnexpectedKeyword,[GetAtom],true); end; function TPascalParserTool.EndOfSourceExpected: boolean; begin Result:=false; RaiseEndOfSourceExpected; end; procedure TPascalParserTool.BuildTree(OnlyInterfaceNeeded: boolean); var SourceType: TCodeTreeNodeDesc; Node: TCodeTreeNode; begin {$IFDEF MEM_CHECK}CheckHeap('TBasicCodeTool.BuildTree A '+IntToStr(MemCheck_GetMem_Cnt));{$ENDIF} {$IFDEF CTDEBUG} DebugLn('TPascalParserTool.BuildTree A ',MainFilename); {$ENDIF} ValidateToolDependencies; if not UpdateNeeded(OnlyInterfaceNeeded) then begin // input is the same as last time -> output is the same // => if there was an error, raise it again //debugln(['TPascalParserTool.BuildTree ',ord(LastErrorPhase),' ',IgnoreErrorAfterValid]); if (LastErrorPhase in [CodeToolPhaseScan,CodeToolPhaseParse]) then begin // last time a parsing error occurred if IgnoreErrorAfterValid and IgnoreErrorAfterPositionIsInFrontOfLastErrMessage then begin // last error is behind needed code // => ignore exit; end; //debugln(['TPascalParserTool.BuildTree ',MainFilename,' OnlyInterfaceNeeded=',OnlyInterfaceNeeded,' ImplementationSectionFound=',ImplementationSectionFound]); if OnlyInterfaceNeeded and ImplementationSectionFound then begin Node:=FindImplementationNode; if (Node<>nil) and not LastErrorIsInFrontOfCleanedPos(Node.StartPos) then begin // last error was after interface section and only interface is needed // => ignore exit; end; end; RaiseLastError; end; exit; end; ClearLastError; //DebugLn('TPascalParserTool.BuildTree B OnlyIntf=',dbgs(OnlyInterfaceNeeded),' ',TCodeBuffer(Scanner.MainCode).Filename); //CheckHeap('TBasicCodeTool.BuildTree B '+IntToStr(MemCheck_GetMem_Cnt)); // scan code BeginParsing(true,OnlyInterfaceNeeded); {$IFDEF VerboseUpdateNeeded} if FForceUpdateNeeded=true then DebugLn(['TCustomCodeTool.BuildTree FForceUpdateNeeded:=false ',MainFilename]); {$ENDIF} FForceUpdateNeeded:=false; // parse code and build codetree CurrentPhase:=CodeToolPhaseParse; if Scanner.CompilerMode=cmDELPHI then WordIsKeyWordFuncList:=WordIsDelphiKeyWord else WordIsKeyWordFuncList:=WordIsKeyWord; InterfaceSectionFound:=false; ImplementationSectionFound:=false; EndOfSourceFound:=false; try ReadNextAtom; if UpAtomIs('UNIT') then CurSection:=ctnUnit else if UpAtomIs('PROGRAM') then CurSection:=ctnProgram else if UpAtomIs('PACKAGE') then CurSection:=ctnPackage else if UpAtomIs('LIBRARY') then CurSection:=ctnLibrary else SaveRaiseExceptionFmt(ctsNoPascalCodeFound,[GetAtom],true); SourceType:=CurSection; CreateChildNode; CurNode.Desc:=CurSection; ReadNextAtom; // read source name AtomIsIdentifier(true); ReadNextAtom; // read ';' (or 'platform;' or 'unimplemented;') if UpAtomIs('PLATFORM') then ReadNextAtom; if UpAtomIs('UNIMPLEMENTED') then ReadNextAtom; if UpAtomIs('LIBRARY') then ReadNextAtom; if UpAtomIs('EXPERIMENTAL') then ReadNextAtom; if UpAtomIs('DEPRECATED') then ReadNextAtom; if (CurPos.Flag<>cafSemicolon) then RaiseCharExpectedButAtomFound(';'); if CurSection=ctnUnit then begin ReadNextAtom; CurNode.EndPos:=CurPos.StartPos; EndChildNode; //DebugLn(['TPascalParserTool.BuildTree ',MainFilename,' ',Scanner.NestedComments]); if not UpAtomIs('INTERFACE') then RaiseStringExpectedButAtomFound('"interface"'); CreateChildNode; CurSection:=ctnInterface; CurNode.Desc:=CurSection; end; InterfaceSectionFound:=true; ReadNextAtom; if UpAtomIs('USES') then ReadUsesSection(true); if (SourceType=ctnPackage) then begin if UpAtomIs('REQUIRES') then ReadRequiresSection(true); if UpAtomIs('CONTAINS') then ReadContainsSection(true); end; repeat //DebugLn('[TPascalParserTool.BuildTree] ALL ',GetAtom); if not DoAtom then break; if CurSection=ctnNone then begin EndOfSourceFound:=true; break; end; ReadNextAtom; until (CurPos.StartPos>SrcLen); FForceUpdateNeeded:=false; except {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildTree ',MainFilename,' ERROR: ',LastErrorMessage); {$ENDIF} if (not IgnoreErrorAfterValid) or (not IgnoreErrorAfterPositionIsInFrontOfLastErrMessage) then raise; FForceUpdateNeeded:=false; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildTree ',MainFilename,' IGNORING ERROR: ',LastErrorMessage); {$ENDIF} end; {$IFDEF CTDEBUG} DebugLn('[TPascalParserTool.BuildTree] END'); {$ENDIF} {$IFDEF MEM_CHECK} CheckHeap('TBasicCodeTool.BuildTree END '+IntToStr(MemCheck_GetMem_Cnt)); {$ENDIF} CurrentPhase:=CodeToolPhaseTool; end; procedure TPascalParserTool.BuildSubTreeForClass(ClassNode: TCodeTreeNode); // reparse a quick parsed class and build the child nodes procedure RaiseClassDescInvalid; begin RaiseException('[TPascalParserTool.BuildSubTreeForClass] ClassNode.Desc=' +ClassNode.DescAsString,true); end; procedure RaiseClassKeyWordExpected; begin RaiseException( 'TPascalParserTool.BuildSubTreeForClass:' +' class/object keyword expected, but '+GetAtom+' found',true); end; var OldPhase: integer; begin if (ClassNode.SubDesc and ctnsNeedJITParsing)=0 then // class already parsed exit; if not (ClassNode.Desc in AllClassObjects) then RaiseClassDescInvalid; // avoid endless loop OldPhase:=CurrentPhase; CurrentPhase:=CodeToolPhaseParse; try if (ctnsHasParseError and ClassNode.SubDesc)>0 then RaiseNodeParserError(ClassNode); // set CursorPos after class head MoveCursorToNodeStart(ClassNode); // parse // - sealed, abstract // - inheritage // - class sections (GUID, type, var, public, published, private, protected) // - methods (procedures, functions, constructors, destructors) // read the "class"/"object" keyword ReadNextAtom; if UpAtomIs('PACKED') or (UpAtomIs('BITPACKED')) then ReadNextAtom; if not (UpAtomIs('CLASS') or UpAtomIs('OBJECT') or UpAtomIs('OBJCCLASS') or UpAtomIs('OBJCCATEGORY') or UpAtomIs('CPPCLASS') or UpAtomIs('INTERFACE') or UpAtomIs('OBJCPROTOCOL')) then RaiseClassKeyWordExpected; ReadNextAtom; // parse modifiers : if CurPos.Flag=cafWord then begin if UpAtomIs('SEALED') then begin while UpAtomIs('SEALED') do begin CreateChildNode; CurNode.Desc:=ctnClassSealed; CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; end; end else if UpAtomIs('ABSTRACT') then begin while UpAtomIs('ABSTRACT') do begin CreateChildNode; CurNode.Desc:=ctnClassAbstract; CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; end; end; end; // parse the inheritage if CurPos.Flag=cafRoundBracketOpen then ReadClassInheritance(true) else UndoReadNextAtom; // clear the last atoms LastAtoms.Clear; // start the first class section (always published) CreateChildNode; CurNode.Desc:=ctnClassPublished; CurNode.StartPos:=CurPos.EndPos; // behind 'class' including the space ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then ReadGUID; // parse till "end" of class/object repeat //DebugLn(['TPascalParserTool.BuildSubTreeForClass Atom=',GetAtom,' ',CurPos.StartPos>=ClassNode.EndPos]); if CurPos.StartPos>=ClassNode.EndPos then break; if not ParseInnerClass(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then break; ReadNextAtom; until false; // end last class section (public, private, ...) CurNode.EndPos:=CurPos.StartPos; EndChildNode; CurrentPhase:=OldPhase; ClassNode.SubDesc:=ClassNode.SubDesc and (not ctnsNeedJITParsing); except CurrentPhase:=OldPhase; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForClass ',MainFilename,' ERROR: ',LastErrorMessage); {$ENDIF} if (not IgnoreErrorAfterValid) or (not IgnoreErrorAfterPositionIsInFrontOfLastErrMessage) then raise; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForClass',MainFilename,' IGNORING ERROR: ',LastErrorMessage); {$ENDIF} end; end; procedure TPascalParserTool.BuildSubTreeForBeginBlock(BeginNode: TCodeTreeNode); // reparse a quick parsed begin..end block and build the child nodes // create nodes for 'with' and 'case' statements procedure RaiseBeginExpected; begin SaveRaiseException( 'TPascalParserTool.BuildSubTreeForBeginBlock: begin expected, but ' +GetAtom+' found',true); end; var MaxPos, OldPhase: integer; begin if BeginNode=nil then RaiseException( 'TPascalParserTool.BuildSubTreeForBeginBlock: BeginNode=nil'); if BeginNode.Desc<>ctnBeginBlock then RaiseException( 'TPascalParserTool.BuildSubTreeForBeginBlock: BeginNode.Desc=' +BeginNode.DescAsString); if (BeginNode.SubDesc and ctnsNeedJITParsing)=0 then // block already parsed exit; OldPhase:=CurrentPhase; CurrentPhase:=CodeToolPhaseParse; try if (ctnsHasParseError and BeginNode.SubDesc)>0 then RaiseNodeParserError(BeginNode); // set CursorPos on 'begin' MoveCursorToNodeStart(BeginNode); ReadNextAtom; if not UpAtomIs('BEGIN') then RaiseBeginExpected; if BeginNode.EndPos=MaxPos); CurrentPhase:=OldPhase; BeginNode.SubDesc:=BeginNode.SubDesc and (not ctnsNeedJITParsing); except CurrentPhase:=OldPhase; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForBeginBlock ',MainFilename,' ERROR: ',LastErrorMessage); {$ENDIF} if (not IgnoreErrorAfterValid) or (not IgnoreErrorAfterPositionIsInFrontOfLastErrMessage) then begin raise; end; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForBeginBlock ',MainFilename,' IGNORING ERROR: ',LastErrorMessage); {$ENDIF} end; end; function TPascalParserTool.KeyWordFuncClassIdentifier: boolean; { parse class variable or type examples for variables: Name: TypeName; Name: UnitName.TypeName; i, j: integer; MyArray: array of array[EnumType] of array [Range] of TypeName; MyRecord: record i: packed record j: integer; k: record end; case integer of 0: (a: integer); 1,2,3: (b: array[char] of char; c: char); 3: ( d: record case byte of 10: (i: integer; ); 11: (y: byte); end; end; end; MyPointer: ^integer; MyEnum: (MyEnumm1, MyEnumm2 := 2, MyEnummy3); MySet: set of (MyEnummy4 := 4 , MyEnummy5); MyRange: 3..5; } begin if CurNode.Desc in AllClassTypeSections then begin // create type definition node CreateChildNode; CurNode.Desc:=ctnTypeDefinition; ReadEqualsType; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else begin // create variable definition node CreateChildNode; CurNode.Desc:=ctnVarDefinition; ReadNextAtom; while CurPos.Flag=cafComma do begin // end variable definition CurNode.EndPos:=CurPos.StartPos; EndChildNode; // read next variable name ReadNextAtom; AtomIsIdentifier(true); // create variable definition node CreateChildNode; CurNode.Desc:=ctnVarDefinition; ReadNextAtom; end; if CurPos.Flag<>cafColon then RaiseCharExpectedButAtomFound(':'); // read type ReadVariableType; end; Result:=true; end; function TPascalParserTool.KeyWordFuncClassVarTypeClass: boolean; // class and object as type are not allowed, because they would have no name begin SaveRaiseExceptionFmt(ctsAnonymDefinitionsAreNotAllowed,[GetAtom]); Result:=false; end; function TPascalParserTool.KeyWordFuncClassVarTypePacked: boolean; // 'packed' record begin ReadNextAtom; if CurPos.Flag=cafRECORD then Result:=KeyWordFuncClassVarTypeRecord else begin RaiseStringExpectedButAtomFound('"record"'); Result:=true; end; end; function TPascalParserTool.KeyWordFuncClassVarTypeBitPacked: boolean; // 'bitpacked' array begin ReadNextAtom; if UpAtomIs('ARRAY') then Result:=KeyWordFuncClassVarTypeArray else begin RaiseStringExpectedButAtomFound('"array"'); Result:=true; end; end; function TPascalParserTool.KeyWordFuncClassVarTypeRecord: boolean; { read variable type 'record' examples: record i: packed record j: integer; k: record end; case integer of 0: (a: integer); 1,2,3: (b: array[char] of char; c: char); 3: ( d: record case byte of 10: (i: integer; ); 11: (y: byte); end; end; end; } var Level: integer; begin Level:=1; while (CurPos.StartPos<=SrcLen) and (Level>0) do begin ReadNextAtom; if CurPos.Flag=cafRECORD then inc(Level) else if (CurPos.Flag=cafEND) then dec(Level); end; if CurPos.StartPos>SrcLen then SaveRaiseException(ctsEndForRecordNotFound); Result:=true; end; function TPascalParserTool.KeyWordFuncClassVarTypeArray: boolean; { read variable type 'array' examples: array of array[EnumType] of array [Range] of TypeName; } begin ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin // array[Range] ReadTilBracketClose(true); ReadNextAtom; end; if not UpAtomIs('OF') then RaiseCharExpectedButAtomFound('['); ReadNextAtom; Result:=ParseClassVarType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); end; function TPascalParserTool.KeyWordFuncClassVarTypeSet: boolean; { read variable type 'set of' examples: set of Name set of (MyEnummy4 := 4 , MyEnummy5); } begin CreateChildNode; CurNode.Desc:=ctnSetType; ReadNextAtom; if not UpAtomIs('OF') then RaiseStringExpectedButAtomFound('"of"'); ReadNextAtom; if CurPos.StartPos>SrcLen then SaveRaiseException(ctsMissingEnumList); if IsIdentStartChar[Src[CurPos.StartPos]] then // set of identifier else if CurPos.Flag=cafRoundBracketOpen then // set of () ReadTilBracketClose(true); CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncClassVarTypeProc: boolean; { read variable type 'procedure ...' or 'function ... : ...' examples: procedure function : integer; procedure (a: char) of object; } var IsFunction, HasForwardModifier: boolean; ParseAttr: TParseProcHeadAttributes; begin //DebugLn('[TPascalParserTool.KeyWordFuncClassVarTypeProc]'); IsFunction:=UpAtomIs('FUNCTION'); ReadNextAtom; HasForwardModifier:=false; ParseAttr:=[pphIsMethod,pphIsType]; if IsFunction then Include(ParseAttr,pphIsFunction); ReadTilProcedureHeadEnd(ParseAttr,HasForwardModifier); Result:=true; end; function TPascalParserTool.KeyWordFuncClassVarTypeIdent: boolean; // read variable type begin if CurPos.StartPos>SrcLen then SaveRaiseException(ctsMissingTypeIdentifier); if IsIdentStartChar[Src[CurPos.StartPos]] then // identifier else SaveRaiseException(ctsMissingTypeIdentifier); Result:=true; end; function TPascalParserTool.KeyWordFuncClassSection: boolean; // change section in a class (public, private, protected, published) begin // end last section CurNode.EndPos:=CurPos.StartPos; EndChildNode; // start new section CreateChildNode; if UpAtomIs('STRICT') then ReadNextAtom; if UpAtomIs('PUBLIC') then CurNode.Desc:=ctnClassPublic else if UpAtomIs('PRIVATE') then CurNode.Desc:=ctnClassPrivate else if UpAtomIs('PROTECTED') then CurNode.Desc:=ctnClassProtected else if UpAtomIs('PUBLISHED') then CurNode.Desc:=ctnClassPublished else RaiseStringExpectedButAtomFound('public'); Result:=true; end; function TPascalParserTool.KeyWordFuncClassTypeSection: boolean; begin // end last section CurNode.EndPos:=CurPos.StartPos; EndChildNode; // start new section CreateChildNode; if UpAtomIs('CLASS') then ReadNextAtom; ReadNextAtom; if UpAtomIs('PUBLIC') then CurNode.Desc:=ctnClassTypePublic else if UpAtomIs('PRIVATE') then CurNode.Desc:=ctnClassTypePrivate else if UpAtomIs('PROTECTED') then CurNode.Desc:=ctnClassTypeProtected else if UpAtomIs('PUBLISHED') then CurNode.Desc:=ctnClassTypePublished else begin if CurNode.PriorBrother<>nil then begin case CurNode.PriorBrother.Desc of ctnClassPrivate: CurNode.Desc:=ctnClassTypePrivate; ctnClassProtected: CurNode.Desc:=ctnClassTypeProtected; ctnClassPublic: CurNode.Desc:=ctnClassTypePublic; ctnClassPublished: CurNode.Desc:=ctnClassTypePublished; else RaiseStringExpectedButAtomFound('public'); end; end; UndoReadNextAtom; end; Result:=true; end; function TPascalParserTool.KeyWordFuncClassVarSection: boolean; { var private var protected var public var published class var private } begin // end last section CurNode.EndPos:=CurPos.StartPos; EndChildNode; // start new section CreateChildNode; CurNode.Desc:=ctnClassVarPublic; if UpAtomIs('CLASS') then ReadNextAtom; ReadNextAtom; if UpAtomIs('PUBLIC') then CurNode.Desc:=ctnClassVarPublic else if UpAtomIs('PRIVATE') then CurNode.Desc:=ctnClassVarPrivate else if UpAtomIs('PROTECTED') then CurNode.Desc:=ctnClassVarProtected else if UpAtomIs('PUBLISHED') then CurNode.Desc:=ctnClassVarPublished else RaiseStringExpectedButAtomFound('public'); Result:=true; end; function TPascalParserTool.KeyWordFuncClassClass: boolean; { parse class procedure class property class constructor class destructor class var class type } begin ReadNextAtom; if UpAtomIs('PROCEDURE') or UpAtomIs('FUNCTION') or UpAtomIs('CONSTRUCTOR') or UpAtomIs('DESTRUCTOR') then begin UndoReadNextAtom; Result:=KeyWordFuncClassMethod; end else if UpAtomIs('PROPERTY') then begin UndoReadNextAtom; Result:=KeyWordFuncClassProperty; end else if UpAtomIs('VAR') then begin UndoReadNextAtom; Result:=KeyWordFuncClassVarSection; end else RaiseStringExpectedButAtomFound('procedure'); end; function TPascalParserTool.KeyWordFuncClassMethod: boolean; { parse class method examples: procedure ProcName; virtual; abstract; function FuncName(Parameter1: Type1; Parameter2: Type2): ResultType; constructor Create; destructor Destroy; override; class function X: integer; static function X: integer; function Intf.Method = ImplementingMethodName; proc specifiers without parameters: stdcall, virtual, abstract, dynamic, overload, override, cdecl, inline, compilerproc proc specifiers with parameters: message dispid enumerator } var IsFunction, HasForwardModifier: boolean; ParseAttr: TParseProcHeadAttributes; begin if not (CurNode.Desc in (AllClassSections+AllClassInterfaces)) then RaiseIdentExpectedButAtomFound; HasForwardModifier:=false; // create class method node CreateChildNode; CurNode.Desc:=ctnProcedure; // read method keyword if UpAtomIs('CLASS') or (UpAtomIs('STATIC')) then begin ReadNextAtom; if (not UpAtomIs('PROCEDURE')) and (not UpAtomIs('FUNCTION')) and (not UpAtomIs('CONSTRUCTOR')) and (not UpAtomIs('DESTRUCTOR')) then begin RaiseStringExpectedButAtomFound(ctsProcedureOrFunctionOrConstructorOrDestructor); end; end; IsFunction:=UpAtomIs('FUNCTION'); // read procedure head // read name ReadNextAtom; AtomIsIdentifier(true); // create node for procedure head CreateChildNode; CurNode.Desc:=ctnProcedureHead; ReadNextAtom; if (CurPos.Flag<>cafPoint) then begin // read rest CurNode.SubDesc:=ctnsNeedJITParsing; ParseAttr:=[pphIsMethod]; if IsFunction then Include(ParseAttr,pphIsFunction); ReadTilProcedureHeadEnd(ParseAttr,HasForwardModifier); end else begin // Method resolution clause (e.g. function Intf.Method = Method_Name) CurNode.Parent.Desc:=ctnMethodMap; // read Method name of interface ReadNextAtom; AtomIsIdentifier(true); //DebugLn(['TPascalParserTool.KeyWordFuncClassMethod ',GetAtom,' at ',CleanPosToStr(CurPos.StartPos,true)]); // read '=' ReadNextAtomIsChar('='); // read implementing method name ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag<>cafSemicolon then UndoReadNextAtom; end; // close procedure header CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close procedure / method map CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.ReadParamList(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; { parse parameter list examples: procedure ProcName; virtual; abstract; function FuncName(Parameter1: Type1; Parameter2: Type2): ResultType; constructor Create; destructor Destroy; override; class function X: integer; function QWidget_mouseGrabber(): QWidgetH; cdecl; procedure Intf.Method = ImplementingMethodName; function CommitUrlCacheEntry; // only Delphi procedure MacProcName(c: char; ...); external; proc specifiers without parameters: stdcall, virtual, abstract, dynamic, overload, override, cdecl, inline proc specifiers with parameters: message ; external; external ; external name delayed; external name ; external index ; [alias: ] [external name ] [internconst:in_const_round, external name 'FPC_ROUND']; dispid ; } var CloseBracket: char; Desc: TCodeTreeNodeDesc; Node: TCodeTreeNode; procedure ReadPrefixModifier; begin // read parameter prefix modifier if UpAtomIs('VAR') or UpAtomIs('CONST') or (UpAtomIs('OUT') and (Scanner.CompilerMode in [cmOBJFPC,cmDELPHI,cmFPC])) then begin Desc:=ctnVarDefinition; if not Extract then ReadNextAtom else ExtractNextAtom(phpWithVarModifiers in Attr,Attr); end else Desc:=ctnVarDefinition; end; procedure ReadDefaultValue; begin // read = if not Extract then ReadNextAtom else ExtractNextAtom([phpWithDefaultValues,phpWithHasDefaultValues]*Attr<>[],Attr); ReadConstant(ExceptionOnError, Extract and (phpWithDefaultValues in Attr),Attr); if (phpCreateNodes in Attr) then begin Node:=CurNode; Node.SubDesc:=Node.SubDesc+ctnsHasDefaultValue; Node:=Node.PriorBrother; while (Node<>nil) and (Node.FirstChild=nil) do begin Node.SubDesc:=Node.SubDesc+ctnsHasDefaultValue; Node:=Node.PriorBrother; end; end; end; begin Result:=false; if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin if CurPos.Flag=cafRoundBracketOpen then CloseBracket:=')' else CloseBracket:=']'; if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnParameterList; end; if not Extract then ReadNextAtom else ExtractNextAtom(not (phpWithoutBrackets in Attr),Attr); end else CloseBracket:=#0; if not (CurPos.Flag in [cafRoundBracketClose,cafEdgedBracketClose]) then begin repeat if AtomIs('...') then begin // MacPas '...' VarArgs parameter if (Scanner.CompilerMode<>cmMacPas) then begin if ExceptionOnError then RaiseIdentExpectedButAtomFound else exit; end; ReadNextAtom; // parse end of parameter list if (CurPos.StartPos>SrcLen) or (Src[CurPos.StartPos]<>CloseBracket) then if ExceptionOnError then RaiseCharExpectedButAtomFound(CloseBracket) else exit; break; end else begin ReadPrefixModifier; // read parameter name(s) repeat if not AtomIsIdentifier(ExceptionOnError) then exit; if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=Desc; end; if not Extract then ReadNextAtom else ExtractNextAtom(phpWithParameterNames in Attr,Attr); if CurPos.Flag<>cafComma then break else begin if (phpCreateNodes in Attr) then begin CurNode.EndPos:=LastAtoms.GetValueAt(0).EndPos; EndChildNode; end; if not Extract then ReadNextAtom else ExtractNextAtom(not (phpWithoutParamList in Attr),Attr); end; until false; // read parameter type if CurPos.Flag=cafColon then begin if not Extract then ReadNextAtom else ExtractNextAtom([phpWithoutParamList,phpWithoutParamTypes]*Attr=[], Attr); if not ReadParamType(ExceptionOnError,Extract,Attr) then exit; if CurPos.Flag=cafEqual then begin // read default value ReadDefaultValue; end; end else if (CurPos.Flag in [cafSemicolon,cafRoundBracketClose, cafEdgedBracketClose]) then begin // no type -> variant if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnVariantType; CurNode.EndPos:=CurNode.StartPos; EndChildNode; end; end else break; if (phpCreateNodes in Attr) then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; end; // read next parameter if (CurPos.StartPos>SrcLen) then if ExceptionOnError then RaiseCharExpectedButAtomFound(CloseBracket) else exit; if (CurPos.Flag in [cafRoundBracketClose,cafEdgedBracketClose]) then break; if (CurPos.Flag<>cafSemicolon) then if ExceptionOnError then RaiseCharExpectedButAtomFound(';') else exit; if not Extract then ReadNextAtom else ExtractNextAtom(not (phpWithoutParamList in Attr),Attr); until false; end; if (CloseBracket<>#0) then begin if Src[CurPos.StartPos]<>CloseBracket then begin if ExceptionOnError then RaiseCharExpectedButAtomFound(CloseBracket) else exit; end; if (phpCreateNodes in Attr) then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; if not Extract then ReadNextAtom else ExtractNextAtom(not (phpWithoutBrackets in Attr),Attr); end; Result:=true; end; function TPascalParserTool.ReadParamType(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; // after reading, CurPos is the atom after the type var copying: boolean; IsArrayType: Boolean; IsFileType: Boolean; NeedIdentifier: boolean; begin copying:=[phpWithoutParamList,phpWithoutParamTypes]*Attr=[]; Result:=false; if CurPos.Flag in AllCommonAtomWords then begin NeedIdentifier:=true; IsArrayType:=UpAtomIs('ARRAY'); if IsArrayType then begin //DebugLn(['TPascalParserTool.ReadParamType is array ',MainFilename,' ',CleanPosToStr(curPos.StartPos)]); if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnOpenArrayType; end; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if not UpAtomIs('OF') then if ExceptionOnError then RaiseStringExpectedButAtomFound('"of"') else exit; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if UpAtomIs('CONST') then begin if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnOfConstType; end; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if (phpCreateNodes in Attr) then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close ctnOpenArrayType CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; Result:=true; exit; end; end; IsFileType:=UpAtomIs('FILE'); if IsFileType then begin if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnFileType; end; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if UpAtomIs('OF') then begin if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); end else begin NeedIdentifier:=false; end; end; if NeedIdentifier then begin if not AtomIsIdentifier(ExceptionOnError) then exit; if (phpCreateNodes in Attr) then begin CreateChildNode; CurNode.Desc:=ctnIdentifier; CurNode.EndPos:=CurPos.EndPos; end; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if CurPos.Flag=cafPoint then begin // first identifier was unitname -> read '.' + identifier if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); if not AtomIsIdentifier(ExceptionOnError) then exit; if not Extract then ReadNextAtom else ExtractNextAtom(copying,Attr); end; if (phpCreateNodes in Attr) then EndChildNode; end; if (phpCreateNodes in Attr) then begin if IsFileType then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; if IsArrayType then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; end; end else begin if ExceptionOnError then RaiseStringExpectedButAtomFound(ctsIdentifier) else exit; end; Result:=true; end; function TPascalParserTool.ReadTilProcedureHeadEnd( ParseAttr: TParseProcHeadAttributes; var HasForwardModifier: boolean): boolean; { parse parameter list, result type, of object, method specifiers examples: procedure ProcName; virtual; abstract; function FuncName(Parameter1: Type1; Parameter2: Type2): ResultType; constructor Create; destructor Destroy; override; class function X: integer; function QWidget_mouseGrabber(): QWidgetH; cdecl; procedure Intf.Method = ImplementingMethodName; function CommitUrlCacheEntry; // only Delphi procedure MacProcName(c: char; ...); external; Delphi mode: Function TPOSControler.Logout; // missing function type proc specifiers without parameters: stdcall, virtual, abstract, dynamic, overload, override, cdecl, inline proc specifiers with parameters: message ; external; external ; external name delayed; external name ; external index ; [alias: ] [external name ] [internconst:in_const_round, external name 'FPC_ROUND']; dispid ; enumerator } procedure RaiseKeyWordExampleExpected; begin SaveRaiseExceptionFmt( ctsKeywordExampleExpectedButAtomFound,['alias',GetAtom]); end; var IsSpecifier: boolean; Attr: TProcHeadAttributes; begin //DebugLn('[TPascalParserTool.ReadTilProcedureHeadEnd] ', //'Method=',IsMethod,', Function=',IsFunction,', Type=',IsType); Result:=true; HasForwardModifier:=false; if CurPos.Flag=cafRoundBracketOpen then begin Attr:=[]; if pphCreateNodes in ParseAttr then Include(Attr,phpCreateNodes); ReadParamList(true,false,Attr); end; if (pphIsOperator in ParseAttr) and (CurPos.Flag<>cafColon) then begin // read operator result identifier AtomIsIdentifier(true); if (pphCreateNodes in ParseAttr) then begin CreateChildNode; CurNode.Desc:=ctnVarDefinition; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; ReadNextAtom; end; if ([pphIsFunction,pphIsOperator]*ParseAttr<>[]) then begin // read function result type if CurPos.Flag=cafColon then begin ReadNextAtom; AtomIsIdentifier(true); if (pphCreateNodes in ParseAttr) then begin CreateChildNode; CurNode.Desc:=ctnIdentifier; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; ReadNextAtom; if CurPos.Flag=cafPoint then begin // unitname.identifier -> read identifier ReadNextAtom; AtomIsIdentifier(true); if (pphCreateNodes in ParseAttr) then begin CreateChildNode; CurNode.Desc:=ctnIdentifier; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; ReadNextAtom; end; end else begin if (Scanner.CompilerMode<>cmDelphi) then RaiseCharExpectedButAtomFound(':') else begin // Delphi Mode if CurPos.Flag=cafEqual then begin // read interface alias ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; end; end; end; if UpAtomIs('OF') then begin // read 'of object' if not (pphIsType in ParseAttr) then RaiseCharExpectedButAtomFound(';'); ReadNextAtom; if not UpAtomIs('OBJECT') then RaiseStringExpectedButAtomFound('"object"'); ReadNextAtom; end; // read procedures/method specifiers if CurPos.Flag=cafEND then begin UndoReadNextAtom; exit; end; if CurPos.Flag=cafSemicolon then ReadNextAtom; if (CurPos.StartPos>SrcLen) then SaveRaiseException(ctsSemicolonNotFound); repeat if (pphIsMethod in ParseAttr) then IsSpecifier:=IsKeyWordMethodSpecifier.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) else IsSpecifier:=IsKeyWordProcedureSpecifier.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); if IsSpecifier then begin // read specifier if UpAtomIs('MESSAGE') or UpAtomIs('DISPID') or UpAtomIs('ENUMERATOR') or UpAtomIs('DEPRECATED') then begin ReadNextAtom; if not (CurPos.Flag in [cafSemicolon,cafEND]) then ReadConstant(true,false,[]); end else if UpAtomIs('EXTERNAL') or UpAtomIs('WEAKEXTERNAL') or UpAtomIs('PUBLIC') then begin HasForwardModifier:=UpAtomIs('EXTERNAL') or UpAtomIs('WEAKEXTERNAL'); ReadNextAtom; if CurPos.Flag<>cafSemicolon then begin if not UpAtomIs('NAME') then ReadConstant(true,false,[]); if UpAtomIs('NAME') or UpAtomIs('INDEX') then begin ReadNextAtom; ReadConstant(true,false,[]); end; if UpAtomIs('DELAYED') then ReadNextAtom; end; end else if UpAtomIs('ALIAS') then begin if not ReadNextAtomIsChar(':') then RaiseCharExpectedButAtomFound(':'); ReadNextAtom; ReadConstant(true,false,[]); end else if CurPos.Flag=cafEdgedBracketOpen then begin // read assembler alias [public,alias: 'alternative name'], // internproc, internconst, external repeat ReadNextAtom; if not (CurPos.Flag in AllCommonAtomWords) then RaiseStringExpectedButAtomFound(ctsKeyword); if not IsKeyWordProcedureBracketSpecifier.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then RaiseKeyWordExampleExpected; if UpAtomIs('INTERNPROC') then HasForwardModifier:=true; if UpAtomIs('INTERNCONST') then begin ReadNextAtom; if AtomIsChar(':') then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; end else if UpAtomIs('EXTERNAL') then begin HasForwardModifier:=true; ReadNextAtom; if not (CurPos.Flag in [cafComma,cafEdgedBracketClose]) then begin if not UpAtomIs('NAME') then ReadConstant(true,false,[]); if UpAtomIs('NAME') or UpAtomIs('INDEX') then begin ReadNextAtom; ReadConstant(true,false,[]); end; end; end else ReadNextAtom; if CurPos.Flag in [cafColon,cafEdgedBracketClose] then break; if CurPos.Flag<>cafComma then RaiseCharExpectedButAtomFound(']'); until false; if CurPos.Flag=cafColon then begin ReadNextAtom; if (not AtomIsStringConstant) and (not AtomIsIdentifier(false)) then RaiseStringExpectedButAtomFound(ctsStringConstant); ReadConstant(true,false,[]); end; if CurPos.Flag<>cafEdgedBracketClose then RaiseCharExpectedButAtomFound(']'); ReadNextAtom; if CurPos.Flag=cafEND then begin UndoReadNextAtom; exit; end; end else begin // read specifier without parameters if UpAtomIs('FORWARD') then HasForwardModifier:=true; ReadNextAtom; if CurPos.Flag=cafEND then begin UndoReadNextAtom; exit; end; end; // check semicolon if CurPos.Flag=cafSemicolon then begin ReadNextAtom; end else begin // Delphi/FPC allow procs without ending semicolon end; end else begin // current atom does not belong to procedure/method declaration UndoReadNextAtom; // unread unknown atom break; end; until false; end; function TPascalParserTool.ReadConstant(ExceptionOnError, Extract: boolean; const Attr: TProcHeadAttributes): boolean; // after reading, the CurPos will be on the atom after the constant var BracketType: TCommonAtomFlag; c: char; begin Result:=false; if CurPos.Flag in AllCommonAtomWords then begin // word (identifier or keyword) if AtomIsKeyWord and (not IsKeyWordInConstAllowed.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)) then begin if ExceptionOnError then RaiseUnexpectedKeyWord else exit; end; if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if CurPos.Flag=cafPoint then begin // Unitname.Constant if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); Result:=ReadConstant(ExceptionOnError,Extract,Attr); exit; end; if WordIsTermOperator.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin // identifier + operator + ? if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); Result:=ReadConstant(ExceptionOnError,Extract,Attr); exit; end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then begin // type cast or constant array BracketType:=CurPos.Flag; if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if not ReadConstant(ExceptionOnError,Extract,Attr) then exit; if (BracketType=cafRoundBracketOpen) and (CurPos.Flag<>cafRoundBracketClose) then if ExceptionOnError then RaiseCharExpectedButAtomFound('(') else exit; if (BracketType=cafEdgedBracketOpen) and (CurPos.Flag<>cafEdgedBracketClose) then if ExceptionOnError then RaiseCharExpectedButAtomFound('[') else exit; if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); end; end else if AtomIsNumber or AtomIsStringConstant then begin // number or '...' or #... if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if WordIsTermOperator.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin // number + operator + ? if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); Result:=ReadConstant(ExceptionOnError,Extract,Attr); exit; end; end else begin if CurPos.EndPos-CurPos.StartPos=1 then begin c:=Src[CurPos.StartPos]; case c of '(': begin // open bracket + ? + close bracket if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if not ReadConstant(ExceptionOnError,Extract,Attr) then exit; if (c='(') and (CurPos.Flag<>cafRoundBracketClose) then if ExceptionOnError then RaiseCharExpectedButAtomFound(')') else exit; if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if WordIsTermOperator.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin // open bracket + ? + close bracket + operator + ? if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); Result:=ReadConstant(ExceptionOnError,Extract,Attr); exit; end; end; '[': begin // open bracket + ? + close bracket if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); repeat if (CurPos.Flag=cafEdgedBracketClose) then break; // read if not ReadConstant(ExceptionOnError,Extract,Attr) then exit; if (CurPos.Flag=cafComma) or AtomIs('..') then begin // continue if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); end else if (CurPos.Flag<>cafEdgedBracketClose) then begin if ExceptionOnError then RaiseCharExpectedButAtomFound(']') else exit; end; until false; if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if WordIsTermOperator.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin // open bracket + ? + close bracket + operator + ? if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); Result:=ReadConstant(ExceptionOnError,Extract,Attr); exit; end; end; '+','-': begin // sign if not Extract then ReadNextAtom else ExtractNextAtom(true,Attr); if not ReadConstant(ExceptionOnError,Extract,Attr) then exit; end; else if ExceptionOnError then RaiseStringExpectedButAtomFound(ctsConstant) else exit; end; end else // syntax error if ExceptionOnError then RaiseStringExpectedButAtomFound(ctsConstant) else exit; end; Result:=true; end; function TPascalParserTool.ReadUsesSection( ExceptionOnError: boolean): boolean; { parse uses section examples: uses name1, name2 in '', name3; } begin CreateChildNode; CurNode.Desc:=ctnUsesSection; repeat ReadNextAtom; // read name if CurPos.Flag=cafSemicolon then break; AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnUseUnit; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; if UpAtomIs('IN') then begin ReadNextAtom; if not AtomIsStringConstant then if ExceptionOnError then RaiseStringExpectedButAtomFound(ctsStringConstant) else exit; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; end; EndChildNode; if CurPos.Flag=cafSemicolon then break; if CurPos.Flag<>cafComma then if ExceptionOnError then RaiseCharExpectedButAtomFound(';') else exit; until (CurPos.StartPos>SrcLen); CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; Result:=true; end; function TPascalParserTool.ReadRequiresSection(ExceptionOnError: boolean ): boolean; { parse requires section examples: requires name1, name2, name3; } begin CreateChildNode; CurNode.Desc:=ctnRequiresSection; repeat ReadNextAtom; // read name if CurPos.Flag=cafSemicolon then break; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag=cafSemicolon then break; if CurPos.Flag<>cafComma then if ExceptionOnError then RaiseCharExpectedButAtomFound(';') else exit; until (CurPos.StartPos>SrcLen); CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; Result:=true; end; function TPascalParserTool.ReadContainsSection(ExceptionOnError: boolean ): boolean; { parse contains section examples: contains name1, name2 in '', name3; } begin CreateChildNode; CurNode.Desc:=ctnContainsSection; repeat ReadNextAtom; // read name if CurPos.Flag=cafSemicolon then break; AtomIsIdentifier(true); ReadNextAtom; if UpAtomIs('IN') then begin ReadNextAtom; if not AtomIsStringConstant then if ExceptionOnError then RaiseStringExpectedButAtomFound(ctsStringConstant) else exit; ReadNextAtom; end; if CurPos.Flag=cafSemicolon then break; if CurPos.Flag<>cafComma then if ExceptionOnError then RaiseCharExpectedButAtomFound(';') else exit; until (CurPos.StartPos>SrcLen); CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; Result:=true; end; function TPascalParserTool.ReadSubRange(ExceptionOnError: boolean): boolean; { parse subrange till ',' ';' ':' ']' or ')' examples: number..number identifier Low(identifier)..High(identifier) Pred(identifier)..Succ(identifier) } var RangeOpFound: boolean; begin RangeOpFound:=false; repeat if CurPos.Flag in [cafSemicolon,cafColon,cafComma,cafRoundBracketClose, cafEdgedBracketClose] then break; if CurPos.StartPos>SrcLen then RaiseCharExpectedButAtomFound(';'); if AtomIs('..') then begin if RangeOpFound then RaiseCharExpectedButAtomFound(';'); RangeOpFound:=true; end else if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then ReadTilBracketClose(ExceptionOnError); ReadNextAtom; until false; Result:=true; end; function TPascalParserTool.ReadTilBracketCloseOrUnexpected( ExceptionOnNotFound: boolean; Flags: TSkipBracketChecks): boolean; { Cursor must be on round/edged bracket open After parsing cursor will be on closing bracket or on the unexpected atom } type TStackItemType = ( siNone, siRoundBracketOpen, siEdgedBracketOpen, siRecord ); TStackItem = record Typ: TStackItemType; StartPos: integer; end; PStackItem = ^TStackItem; var Stack: array[0..16] of TStackItem; ExtStack: PStackItem; ExtStackCapacity: integer; Ptr: integer; Top: TStackItemType; p: PChar; procedure Push(Item: TStackItemType); var p: Integer; begin inc(Ptr); if Ptr<=High(Stack) then begin Stack[Ptr].Typ:=Item; Stack[Ptr].StartPos:=CurPos.StartPos; end else begin // need ExStack if (ExtStack=nil) then begin ExtStackCapacity:=10; GetMem(ExtStack,SizeOf(TStackItem)*ExtStackCapacity); end else begin ExtStackCapacity:=ExtStackCapacity*2; ReAllocMem(ExtStack,SizeOf(TStackItem)*ExtStackCapacity); end; p:=Ptr-High(Stack)-1; ExtStack[p].Typ:=Item; ExtStack[p].StartPos:=CurPos.StartPos; end; Top:=Item; end; procedure Pop; begin dec(Ptr); if Ptr<0 then Top:=siNone else if Ptr<=High(Stack) then Top:=Stack[Ptr].Typ else Top:=ExtStack[Ptr-High(Stack)-1].Typ; end; function GetTopPos: integer; begin if Ptr<0 then Result:=0 else if Ptr<=High(Stack) then Result:=Stack[Ptr].StartPos else Result:=ExtStack[Ptr-High(Stack)-1].StartPos; end; procedure Unexpected; var p: LongInt; Msg: String; begin ReadTilBracketCloseOrUnexpected:=false; if not ExceptionOnNotFound then exit; // the unexpected keyword is wrong, but probably the closing bracket is // missing and the method has read too far p:=GetTopPos; CleanPosToCaret(p,ErrorNicePosition); case Top of siNone: Msg:='closing bracket not found'; siRoundBracketOpen: Msg:='bracket ) not found'; siEdgedBracketOpen: Msg:='bracket ] not found'; siRecord: Msg:='record end not found'; end; if CurPos.StartPos<=SrcLen then Msg:=Msg+', found unexpected '+GetAtom +' at '+CleanPosToRelativeStr(CurPos.StartPos,ErrorNicePosition); SaveRaiseException(Msg,not CleanPosToCaret(p,ErrorNicePosition)); end; begin Result:=true; Ptr:=-1; ExtStack:=nil; if CurPos.Flag=cafRoundBracketOpen then Push(siRoundBracketOpen) else if CurPos.Flag=cafEdgedBracketOpen then Push(siEdgedBracketOpen) else RaiseBracketOpenExpectedButAtomFound; try repeat ReadNextAtom; //debugln(['TPascalParserTool.ReadTilBracketCloseOrUnexpected ',GetAtom]); case CurPos.Flag of cafNone: if CurPos.StartPos>SrcLen then Unexpected; cafSemicolon: if sbcStopOnSemicolon in Flags then Unexpected; cafRoundBracketOpen: Push(siRoundBracketOpen); cafRoundBracketClose: if Top=siRoundBracketOpen then begin if Ptr=0 then exit(true); Pop; end else Unexpected; cafEdgedBracketOpen: Push(siEdgedBracketOpen); cafEdgedBracketClose: if Top=siEdgedBracketOpen then begin if Ptr=0 then exit(true); Pop; end else Unexpected; cafWord: begin p:=@Src[CurPos.StartPos]; case UpChars[p^] of 'A': case UpChars[p[1]] of 'S': if UpAtomIs('ASM') then Unexpected; end; 'B': case UpChars[p[1]] of 'E': if UpAtomIs('BEGIN') then Unexpected; end; 'C': case UpChars[p[1]] of 'O': if UpAtomIs('CONST') then Unexpected; end; 'D': case UpChars[p[1]] of 'O': if UpAtomIs('DO') then Unexpected; end; 'E': if UpAtomIs('END') then begin if Top=siRecord then Pop else Unexpected; end; 'I': case UpChars[p[1]] of 'N': case UpChars[p[2]] of 'I': if UpAtomIs('INITIALIZATION') then Unexpected; 'T': if UpAtomIs('INTERFACE') then Unexpected; end; 'M': if UpAtomIs('IMPLEMENTATION') then Unexpected; end; 'F': case UpChars[p[1]] of 'I': if UpAtomIs('FINALIZATION') or UpAtomIs('FINALLY') then Unexpected; 'O': if UpAtomIs('FOR') then Unexpected; end; 'L': case UpChars[p[1]] of 'A': if UpAtomIs('LABEL') then Unexpected; end; 'P': case UpChars[p[1]] of 'U': case UpChars[p[2]] of 'B': if UpAtomIs('PUBLIC') or UpAtomIs('PUBLISHED') then Unexpected; end; 'R': case UpChars[p[2]] of 'I': if UpAtomIs('PRIVATE') then Unexpected; 'O': if UpAtomIs('PROTECTED') then Unexpected; end; end; 'R': case UpChars[p[1]] of 'E': case UpChars[p[2]] of 'C': if UpAtomIs('RECORD') then begin if sbcStopOnRecord in Flags then Unexpected else Push(siRecord); end; 'P': if UpAtomIs('REPEAT') then Unexpected; 'S': if UpAtomIs('RESOURCESTRING') then Unexpected; end; end; 'T': case UpChars[p[1]] of 'R': if UpAtomIs('TRY') then Unexpected; end; 'V': case UpChars[p[1]] of 'A': if UpAtomIs('VAR') then Unexpected; end; 'W': case UpChars[p[1]] of 'H': if UpAtomIs('WHILE') then Unexpected; end; end; end; end; until false; finally if ExtStack<>nil then FreeMem(ExtStack); end; end; function TPascalParserTool.KeyWordFuncClassProperty: boolean; { parse class/object property examples: property Visible; property Count: integer; property Color: TColor read FColor write SetColor; property Items[Index1, Index2: integer]: integer read GetItems; default; property X: integer index 1 read GetCoords write SetCoords stored IsStored; deprecated; property Col8: ICol8 read FCol8 write FCol8 implements ICol8, IColor; property Value: Integer read FCurrent; enumerator Current; property Visible: WordBool readonly dispid 401; property specifiers before semicolon: index , read , write , stored , default , implements [,...], nodefault for dispinterfaces: dispid , readonly, writeonly property modifiers after semicolon: default, deprecated, enumerator } procedure RaiseSemicolonAfterPropSpecMissing(const s: string); begin SaveRaiseExceptionFmt(ctsSemicolonAfterPropSpecMissing,[s,GetAtom]); end; begin if not (CurNode.Desc in (AllClassBaseSections+AllClassInterfaces)) then RaiseIdentExpectedButAtomFound; // create class method node CreateChildNode; CurNode.Desc:=ctnProperty; // read property Name if UpAtomIs('CLASS') then begin ReadNextAtom; if not UpAtomIs('PROPERTY') then RaiseStringExpectedButAtomFound('property'); end; ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin // read parameter list ReadTilBracketClose(true); ReadNextAtom; end; while (CurPos.StartPos<=SrcLen) do begin case CurPos.Flag of cafSemicolon: break; cafEnd: break; cafWord: if WordIsPropertyEnd then break; end; ReadNextAtom; end; if CurPos.Flag=cafSemicolon then begin // read modifiers ReadNextAtom; if UpAtomIs('DEFAULT') then begin ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseSemicolonAfterPropSpecMissing('default'); end else if UpAtomIs('NODEFAULT') then begin ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseSemicolonAfterPropSpecMissing('nodefault'); end else if UpAtomIs('ENUMERATOR') then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseSemicolonAfterPropSpecMissing('enumerator'); end else UndoReadNextAtom; if CurPos.Flag=cafSemicolon then begin // read hint directives ReadNextAtom; if UpAtomIs('DEPRECATED') then begin ReadNextAtom; if AtomIsStringConstant then ReadConstant(true,false,[]); if CurPos.Flag<>cafSemicolon then RaiseSemicolonAfterPropSpecMissing('deprecated'); end else if UpAtomIs('PLATFORM') or UpAtomIs('UNIMPLEMENTED') or UpAtomIs('EXPERIMENTAL') then begin ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseSemicolonAfterPropSpecMissing('hint directive'); end else UndoReadNextAtom; end; end else UndoReadNextAtom; // close property CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.DoAtom: boolean; begin //DebugLn('[TPascalParserTool.DoAtom] A ',DbgS(CurKeyWordFuncList)); if (CurPos.StartPos<=SrcLen) and (CurPos.EndPos>CurPos.StartPos) then begin if IsIdentStartChar[Src[CurPos.StartPos]] then Result:=KeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) else begin if Src[CurPos.StartPos] in ['(','['] then ReadTilBracketClose(true); Result:=true; end; end else Result:=false; end; function TPascalParserTool.KeyWordFuncSection: boolean; // parse section keywords (program, unit, interface, implementation, ...) procedure RaiseUnexpectedSectionKeyWord; begin SaveRaiseExceptionFmt(ctsUnknownSectionKeyword,[GetAtom]); end; begin if UpAtomIs('IMPLEMENTATION') then begin if not (CurSection in [ctnInterface,ctnUnit,ctnLibrary,ctnPackage]) then RaiseUnexpectedSectionKeyWord; // close section node CurNode.EndPos:=CurPos.StartPos; EndChildNode; ImplementationSectionFound:=true; // start implementation section node CreateChildNode; CurNode.Desc:=ctnImplementation; CurSection:=ctnImplementation; ReadNextAtom; if UpAtomIs('USES') then ReadUsesSection(true); UndoReadNextAtom; Result:=true; end else if (UpAtomIs('INITIALIZATION') or UpAtomIs('FINALIZATION')) then begin if UpAtomIs('INITIALIZATION') and (not CurSection in [ctnInterface,ctnImplementation, ctnUnit,ctnLibrary,ctnPackage]) then RaiseUnexpectedSectionKeyWord; if UpAtomIs('FINALIZATION') and (not CurSection in [ctnInterface,ctnImplementation,ctnInitialization, ctnUnit,ctnLibrary,ctnPackage]) then RaiseUnexpectedSectionKeyWord; // close section node CurNode.EndPos:=CurPos.StartPos; EndChildNode; // start initialization / finalization section node CreateChildNode; if UpAtomIs('INITIALIZATION') then begin CurNode.Desc:=ctnInitialization; end else CurNode.Desc:=ctnFinalization; CurSection:=CurNode.Desc; repeat ReadNextAtom; if (CurSection=ctnInitialization) and UpAtomIs('FINALIZATION') then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; CreateChildNode; CurNode.Desc:=ctnFinalization; CurSection:=CurNode.Desc; end else if EndKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then begin ReadTilBlockEnd(false,false); end else if CurPos.Flag=cafEND then begin Result:=KeyWordFuncEndPoint; break; end; until (CurPos.StartPos>SrcLen); Result:=true; end else begin RaiseUnexpectedSectionKeyWord; end; Result:=true; end; function TPascalParserTool.KeyWordFuncEndPoint: boolean; // keyword 'end' or '.' (source end.) var LastNodeEnd: LongInt; begin if CurPos.Flag=cafPoint then begin if not LastUpAtomIs(0,'END') then RaiseIllegalQualifier; UndoReadNextAtom; if CurNode.Desc in [ctnInterface] then RaiseStringExpectedButAtomFound('"implementation"'); if not (CurNode.Desc in [ctnImplementation,ctnInitialization, ctnFinalization,ctnProgram,ctnLibrary]) then begin ReadNextAtom; SaveRaiseException(ctsUnexpectedEndOfSource+' 1'); end; end else if CurPos.Flag=cafEND then begin if LastAtomIs(0,'@') then RaiseStringExpectedButAtomFound(ctsIdentifier); if LastAtomIs(0,'@@') then begin // for Delphi compatibility @@end is allowed Result:=true; exit; end; end else SaveRaiseException('[TPascalParserTool.KeyWordFuncEndPoint] internal error'); if CurNode.Desc in [ctnBeginBlock] then CurNode.EndPos:=CurPos.EndPos else CurNode.EndPos:=CurPos.StartPos; LastNodeEnd:=CurNode.EndPos; EndChildNode; CreateChildNode; CurNode.Desc:=ctnEndPoint; CurNode.StartPos:=LastNodeEnd; ReadNextAtom; if CurPos.Flag<>cafPoint then RaiseCharExpectedButAtomFound('.'); CurNode.EndPos:=CurPos.EndPos; EndChildNode; CurSection:=ctnNone; Result:=true; end; function TPascalParserTool.KeyWordFuncProc: boolean; // procedure, function, constructor, destructor, operator var ChildCreated: boolean; IsFunction, HasForwardModifier, IsClassProc, IsOperator: boolean; ProcNode: TCodeTreeNode; ParseAttr: TParseProcHeadAttributes; begin if UpAtomIs('CLASS') then begin if not (CurSection in [ctnImplementation]+AllSourceTypes) then RaiseStringExpectedButAtomFound(ctsIdentifier); ReadNextAtom; if UpAtomIs('PROCEDURE') or UpAtomIs('FUNCTION') or UpAtomIs('CONSTRUCTOR') or UpAtomIs('DESTRUCTOR') then IsClassProc:=true else RaiseStringExpectedButAtomFound(ctsProcedureOrFunctionOrConstructorOrDestructor); end else IsClassProc:=false; ChildCreated:=true; if ChildCreated then begin // create node for procedure CreateChildNode; if IsClassProc then CurNode.StartPos:=LastAtoms.GetValueAt(0).StartPos; ProcNode:=CurNode; ProcNode.Desc:=ctnProcedure; if CurSection=ctnInterface then ProcNode.SubDesc:=ctnsForwardDeclaration; end; IsFunction:=UpAtomIs('FUNCTION'); IsOperator:=UpAtomIs('OPERATOR'); ReadNextAtom;// read first atom of head (= name + parameterlist + resulttype;) if not IsOperator then AtomIsIdentifier(true); if ChildCreated then begin // create node for procedure head CreateChildNode; CurNode.Desc:=ctnProcedureHead; CurNode.SubDesc:=ctnsNeedJITParsing; end; ReadNextAtom; if (CurSection<>ctnInterface) and (CurPos.Flag=cafPoint) then begin // read procedure name of a class method (the name after the . ) ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; // read rest of procedure head HasForwardModifier:=false; ParseAttr:=[]; if IsFunction then Include(ParseAttr,pphIsFunction); if IsOperator then Include(ParseAttr,pphIsOperator); ReadTilProcedureHeadEnd(ParseAttr,HasForwardModifier); if ChildCreated then begin if HasForwardModifier then ProcNode.SubDesc:=ctnsForwardDeclaration; // close head CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; if ChildCreated and ((ProcNode.SubDesc and ctnsForwardDeclaration)>0) then begin // close method CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; Result:=true; end; function TPascalParserTool.ReadTilBlockEnd( StopOnBlockMiddlePart, CreateNodes: boolean): boolean; // after reading cursor will be on the keyword ending the block (e.g. 'end') var BlockType: TEndBlockType; TryType: TTryType; BlockStartPos: integer; Desc: TCodeTreeNodeDesc; procedure SaveRaiseExceptionWithBlockStartHint(const AMessage: string); var CaretXY: TCodeXYPosition; begin if (CleanPosToCaret(BlockStartPos,CaretXY)) and (CaretXY.Code<>nil) then begin if CaretXY.Code=TCodeBuffer(Scanner.MainCode) then SaveRaiseException(AMessage+ctsPointStartAt +'('+IntToStr(CaretXY.Y)+','+IntToStr(CaretXY.X)+')') else SaveRaiseException(AMessage+ctsPointStartAt +TCodeBuffer(CaretXY.Code).Filename +'('+IntToStr(CaretXY.Y)+','+IntToStr(CaretXY.X)+')'); end else if (Scanner<>nil) and (Scanner.MainCode<>nil) then begin SaveRaiseException(AMessage); end; end; procedure RaiseUnknownBlockType; begin SaveRaiseException('internal codetool error in ' +'TPascalParserTool.ReadTilBlockEnd: unkown block type: '+GetAtom); end; procedure RaiseStrExpectedWithBlockStartHint(const Msg: string); begin SaveRaiseExceptionWithBlockStartHint( Format(ctsStrExpectedButAtomFound,[Msg,GetAtom])); end; procedure RaiseUnexpectedKeywordInAsmBlock; begin SaveRaiseExceptionFmt(ctsUnexpectedKeywordInAsmBlock,[GetAtom]); end; procedure RaiseUnexpectedKeyWordInBeginEndBlock; begin SaveRaiseExceptionWithBlockStartHint( Format(ctsUnexpectedKeywordInBeginEndBlock,[GetAtom])); end; begin Result:=true; TryType:=ttNone; Desc:=ctnNone; if UpAtomIs('BEGIN') then begin BlockType:=ebtBegin; Desc:=ctnBeginBlock; end else if UpAtomIs('REPEAT') then BlockType:=ebtRepeat else if UpAtomIs('TRY') then BlockType:=ebtTry else if UpAtomIs('CASE') then BlockType:=ebtCase else if UpAtomIs('ASM') then BlockType:=ebtAsm else if CurPos.Flag=cafRECORD then BlockType:=ebtRecord else RaiseUnknownBlockType; if (Desc<>ctnNone) then begin if CreateNodes then begin CreateChildNode; CurNode.Desc:=Desc; end else Desc:=ctnNone; end; BlockStartPos:=CurPos.StartPos; repeat ReadNextAtom; if (CurPos.StartPos>SrcLen) then SaveRaiseExceptionWithBlockStartHint(ctsUnexpectedEndOfSource); if not (CurPos.Flag in AllCommonAtomWords) then continue; if (CurPos.Flag=cafEND) then begin if (BlockType<>ebtAsm) or (CurPos.StartPos=1) or (Src[CurPos.StartPos-1]<>'@') then begin if BlockType=ebtRepeat then RaiseStrExpectedWithBlockStartHint('"until"'); if (BlockType=ebtTry) and (TryType=ttNone) then RaiseStrExpectedWithBlockStartHint('"finally"'); if Desc<>ctnNone then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; ReadNextAtom; if (CurPos.Flag=cafPoint) and (BlockType<>ebtBegin) then begin RaiseCharExpectedButAtomFound(';'); end; UndoReadNextAtom; break; end; end else if EndKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) or UpAtomIs('REPEAT') then begin if BlockType=ebtAsm then RaiseUnexpectedKeywordInAsmBlock; if (BlockType<>ebtRecord) or (not UpAtomIs('CASE')) then ReadTilBlockEnd(false,CreateNodes); end else if UpAtomIs('UNTIL') then begin if BlockType<>ebtRepeat then RaiseStrExpectedWithBlockStartHint('"end"'); if Desc<>ctnNone then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; break; end else if UpAtomIs('FINALLY') then begin if (BlockType=ebtTry) and (TryType=ttNone) then begin if StopOnBlockMiddlePart then break; TryType:=ttFinally; end else RaiseStrExpectedWithBlockStartHint('"end"'); end else if UpAtomIs('EXCEPT') then begin if (BlockType=ebtTry) and (TryType=ttNone) then begin if StopOnBlockMiddlePart then break; TryType:=ttExcept; end else RaiseStrExpectedWithBlockStartHint('"end"'); end else if CreateNodes and UpAtomIs('WITH') then begin ReadWithStatement(true,CreateNodes); end else if CreateNodes and UpAtomIs('ON') and (BlockType=ebtTry) and (TryType=ttExcept) then begin ReadOnStatement(true,CreateNodes); end else begin // check for unexpected keywords case BlockType of ebtBegin,ebtTry,ebtCase,ebtRepeat: if UnexpectedKeyWordInBeginBlock.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then RaiseUnexpectedKeyWordInBeginEndBlock; ebtAsm: if UnexpectedKeyWordInAsmBlock.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then RaiseUnexpectedKeyWordInBeginEndBlock; end; end; until false; end; function TPascalParserTool.ReadTilBlockStatementEnd( ExceptionOnNotFound: boolean): boolean; begin if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then Result:=ReadTilBracketClose(ExceptionOnNotFound) else if WordIsBlockStatementStart.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then Result:=ReadTilBlockEnd(false,false) else Result:=false; end; function TPascalParserTool.ReadBackTilBlockEnd( StopOnBlockMiddlePart: boolean): boolean; // read begin..end, try..finally, case..end, repeat..until, asm..end blocks // backwards var BlockType: TEndBlockType; procedure RaiseBlockError; begin case BlockType of ebtBegin: SaveRaiseExceptionFmt(ctsStrExpectedButAtomFound,['"begin"',GetAtom]); ebtTry: SaveRaiseExceptionFmt(ctsStrExpectedButAtomFound,['"try"',GetAtom]); ebtRepeat: SaveRaiseExceptionFmt(ctsStrExpectedButAtomFound,['"repeat"',GetAtom]); else SaveRaiseExceptionFmt(ctsUnexpectedKeywordWhileReadingBackwards,[GetAtom]); end; end; procedure RaiseUnknownBlockType; begin SaveRaiseException('internal codetool error in ' +'TPascalParserTool.ReadBackTilBlockEnd: unkown block type: '+GetAtom); end; var OldAtom: TAtomPosition; begin Result:=true; if CurPos.Flag=cafEND then BlockType:=ebtBegin else if UpAtomIs('UNTIL') then BlockType:=ebtRepeat else if UpAtomIs('FINALLY') or UpAtomIs('EXCEPT') then BlockType:=ebtTry else RaiseUnknownBlockType; repeat ReadPriorAtom; if (CurPos.StartPos<1) then begin SaveRaiseExceptionFmt(ctsWordNotFound,['begin']); end else if WordIsBlockKeyWord.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then begin if (CurPos.Flag=cafEND) or (UpAtomIs('UNTIL')) then begin ReadBackTilBlockEnd(false); end else if UpAtomIs('BEGIN') or (CurPos.Flag in [cafRECORD]) or UpAtomIs('ASM') then begin if BlockType=ebtBegin then break else RaiseBlockError; end else if UpAtomIs('OBJECT') then begin if BlockType=ebtBegin then begin // could also be 'of object' ReadPriorAtom; if not UpAtomIs('OF') then begin CurPos:=NextPos; NextPos.StartPos:=-1; break; end; end else RaiseBlockError; end else if UpAtomIs('CLASS') then begin ReadNextAtom; if UpAtomIs('FUNCTION') or UpAtomIs('PROCEDURE') or (CurPos.Flag=cafSemicolon) or UpAtomIs('OF') then UndoReadNextAtom else begin UndoReadNextAtom; break; end; end else if UpAtomIs('CASE') then begin // case could also be in a record, then it should not close the block if BlockType=ebtBegin then begin // check if case in a record OldAtom:=CurPos; repeat ReadPriorAtom; if WordIsBlockKeyWord.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then begin if UpAtomIs('CASE') then begin // could be another variant record, -> read further ... end else if CurPos.Flag=cafRECORD then begin // record start found -> the case is a variant record // block start found break; end else begin // this is not a variant record MoveCursorToCleanPos(OldAtom.StartPos); ReadNextAtom; break; end; end; until (CurPos.StartPos<1); break; end else RaiseBlockError; end else if UpAtomIs('REPEAT') then begin if BlockType=ebtRepeat then break else RaiseBlockError; end else if UpAtomIs('FINALLY') or UpAtomIs('EXCEPT') then begin if BlockType=ebtBegin then begin if StopOnBlockMiddlePart then break; BlockType:=ebtTry; end else RaiseBlockError; end else if UpAtomIs('TRY') then begin if BlockType=ebtTry then break else RaiseBlockError; end; end; until false; end; function TPascalParserTool.ReadTilVariableEnd( ExceptionOnError, WithAsOperator: boolean): boolean; { Examples: A A.B^.C[...].D(...).E (...).A @B inherited A A as B } begin while AtomIsChar('@') do ReadNextAtom; while UpAtomIs('INHERITED') do ReadNextAtom; Result:=AtomIsIdentifier(false) or (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]); if not Result then exit; repeat if AtomIsIdentifier(false) then ReadNextAtom; repeat if (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]) then begin Result:=ReadTilBracketClose(ExceptionOnError); if not Result then exit; ReadNextAtom; end else if AtomIsChar('^') then begin ReadNextAtom; end else break; until false; if (CurPos.Flag=cafPoint) or (WithAsOperator and UpAtomIs('AS')) then ReadNextAtom else break; until false; end; function TPascalParserTool.ReadTilStatementEnd(ExceptionOnError, CreateNodes: boolean): boolean; // after reading the current atom will be on the last atom of the statement begin Result:=true; repeat if BlockStatementStartKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then begin if not ReadTilBlockEnd(false,CreateNodes) then exit(false); ReadNextAtom; if CurPos.Flag<>cafSemicolon then UndoReadNextAtom; exit; end else if UpAtomIs('WITH') then begin Result:=ReadWithStatement(ExceptionOnError,CreateNodes); exit; end else begin case CurPos.Flag of cafEND: begin UndoReadNextAtom; exit; end; cafSemicolon: exit; else if CurPos.StartPos>SrcLen then exit; ReadNextAtom; end; end; until false; end; function TPascalParserTool.ReadWithStatement(ExceptionOnError, CreateNodes: boolean): boolean; procedure CloseNodes; var WithVarNode: TCodeTreeNode; EndPos: LongInt; begin if CreateNodes then begin EndPos:=CurPos.EndPos; if CurNode.Desc=ctnWithStatement then begin if not (CurPos.Flag in [cafSemicolon,cafEnd]) then begin // the with statement is valid until the next atom // this is important for context when cursor is behind last atom of the // with statement, but in front of the next atom ReadNextAtom; EndPos:=CurPos.StartPos; UndoReadNextAtom; end; CurNode.EndPos:=EndPos; //DebugLn(['CloseNodes "',copy(Src,CurNode.StartPos,CurNode.EndPos-CurNode.STartPos),'"']); EndChildNode; // ctnWithStatement end; WithVarNode:=CurNode; CurNode.EndPos:=EndPos; EndChildNode; // ctnWithVariable // set all with variable ends repeat WithVarNode:=WithVarNode.PriorBrother; if (WithVarNode=nil) or (WithVarNode.Desc<>ctnWithVariable) or (WithVarNode.EndPos>0) then break; WithVarNode.EndPos:=EndPos; until false; end; end; begin ReadNextAtom; // read start of variable if CreateNodes then begin CreateChildNode; CurNode.Desc:=ctnWithVariable; end; // read til the end of the variable if not ReadTilVariableEnd(ExceptionOnError,true) then begin CloseNodes; Result:=false; exit; end; // read all other variables while CurPos.Flag=cafComma do begin if CreateNodes then EndChildNode; ReadNextAtom; if CreateNodes then begin CreateChildNode; CurNode.Desc:=ctnWithVariable end; if not ReadTilVariableEnd(ExceptionOnError,true) then begin CloseNodes; Result:=false; exit; end; end; // read DO if not UpAtomIs('DO') then begin if ExceptionOnError then RaiseStringExpectedButAtomFound('"do"') else begin CloseNodes; Result:=false; exit; end; end; // read statement ReadNextAtom; if CreateNodes then begin CreateChildNode; CurNode.Desc:=ctnWithStatement; end; Result:=ReadTilStatementEnd(ExceptionOnError,CreateNodes); CloseNodes; end; function TPascalParserTool.ReadOnStatement(ExceptionOnError, CreateNodes: boolean): boolean; // for example: // on E: Exception do ; // on Exception do ; // on Unit.Exception do ; begin if CreateNodes then begin CreateChildNode; CurNode.Desc:=ctnOnBlock; end; // read variable name ReadNextAtom; AtomIsIdentifier(true); if CreateNodes then begin // ctnOnIdentifier for the variable or the type CreateChildNode; CurNode.Desc:=ctnOnIdentifier; CurNode.EndPos:=CurPos.EndPos; end; ReadNextAtom; if CurPos.Flag=cafColon then begin // this is for example: on E: Exception do ; if CreateNodes then begin // close the variable EndChildNode; end; ReadNextAtom; AtomIsIdentifier(true); if CreateNodes then begin // ctnOnIdentifier for the type CreateChildNode; CurNode.Desc:=ctnOnIdentifier; end; ReadNextAtom; end; if CurPos.Flag=cafPoint then begin // this is for example: on Unit.Exception do ; ReadNextAtom; AtomIsIdentifier(true); if CreateNodes then begin CurNode.EndPos:=CurPos.EndPos; end; ReadNextAtom; end; if CreateNodes then begin // close the type EndChildNode; end; // read 'do' if not UpAtomIs('DO') then RaiseStringExpectedButAtomFound('DO'); // ctnOnStatement if CreateNodes then begin CreateChildNode; CurNode.Desc:=ctnOnStatement; end; ReadTilStatementEnd(true,CreateNodes); if CreateNodes then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; // ctnOnStatement CurNode.EndPos:=CurPos.EndPos; EndChildNode; // ctnOnVariable end; Result:=true; end; procedure TPascalParserTool.ReadVariableType; { creates nodes for variable type examples: interface var a:b; a:b; cvar; a:b; public name 'string constant'; a:b; public name ; a:b; external name 'string constant'; a:b; cvar; external; a:b; external 'library' name 'avar'; implementation procedure c; var d:e; f:g=h; } begin ReadNextAtom; ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); if UpAtomIs('ABSOLUTE') then begin ReadNextAtom; ReadConstant(true,false,[]); end; if CurPos.Flag=cafEqual then begin // read constant repeat ReadNextAtom; if (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]) then ReadTilBracketClose(true); if (CurPos.Flag in AllCommonAtomWords) and (not IsKeyWordInConstAllowed.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)) and AtomIsKeyWord then RaiseCharExpectedButAtomFound(';'); until (CurPos.Flag=cafSemicolon) or (CurPos.StartPos>SrcLen); end; // read ; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); ReadNextAtom; if UpAtomIs('CVAR') then begin // for example: 'var a: char; cvar;' ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); ReadNextAtom; end; if UpAtomIs('STATIC') and (CurNode.Parent<>nil) and (CurNode.Parent.Desc in AllClassSections) then begin // 'static' is allowed for class variables // for example: 'a: char; static;' ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); ReadNextAtom; end; if (CurNode.Parent.Desc=ctnVarSection) and (UpAtomIs('PUBLIC') or UpAtomIs('EXPORT') or UpAtomIs('EXTERNAL')) then begin // examples: // a: b; public; // a: b; external; // a: b; external c; // a: b; external name 'c'; // a: b; external 'library' name 'c'; if UpAtomIs('EXTERNAL') then begin // read external identifier ReadNextAtom; if (CurPos.Flag<>cafSemicolon) and (not UpAtomIs('NAME')) then ReadConstant(true,false,[]); // library name end else ReadNextAtom; if UpAtomIs('NAME') then begin // for example 'var a: char; public name 'b' ;' // for example 'var a: char; public name test;' ReadNextAtom; if (not AtomIsStringConstant) and (not AtomIsIdentifier(false)) then RaiseStringExpectedButAtomFound(ctsStringConstant); ReadConstant(true,false,[]); end; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); end else UndoReadNextAtom; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; function TPascalParserTool.KeyWordFuncBeginEnd: boolean; // Keyword: begin, asm procedure SaveRaiseExceptionWithHint; var CaretXY: TCodeXYPosition; AMessage: string; begin AMessage:=Format(ctsStrExpectedButAtomFound,[';','.']); if (CleanPosToCaret(CurNode.StartPos,CaretXY)) and (CaretXY.Code<>nil) then begin if CaretXY.Code=TCodeBuffer(Scanner.MainCode) then SaveRaiseException(AMessage+ctsPointHintProcStartAt +'('+IntToStr(CaretXY.Y)+','+IntToStr(CaretXY.X)+')') else SaveRaiseException(AMessage+ctsPointHintProcStartAt +TCodeBuffer(CaretXY.Code).Filename +'('+IntToStr(CaretXY.Y)+','+IntToStr(CaretXY.X)+')'); end else if (Scanner<>nil) and (Scanner.MainCode<>nil) then begin SaveRaiseException(AMessage); end; end; var ChildNodeCreated: boolean; begin //DebugLn('TPascalParserTool.KeyWordFuncBeginEnd CurNode=',CurNode.DescAsString); if (CurNode<>nil) and (not (CurNode.Desc in [ctnProcedure,ctnProgram,ctnLibrary,ctnImplementation])) then RaiseStringExpectedButAtomFound('end'); ChildNodeCreated:=UpAtomIs('BEGIN') or UpAtomIs('ASM'); if ChildNodeCreated then begin CreateChildNode; if UpAtomIs('BEGIN') then CurNode.Desc:=ctnBeginBlock else CurNode.Desc:=ctnAsmBlock; CurNode.SubDesc:=ctnsNeedJITParsing; end; // search "end" ReadTilBlockEnd(false,false); // close node if ChildNodeCreated then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; if (CurSection<>ctnInterface) and (CurNode<>nil) and (CurNode.Desc=ctnProcedure) then begin // close procedure CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; if (CurPos.Flag=cafPoint) then SaveRaiseExceptionWithHint; UndoReadNextAtom; EndChildNode; end else if (CurNode.Desc in [ctnProgram,ctnLibrary,ctnImplementation]) then begin ReadNextAtom; if (CurPos.Flag<>cafPoint) then SaveRaiseException(ctsMissingPointAfterEnd); // close program CurNode.EndPos:=CurPos.EndPos; EndChildNode; CurSection:=ctnNone; end; Result:=true; end; function TPascalParserTool.KeyWordFuncType: boolean; { The 'type' keyword is the start of a type section. examples: interface type a=b; generic c<> = d; implementation procedure c; type d=e; } begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnTypeSection; // read all type definitions Name = Type; or generic Name = Type; repeat ReadNextAtom; // name if UpAtomIs('GENERIC') then begin CreateChildNode; CurNode.Desc:=ctnGenericType; // read name ReadNextAtom; AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnGenericName; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // read < ReadNextAtom; if not AtomIsChar('<') then RaiseCharExpectedButAtomFound('<'); CreateChildNode; CurNode.Desc:=ctnGenericParams; // read parameter list ReadNextAtom; if AtomIsIdentifier(false) then begin repeat CreateChildNode; CurNode.Desc:=ctnGenericParameter; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // read name ReadNextAtom; if CurPos.Flag=cafComma then begin ReadNextAtom; AtomIsIdentifier(true); end else if AtomIsChar('>') then begin break; end else if AtomIs('>=') then begin // this is the rare case where >= are two separate atoms dec(CurPos.EndPos); break; end else RaiseCharExpectedButAtomFound('>'); until false; end else begin if AtomIs('>=') then // this is the rare case where >= are two separate atoms dec(CurPos.EndPos); if not AtomIsChar('>') then RaiseCharExpectedButAtomFound('>'); end; // close ctnGenericParams CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadEqualsType; // close ctnGenericType CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else if AtomIsIdentifier(false) then begin CreateChildNode; CurNode.Desc:=ctnTypeDefinition; ReadEqualsType; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else begin UndoReadNextAtom; break; end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncVar: boolean; { examples: interface var a:b; a:b; cvar; a:b; public name 'string constant'; a:b; public name ; a:b; external name 'string constant'; a:b; cvar; external; a:b; external 'library' name 'avar'; implementation procedure c; var d:e; f:g=h; } var LastIdentifierEnd: LongInt; begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnVarSection; // read all variable definitions Name : Type; [cvar;] [public [name '']] repeat ReadNextAtom; // name if AtomIsIdentifier(false) then begin CreateChildNode; CurNode.Desc:=ctnVarDefinition; LastIdentifierEnd:=CurPos.EndPos; ReadNextAtom; while (CurPos.Flag=cafComma) do begin CurNode.EndPos:=LastIdentifierEnd; EndChildNode; // close variable definition ReadNextAtom; AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnVarDefinition; LastIdentifierEnd:=CurPos.EndPos; ReadNextAtom; end; if (CurPos.Flag<>cafColon) then begin RaiseCharExpectedButAtomFound(':'); end; // read type ReadVariableType; end else begin UndoReadNextAtom; break; end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncConst: boolean; { examples: interface const a:b=3; ; c =4; implementation procedure c; const d=2; } begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnConstSection; // read all constants Name = ; or Name : type = ; repeat ReadNextAtom; // name if CurPos.Flag=cafSemicolon then begin // ignore empty semicolons end else if AtomIsIdentifier(false) then begin CreateChildNode; CurNode.Desc:=ctnConstDefinition; ReadNextAtom; if (CurPos.Flag=cafColon) then begin // read type ReadNextAtom; ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); end; if (CurPos.Flag<>cafEqual) then RaiseCharExpectedButAtomFound('='); // read constant ReadNextAtom; CreateChildNode; CurNode.Desc:=ctnConstant; repeat if (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]) then ReadTilBracketClose(true); if (CurPos.Flag in AllCommonAtomWords) and (not IsKeyWordInConstAllowed.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)) and AtomIsKeyWord then RaiseStringExpectedButAtomFound('constant'); if (CurPos.Flag=cafSemicolon) then break; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; until (CurPos.StartPos>SrcLen); // close ctnConstant node EndChildNode; // close ctnConstDefinition node CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else begin UndoReadNextAtom; break; end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncResourceString: boolean; { examples: interface ResourceString a=''; implementation procedure c; ResourceString b=''; } begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnResStrSection; // read all string constants Name = 'abc'; repeat ReadNextAtom; // name if AtomIsIdentifier(false) then begin CreateChildNode; CurNode.Desc:=ctnConstDefinition; ReadNextAtom; if (CurPos.Flag<>cafEqual) then RaiseCharExpectedButAtomFound('='); // read string constant ReadNextAtom; if not AtomIsStringConstant then RaiseStringExpectedButAtomFound(ctsStringConstant); ReadConstant(true,false,[]); if UpAtomIs('DEPRECATED') then begin ReadNextAtom; if AtomIsStringConstant then ReadConstant(true,false,[]); end; // read ; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else begin UndoReadNextAtom; break; end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncExports: boolean; { exports keyword - only allowed in library examples: exports i, j index 3+4, k name 'StrConst', l index 0 name 's'; } procedure RaiseExportsOnlyAllowedInLibraries; begin SaveRaiseException(ctsExportsClauseOnlyAllowedInLibraries); end; begin if not (CurSection in [ctnLibrary,ctnProgram]) then RaiseExportsOnlyAllowedInLibraries; CreateChildNode; CurNode.Desc:=ctnExportsSection; repeat ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if UpAtomIs('INDEX') then begin ReadNextAtom; ReadConstant(true,false,[]); end; if UpAtomIs('NAME') then begin ReadNextAtom; ReadConstant(true,false,[]); end; if (CurPos.Flag=cafSemicolon) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(';'); until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncLabel: boolean; { examples: label a, 23, b; } begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnLabelSection; // read all constants repeat ReadNextAtom; // identifier or number if (not AtomIsIdentifier(false)) and (not AtomIsNumber) then begin RaiseStringExpectedButAtomFound(ctsIdentifier); end; CreateChildNode; CurNode.Desc:=ctnLabelType; CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; if CurPos.Flag=cafSemicolon then begin break; end else if (CurPos.Flag<>cafComma) then begin RaiseCharExpectedButAtomFound(';'); end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncProperty: boolean; { examples: property errno : cint read fpgeterrno write fpseterrno; A2 : Integer Read GetA2 Write SetA2; } begin if not (CurSection in [ctnProgram,ctnLibrary,ctnInterface,ctnImplementation]) then RaiseUnexpectedKeyWord; CreateChildNode; CurNode.Desc:=ctnPropertySection; // read all global properties repeat // read property Name ReadNextAtom; if AtomIsIdentifier(false) then begin CreateChildNode; CurNode.Desc:=ctnGlobalProperty; ReadNextAtom; if CurPos.Flag=cafEdgedBracketOpen then begin // read parameter list ReadTilBracketClose(true); ReadNextAtom; end; while (CurPos.StartPos<=SrcLen) and (CurPos.Flag<>cafSemicolon) do ReadNextAtom; // close global property CurNode.EndPos:=CurPos.EndPos; EndChildNode; end else begin UndoReadNextAtom; break; end; until CurPos.StartPos>SrcLen; // close property section CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; procedure TPascalParserTool.ReadEqualsType; // read = type; begin // read = ReadNextAtom; if (CurPos.Flag<>cafEqual) then RaiseCharExpectedButAtomFound('='); // read type ReadNextAtom; ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); // read ; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); end; function TPascalParserTool.KeyWordFuncTypePacked: boolean; begin ReadNextAtom; if not PackedTypesKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then RaiseStringExpectedButAtomFound('"record"'); Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); end; function TPascalParserTool.KeyWordFuncTypeBitPacked: boolean; begin ReadNextAtom; if not BitPackedTypesKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos, CurPos.EndPos-CurPos.StartPos) then RaiseStringExpectedButAtomFound('"array"'); Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); end; function TPascalParserTool.KeyWordFuncSpecialize: boolean; begin ReadSpecialize(true); Result:=true; end; function TPascalParserTool.KeyWordFuncClass: boolean; // class, object // this is a quick parser, which will only create one node for each class // the nodes for the methods and properties are created in a second // parsing phase (in KeyWordFuncClassMethod) var ChildCreated: boolean; ClassAtomPos: TAtomPosition; Level: integer; ContextDesc: Word; IsForward: Boolean; p: PChar; BracketLvl: Integer; ClassDesc: TCodeTreeNodeDesc; begin ContextDesc:=CurNode.Desc; if not (ContextDesc in [ctnTypeDefinition,ctnGenericType, ctnVarDefinition,ctnConstDefinition]) then SaveRaiseExceptionFmt(ctsAnonymDefinitionsAreNotAllowed,['class']); if CurNode.Parent.Desc<>ctnTypeSection then SaveRaiseExceptionFmt(ctsNestedDefinitionsAreNotAllowed,['class']); if LastUpAtomIs(0,'PACKED') or LastUpAtomIs(0,'BITPACKED') then begin ClassAtomPos:=LastAtoms.GetValueAt(0); end else begin ClassAtomPos:=CurPos; end; // class or 'class of' start found if UpAtomIs('CLASS') then ClassDesc:=ctnClass else if UpAtomIs('OBJECT') then ClassDesc:=ctnObject else if UpAtomIs('OBJCCLASS') then ClassDesc:=ctnObjCClass else if UpAtomIs('OBJCCATEGORY') then ClassDesc:=ctnObjCCategory else if UpAtomIs('CPPCLASS') then ClassDesc:=ctnCPPClass else ClassDesc:=ctnNone; ChildCreated:=ClassDesc<>ctnNone; if ChildCreated then begin CreateChildNode; CurNode.Desc:=ClassDesc; CurNode.StartPos:=ClassAtomPos.StartPos; CurNode.SubDesc:=CurNode.SubDesc+ctnsNeedJITParsing; // will not create sub nodes now end; // find end of class IsForward:=true; ReadNextAtom; if UpAtomIs('OF') then begin IsForward:=false; if ChildCreated then begin CurNode.Desc:=ctnClassOfType; CurNode.SubDesc:=CurNode.SubDesc-ctnsNeedJITParsing; end; ReadNextAtom; AtomIsIdentifier(true); if ChildCreated then begin CreateChildNode; CurNode.Desc:=ctnIdentifier; CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; ReadNextAtom; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); end else if not (ContextDesc in [ctnTypeDefinition,ctnGenericType]) then begin MoveCursorToNodeStart(CurNode); SaveRaiseExceptionFmt(ctsAnonymDefinitionsAreNotAllowed,['class']); end else begin if UpAtomIs('SEALED') then begin while UpAtomIs('SEALED') do ReadNextAtom; end else if UpAtomIs('ABSTRACT') then begin IsForward:=false; while UpAtomIs('ABSTRACT') do ReadNextAtom; end; if (CurPos.Flag=cafRoundBracketOpen) then begin // read inheritage brackets IsForward:=false; ReadTilBracketCloseOrUnexpected(true,[sbcStopOnSemicolon,sbcStopOnRecord]); ReadNextAtom; end; end; if CurPos.Flag=cafSemicolon then begin if ChildCreated and (ClassDesc in AllClassObjects) then begin if IsForward then begin // forward class definition found CurNode.SubDesc:=CurNode.SubDesc+ctnsForwardDeclaration-ctnsNeedJITParsing; end else begin // very short class found e.g. = class(TAncestor); end; end; end else begin // read til end or any suspicious keyword Level:=1; BracketLvl:=0; while (CurPos.StartPos<=SrcLen) do begin case CurPos.Flag of cafEND: begin dec(Level); if Level=0 then break; end; cafRECORD: inc(Level); cafRoundBracketOpen,cafEdgedBracketOpen: inc(BracketLvl); cafRoundBracketClose,cafEdgedBracketClose: dec(BracketLvl); cafEqual: ; // Note: this is allowed: function a=b; cafWord: begin p:=@Src[CurPos.StartPos]; case UpChars[p^] of 'B': if CompareSrcIdentifiers(p,'BEGIN') then SaveRaiseException(ctsEndForClassNotFound); 'C': if CompareSrcIdentifiers(p,'CONST') and (BracketLvl=0) then SaveRaiseException(ctsEndForClassNotFound); 'I': if CompareSrcIdentifiers(p,'INTERFACE') or CompareSrcIdentifiers(p,'IMPLEMENTATION') then SaveRaiseException(ctsEndForClassNotFound); 'R': if CompareSrcIdentifiers(p,'RESOURCESTRING') then SaveRaiseException(ctsEndForClassNotFound); 'T': if CompareSrcIdentifiers(p,'THREADVAR') then SaveRaiseException(ctsEndForClassNotFound) else if CompareSrcIdentifiers(p,'TYPE') and (BracketLvl>0) then SaveRaiseException(ctsEndForClassNotFound); 'V': if CompareSrcIdentifiers(p,'VAR') and (BracketLvl>1) then begin SaveRaiseException(ctsEndForClassNotFound); end; end; end; end; ReadNextAtom; end; if (CurPos.StartPos>SrcLen) then SaveRaiseException(ctsEndForClassNotFound); end; if CurPos.Flag=cafEND then begin // read extra flags ReadNextAtom; if CurPos.Flag=cafSemicolon then ReadNextAtom; if UpAtomIs('DEPRECATED') then begin ReadNextAtom; if AtomIsStringConstant then ReadConstant(true,false,[]); end else if UpAtomIs('PLATFORM') or UpAtomIs('UNIMPLEMENTED') or UpAtomIs('EXPERIMENTAL') or UpAtomIs('LIBRARY') then ReadNextAtom; if CurPos.Flag=cafSemicolon then ReadNextAtom; if UpAtomIs('EXTERNAL') then begin ReadNextAtom; if UpAtomIs('NAME') then begin ReadNextAtom; ReadConstant(true,false,[]); end; end; if CurPos.Flag<>cafSemicolon then UndoReadNextAtom; end; if ChildCreated then begin // close class CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; Result:=true; end; function TPascalParserTool.KeyWordFuncClassInterface: boolean; // class interface, dispinterface var ChildCreated: boolean; IntfAtomPos: TAtomPosition; IntfDesc: TCodeTreeNodeDesc; begin if not (CurNode.Desc in [ctnTypeDefinition,ctnGenericType]) then SaveRaiseExceptionFmt(ctsAnonymDefinitionsAreNotAllowed,['interface']); if CurNode.Parent.Desc<>ctnTypeSection then SaveRaiseExceptionFmt(ctsNestedDefinitionsAreNotAllowed,['interface']); IntfAtomPos:=CurPos; // class interface start found ChildCreated:=true; // maybe change this in future to jit parsing if UpAtomIs('INTERFACE') then IntfDesc:=ctnClassInterface else if UpAtomIs('DISPINTERFACE') then IntfDesc:=ctnDispinterface else IntfDesc:=ctnObjCProtocol; if ChildCreated then begin CreateChildNode; CurNode.Desc:=IntfDesc; CurNode.StartPos:=IntfAtomPos.StartPos; end; // find end of interface ReadNextAtom; if (CurPos.Flag<>cafSemicolon) then begin if (CurPos.Flag=cafRoundBracketOpen) then begin // read inheritage brackets ReadClassInheritance(ChildCreated); ReadNextAtom; end; if CurPos.Flag=cafEdgedBracketOpen then ReadGUID; // parse till "end" of interface repeat if (CurPos.Flag=cafEnd) or (CurPos.StartPos>SrcLen) then break; if not SkipInnerClassInterface(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then break; ReadNextAtom; until false; end else begin // forward definition CurNode.SubDesc:=CurNode.SubDesc+ctnsForwardDeclaration; end; if ChildCreated then begin // close class interface CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; if CurPos.Flag=cafEND then begin ReadNextAtom; if CurPos.Flag=cafSemicolon then ReadNextAtom; if UpAtomIs('DEPRECATED') then begin ReadNextAtom; if AtomIsStringConstant then ReadConstant(true,false,[]); end else if UpAtomIs('EXTERNAL') then begin ReadNextAtom; if UpAtomIs('NAME') then begin ReadNextAtom; ReadConstant(true,false,[]); end; end else if UpAtomIs('PLATFORM') or UpAtomIs('UNIMPLEMENTED') or UpAtomIs('EXPERIMENTAL') or UpAtomIs('LIBRARY') then ReadNextAtom; if CurPos.Flag<>cafSemicolon then UndoReadNextAtom; end; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeArray: boolean; { examples: array of ... array[SubRange] of ... array[SubRange,SubRange,...] of ... } begin CreateChildNode; // first set the type to open array (an array type without brackets) CurNode.Desc:=ctnOpenArrayType; ReadNextAtom; if (CurPos.Flag=cafEdgedBracketOpen) then begin repeat ReadNextAtom; // this is a ranged array -> change type CurNode.Desc:=ctnRangedArrayType; CreateChildNode; CurNode.Desc:=ctnRangeType; ReadSubRange(true); CurNode.EndPos:=LastAtoms.GetValueAt(0).EndPos; EndChildNode; if (CurPos.Flag=cafEdgedBracketClose) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(']'); until false; ReadNextAtom; end; if not UpAtomIs('OF') then RaiseStringExpectedButAtomFound('"of"'); ReadNextAtom; Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); CurNode.EndPos:=CurPos.StartPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeProc: boolean; { examples: procedure; procedure of object; procedure(ParmList) of object; function(ParmList):SimpleType of object; procedure; cdecl; popstack; register; pascal; stdcall; } var IsFunction, EqualFound: boolean; begin IsFunction:=UpAtomIs('FUNCTION'); CreateChildNode; CurNode.Desc:=ctnProcedureType; ReadNextAtom; CreateChildNode; CurNode.Desc:=ctnProcedureHead; CurNode.SubDesc:=ctnsNeedJITParsing; if (CurPos.Flag=cafRoundBracketOpen) then begin // read parameter list ReadParamList(true,false,[]); end; if IsFunction then begin if (CurPos.Flag=cafColon) then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if CurPos.Flag=cafPoint then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; end else begin RaiseCharExpectedButAtomFound(':'); end; end; if UpAtomIs('OF') then begin if not ReadNextUpAtomIs('OBJECT') then RaiseStringExpectedButAtomFound('"object"'); ReadNextAtom; end; if (CurPos.Flag=cafEqual) and (CurNode.Parent.Desc in [ctnConstDefinition,ctnVarDefinition]) then begin // for example 'const f: procedure = nil;' end else begin if CurPos.Flag=cafSemicolon then begin ReadNextAtom; EqualFound:=false; end else if (CurPos.Flag=cafEqual) then begin EqualFound:=true; end else EqualFound:=false; if not EqualFound then begin // read modifiers repeat if (not IsKeyWordProcedureTypeSpecifier.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos)) then begin UndoReadNextAtom; break; end else begin ReadNextAtom; if CurPos.Flag<>cafSemicolon then begin if (CurPos.Flag=cafEqual) then begin break; end; // delphi/fpc allow proc modifiers without semicolons if not IsKeyWordProcedureTypeSpecifier.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos) then begin RaiseCharExpectedButAtomFound(';'); end; UndoReadNextAtom; end; end; ReadNextAtom; until false; end; end; CurNode.EndPos:=CurPos.StartPos; EndChildNode; CurNode.EndPos:=CurPos.StartPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeSet: boolean; { examples: set of Identifier; set of SubRange; } begin CreateChildNode; CurNode.Desc:=ctnSetType; if not ReadNextUpAtomIs('OF') then RaiseStringExpectedButAtomFound('"of"'); ReadNextAtom; Result:=KeyWordFuncTypeDefault; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeLabel: boolean; // 'label;' begin CreateChildNode; CurNode.Desc:=ctnLabelType; CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeType: boolean; // 'type identifier' begin if not LastAtomIs(0,'=') then RaiseStringExpectedButAtomFound(ctsIdentifier); CreateChildNode; CurNode.Desc:=ctnTypeType; ReadNextAtom; Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; function TPascalParserTool.KeyWordFuncTypeFile: boolean; // 'file' or 'file of ' begin CreateChildNode; CurNode.Desc:=ctnFileType; if ReadNextUpAtomIs('OF') then begin ReadNextAtom; Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); if not Result then exit; end; CurNode.EndPos:=CurPos.EndPos; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncTypePointer: boolean; // '^Identifier' begin CreateChildNode; CurNode.Desc:=ctnPointerType; ReadNextAtom; Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; function TPascalParserTool.KeyWordFuncTypeDefault: boolean; { check for enumeration, subrange and identifier types examples: integer 1..3 (a,b:=3,c=4) (a)..4 Low(integer)..High(integer) 'a'..'z' } var SubRangeOperatorFound: boolean; procedure ReadTillTypeEnd; begin // read till ';', ':', ')', '=', 'end' while (CurPos.StartPos<=SrcLen) do begin if (CurPos.Flag in [cafSemicolon,cafColon,cafRoundBracketClose, cafEqual,cafEdgedBracketClose]) or (AtomIsKeyWord and (not IsKeyWordInConstAllowed.DoItCaseInsensitive(Src, CurPos.StartPos,CurPos.EndPos-CurPos.StartPos))) then break; if (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]) then ReadTilBracketClose(true) else if AtomIs('..') then begin if SubRangeOperatorFound then SaveRaiseException(ctsUnexpectedSubRangeOperatorFound); SubRangeOperatorFound:=true; end; ReadNextAtom; end; end; // TPascalParserTool.KeyWordFuncTypeDefault: boolean begin CreateChildNode; SubRangeOperatorFound:=false; if CurPos.Flag in AllCommonAtomWords then begin AtomIsIdentifier(true); ReadNextAtom; if (CurPos.Flag=cafPoint) then begin // first word was unit name ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; while (CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen]) do begin ReadTilBracketClose(true); ReadNextAtom; end; if AtomIs('..') then begin // a subrange CurNode.Desc:=ctnRangeType; ReadTillTypeEnd; if not SubRangeOperatorFound then SaveRaiseException(ctsInvalidSubrange); CurNode.EndPos:=CurPos.StartPos; end else if AtomIsChar('<') and (Scanner.CompilerMode in [cmOBJFPC,cmFPC]) and (LastUpAtomIs(0,'STRING')) then begin // string< CurNode.Desc:=ctnIdentifier; repeat ReadNextAtom; if AtomIsChar('>') then break; case CurPos.Flag of cafRoundBracketOpen,cafEdgedBracketOpen: ReadTilBracketClose(true); cafNone: if (CurPos.StartPos>SrcLen) then RaiseCharExpectedButAtomFound('>') else if (((CurPos.EndPos-CurPos.StartPos=1) and (Src[CurPos.StartPos] in ['+','-','*','&','$']))) or AtomIsNumber then begin end else begin RaiseCharExpectedButAtomFound('>') end; else RaiseCharExpectedButAtomFound('>'); end; until false; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; end else begin // an identifier CurNode.Desc:=ctnIdentifier; CurNode.EndPos:=CurPos.StartPos; end; end else begin // enum or subrange ReadTillTypeEnd; if SubRangeOperatorFound then begin // a subrange CurNode.Desc:=ctnRangeType; CurNode.EndPos:=CurPos.StartPos; end else begin // an enum or syntax error MoveCursorToNodeStart(CurNode); ReadNextAtom; if (CurPos.Flag=cafRoundBracketOpen) then begin // an enumeration -> read all enums CurNode.Desc:=ctnEnumerationType; repeat ReadNextAtom; // read enum name if (CurPos.Flag=cafRoundBracketClose) then break; AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnEnumIdentifier; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close enum node ReadNextAtom; if AtomIs(':=') or (CurPos.Flag=cafEqual) then begin // read ordinal value ReadNextAtom; ReadConstant(true,false,[]); end; if (CurPos.Flag=cafRoundBracketClose) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(')'); until false; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; end else SaveRaiseException(ctsInvalidType); end; end; if UpAtomIs('PLATFORM') or UpAtomIs('UNIMPLEMENTED') or UpAtomIs('EXPERIMENTAL') or UpAtomIs('LIBRARY') then ReadNextAtom; if UpAtomIs('DEPRECATED') then begin ReadNextAtom; if AtomIsStringConstant then ReadConstant(true,false,[]); end; EndChildNode; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeRecord: boolean; { read variable type 'record' examples: record i: packed record j: integer; k: record end; case y: integer of 0: (a: integer); 1,2,3: (b: array[char] of char; c: char); 3: ( d: record case byte of 10: (i: integer; ); 11: (y: byte); end; ); 4: (e: integer; case z of 8: (f: integer) ); end; end; } // function TPascalParserTool.KeyWordFuncTypeRecord: boolean; begin CreateChildNode; CurNode.Desc:=ctnRecordType; if LastUpAtomIs(0,'PACKED') or LastUpAtomIs(0,'BITPACKED') then CurNode.StartPos:=LastAtoms.GetValueAt(0).StartPos; // read all variables repeat ReadNextAtom; if CurPos.Flag=cafEND then break; if UpAtomIs('CASE') then begin KeyWordFuncTypeRecordCase; break; end else begin // read variable names repeat AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnVarDefinition; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; if (CurPos.Flag=cafColon) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(':'); EndChildNode; // close variable ReadNextAtom; // read next variable name until false; ReadNextAtom; Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); if not Result then exit; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close variable if CurPos.Flag=cafEND then break; end; until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close record ReadNextAtom; if UpAtomIs('PLATFORM') or UpAtomIs('DEPRECATED') or UpAtomIs('UNIMPLEMENTED') or UpAtomIs('EXPERIMENTAL') or UpAtomIs('LIBRARY') then ReadNextAtom; Result:=true; end; function TPascalParserTool.KeyWordFuncTypeRecordCase: boolean; begin if not UpAtomIs('CASE') then SaveRaiseException('[TPascalParserTool.KeyWordFuncTypeRecordCase] ' +'internal error'); CreateChildNode; CurNode.Desc:=ctnRecordCase; ReadNextAtom; // read ordinal type { case a of case a:b of case a:b.c of } AtomIsIdentifier(true); ReadNextAtom; if (CurPos.Flag=cafColon) then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; if CurPos.Flag=cafPoint then begin ReadNextAtom; // unit.type AtomIsIdentifier(true); ReadNextAtom; end; if not UpAtomIs('OF') then // read 'of' RaiseStringExpectedButAtomFound('"of"'); // read all variants repeat ReadNextAtom; // read constant (variant identifier) if (CurPos.Flag in [cafRoundBracketClose,cafEnd]) then break; CreateChildNode; CurNode.Desc:=ctnRecordVariant; repeat ReadConstant(true,false,[]); if (CurPos.Flag=cafColon) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(':'); ReadNextAtom; until false; ReadNextAtom; // read '(' if (CurPos.Flag<>cafRoundBracketOpen) then RaiseCharExpectedButAtomFound('('); // read all variables ReadNextAtom; // read first variable name repeat if (CurPos.Flag=cafRoundBracketClose) then begin // end of variant record break; end else if UpAtomIs('CASE') then begin // sub record variant KeyWordFuncTypeRecordCase(); if (CurPos.Flag<>cafRoundBracketClose) then RaiseCharExpectedButAtomFound(')'); break; end else begin // sub identifier repeat AtomIsIdentifier(true); CreateChildNode; CurNode.Desc:=ctnVarDefinition; CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; if (CurPos.Flag=cafColon) then break; if (CurPos.Flag<>cafComma) then RaiseCharExpectedButAtomFound(','); EndChildNode; ReadNextAtom; // read next variable name until false; ReadNextAtom; // read type Result:=ParseType(CurPos.StartPos,CurPos.EndPos-CurPos.StartPos); if not Result then exit; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close variable definition end; if (CurPos.Flag=cafRoundBracketClose) then break; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); ReadNextAtom; until false; ReadNextAtom; if (CurPos.Flag in [cafEnd,cafRoundBracketClose]) then begin CurNode.EndPos:=CurPos.StartPos; EndChildNode; // close variant break; end; if CurPos.Flag<>cafSemicolon then RaiseCharExpectedButAtomFound(';'); CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close variant // read next variant until false; CurNode.EndPos:=CurPos.EndPos; EndChildNode; // close case Result:=true; end; procedure TPascalParserTool.RaiseCharExpectedButAtomFound(c: char); begin SaveRaiseExceptionFmt(ctsStrExpectedButAtomFound,[c,GetAtom]); end; procedure TPascalParserTool.RaiseStringExpectedButAtomFound(const s: string); begin SaveRaiseExceptionFmt(ctsStrExpectedButAtomFound,[s,GetAtom]); end; procedure TPascalParserTool.RaiseUnexpectedKeyWord; begin SaveRaiseExceptionFmt(ctsUnexpectedKeyword,[GetAtom]); end; procedure TPascalParserTool.RaiseIllegalQualifier; begin SaveRaiseExceptionFmt(ctsIllegalQualifier,[GetAtom]); end; procedure TPascalParserTool.RaiseEndOfSourceExpected; begin SaveRaiseExceptionFmt(ctsEndofSourceExpectedButAtomFound,[GetAtom]); end; procedure TPascalParserTool.InitExtraction; begin if ExtractMemStream=nil then ExtractMemStream:=TMemoryStream.Create; ExtractMemStream.Position:=0; end; function TPascalParserTool.GetExtraction(InUpperCase: boolean): string; begin SetLength(Result,ExtractMemStream.Position); ExtractMemStream.Position:=0; if Result<>'' then ExtractMemStream.Read(Result[1],length(Result)); if InUpperCase then Result:=UpperCaseStr(Result); end; function TPascalParserTool.ExtractStreamEndIsIdentChar: boolean; var c: char; begin if ExtractMemStream.Position=0 then begin Result:=false; exit; end; ExtractMemStream.Position:=ExtractMemStream.Position-1; ExtractMemStream.Read(c,1); Result:=IsIdentChar[c]; end; procedure TPascalParserTool.ExtractNextAtom(AddAtom: boolean; Attr: TProcHeadAttributes); // add current atom and text before, then read next atom // if not phpWithComments in Attr then the text before will be shortened var LastAtomEndPos: integer; LastStreamPos: TFPCStreamSeekType; const space: char = ' '; begin LastStreamPos:=ExtractMemStream.Position; if LastAtoms.Count>0 then begin LastAtomEndPos:=LastAtoms.GetValueAt(0).EndPos; if phpWithComments in Attr then begin // add space/comment between pascal atoms ExtractMemStream.Write(Src[LastAtomEndPos],CurPos.StartPos-LastAtomEndPos); end else if (ExtractMemStream.Position>0) then begin // some space/comments were skipped // -> check if a space must be inserted if AddAtom and ( ((phpCommentsToSpace in Attr) and (CurPos.StartPos>LastAtomEndPos)) or ((CurPos.StartPos<=SrcLen) and (IsIdentStartChar[Src[CurPos.StartPos]]) and ExtractStreamEndIsIdentChar) ) then begin ExtractMemStream.Write(space,1); LastStreamPos:=ExtractMemStream.Position; end; end; end; if AddAtom then begin ExtractMemStream.Write(Src[CurPos.StartPos],CurPos.EndPos-CurPos.StartPos); end; if (ExtractSearchPos>0) and (ExtractSearchPos<=ExtractMemStream.Position) then begin ExtractFoundPos:=ExtractSearchPos-1-LastStreamPos+CurPos.StartPos; ExtractSearchPos:=-1; end; ReadNextAtom; end; function TPascalParserTool.FindFirstNodeOnSameLvl( StartNode: TCodeTreeNode): TCodeTreeNode; begin Result:=StartNode; if Result=nil then exit; if Result.Parent=nil then begin while Result.PriorBrother<>nil do Result:=Result.PriorBrother; end else begin Result:=Result.Parent; while (Result.Desc in AllCodeSections) and (Result.PriorBrother<>nil) do Result:=Result.PriorBrother; while (Result<>nil) and (Result.FirstChild=nil) do Result:=Result.NextBrother; Result:=Result.FirstChild; end; end; function TPascalParserTool.FindNextNodeOnSameLvl( StartNode: TCodeTreeNode): TCodeTreeNode; begin Result:=StartNode; if Result=nil then exit; if Result.NextBrother<>nil then Result:=Result.NextBrother else begin Result:=Result.Parent; if Result=nil then exit; Result:=Result.NextBrother; while (Result<>nil) and (Result.FirstChild=nil) do Result:=Result.NextBrother; if Result=nil then exit; Result:=Result.FirstChild; end; end; function TPascalParserTool.FindPrevNodeOnSameLvl(StartNode: TCodeTreeNode ): TCodeTreeNode; begin Result:=StartNode; if Result=nil then exit; if Result.PriorBrother<>nil then Result:=Result.PriorBrother else begin Result:=Result.Parent; if Result=nil then exit; Result:=Result.PriorBrother; while (Result<>nil) and (Result.LastChild=nil) do Result:=Result.PriorBrother; if Result=nil then exit; Result:=Result.LastChild; end; end; function TPascalParserTool.NodeHasParentOfType(ANode: TCodeTreeNode; NodeDesc: TCodeTreeNodeDesc): boolean; begin if ANode<>nil then begin repeat ANode:=ANode.Parent; until (ANode=nil) or (ANode.Desc=NodeDesc); end; Result:=(ANode<>nil); end; procedure TPascalParserTool.BuildTreeAndGetCleanPos( TreeRange: TTreeRange; const CursorPos: TCodeXYPosition; out CleanCursorPos: integer; BuildTreeFlags: TBuildTreeFlags); var CaretType: integer; IgnorePos: TCodePosition; RealTreeRange: TTreeRange; Node: TCodeTreeNode; begin RealTreeRange:=TreeRange; //DebugLn(['TPascalParserTool.BuildTreeAndGetCleanPos ',MainFilename,' btSetIgnoreErrorPos=',btSetIgnoreErrorPos in BuildTreeFlags,' btKeepIgnoreErrorPos=',btKeepIgnoreErrorPos in BuildTreeFlags,' CursorPos=x=',CursorPos.X,',y=',CursorPos.Y]); if (btSetIgnoreErrorPos in BuildTreeFlags) then begin // ignore errors after cursor position if (CursorPos.Code<>nil) then begin IgnorePos.Code:=CursorPos.Code; IgnorePos.Code.LineColToPosition(CursorPos.Y,CursorPos.X,IgnorePos.P); if IgnorePos.P<1 then IgnorePos.Code:=nil; //debugln(['TPascalParserTool.BuildTreeAndGetCleanPos IgnorePos=',dbgsCP(IgnorePos),' After=',IgnorePos.P,'=',copy(CursorPos.Code.Source,IgnorePos.P,10)]); IgnoreErrorAfter:=IgnorePos; end else ClearIgnoreErrorAfter; end else if not (btKeepIgnoreErrorPos in BuildTreeFlags) then ClearIgnoreErrorAfter; if (RealTreeRange in [trTillCursor,trTillCursorSection]) then begin // find out, if interface is enough if (Tree<>nil) and (Tree.Root<>nil) then begin Node:=Tree.Root; while (Node<>nil) and (Node.Desc<>ctnImplementation) do Node:=Node.NextBrother; if Node<>nil then begin // start of implementation section found // => whole interface was read CaretType:=CaretToCleanPos(CursorPos, CleanCursorPos); if (CaretType=0) or (CaretType=-1) then begin if (CleanCursorPos<=Node.StartPos) and (not UpdateNeeded(true)) then begin // interface section is already parsed, is still valid and // cursor is in this section ValidateToolDependencies; exit; end; end; end; end; if RealTreeRange=trTillCursorSection then begin // interface is no enough => parse whole unit RealTreeRange:=trAll; end; end; if (RealTreeRange=trTillCursor) and (not UpdateNeeded(false)) then begin // tree is valid // -> if there was an error, raise it again if (LastErrorPhase in [CodeToolPhaseScan,CodeToolPhaseParse]) and ((not IgnoreErrorAfterValid) or (not IgnoreErrorAfterPositionIsInFrontOfLastErrMessage)) then begin DebugLn('TPascalParserTool.BuildTreeAndGetCleanPos RaiseLastError ',MainFilename); RaiseLastError; end; // check if cursor is in interface CaretType:=CaretToCleanPos(CursorPos, CleanCursorPos); if (CaretType=0) or (CaretType=-1) then begin BuildSubTree(CleanCursorPos); if (CaretType=-1) and (btLoadDirtySource in BuildTreeFlags) then begin // cursor position is in dead code (skipped code between IFDEF/ENDIF) LoadDirtySource(CursorPos); end; exit; end; // cursor is not in partially parsed code -> parse complete code end; // parse code BuildTree(RealTreeRange=trInterface); if (not IgnoreErrorAfterValid) and (not EndOfSourceFound) then SaveRaiseException(ctsEndOfSourceNotFound); // find the CursorPos in cleaned source CaretType:=CaretToCleanPos(CursorPos, CleanCursorPos); if (CaretType=0) or (CaretType=-1) then begin BuildSubTree(CleanCursorPos); if (CaretType=-1) and (btLoadDirtySource in BuildTreeFlags) then begin // cursor position lies in dead code (skipped code between IFDEF/ENDIF) LoadDirtySource(CursorPos); end; exit; end; if (CaretType=-2) or (not (btCursorPosOutAllowed in BuildTreeFlags)) then RaiseException(ctsCursorPosOutsideOfCode); // cursor outside of clean code CleanCursorPos:=-1; end; function TPascalParserTool.ReadTilTypeOfProperty( PropertyNode: TCodeTreeNode): boolean; begin MoveCursorToNodeStart(PropertyNode); ReadNextAtom; // read keyword 'property' if UpAtomIs('CLASS') then ReadNextAtom; ReadNextAtom; // read property name AtomIsIdentifier(true); ReadNextAtom; if (CurPos.Flag=cafEdgedBracketOpen) then begin // read parameter list ReadTilBracketClose(true); ReadNextAtom; end; if (CurPos.Flag<>cafColon) then begin Result:=false; exit; end; ReadNextAtom; // read type AtomIsIdentifier(true); Result:=true; end; procedure TPascalParserTool.ReadGUID; procedure RaiseStringConstantExpected; begin RaiseStringExpectedButAtomFound(ctsStringConstant); end; begin CreateChildNode; CurNode.Desc:=ctnClassGUID; // read GUID ReadNextAtom; if (not AtomIsStringConstant) and (not AtomIsIdentifier(false)) then RaiseStringConstantExpected; ReadNextAtom; if CurPos.Flag<>cafEdgedBracketClose then RaiseCharExpectedButAtomFound(']'); CurNode.EndPos:=CurPos.EndPos; EndChildNode; ReadNextAtom; end; procedure TPascalParserTool.ReadClassInheritance(CreateChildNodes: boolean); // cursor must be the round bracket open // at the end cursor will be on round bracket close begin // read inheritage if CreateChildNodes then begin CreateChildNode; CurNode.Desc:=ctnClassInheritance; end; // read list of ancestors, interfaces ReadNextAtom; if CurPos.Flag<>cafRoundBracketClose then begin repeat if UpAtomIs('SPECIALIZE') then begin // specialize Identifier ReadSpecialize(CreateChildNodes); end else begin // read Identifier or Unit.Identifier AtomIsIdentifier(true); if CreateChildNodes then begin CreateChildNode; CurNode.Desc:=ctnIdentifier; end; ReadNextAtom; if CurPos.Flag=cafPoint then begin ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; if CreateChildNodes then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; end; // read comma or ) if CurPos.Flag=cafRoundBracketClose then break; if CurPos.Flag<>cafComma then RaiseCharExpectedButAtomFound(')'); ReadNextAtom; until false; end; // close ctnClassInheritance if CreateChildNodes then begin CurNode.EndPos:=CurPos.EndPos; EndChildNode; end; end; procedure TPascalParserTool.ReadSpecialize(CreateChildNodes: boolean); // specialize template // after parsing the cursor is on the atom behind the > // examples: // type TListOfInteger = specialize TGenericList; // type TListOfChar = specialize Classes.TGenericList; // type l = class(specialize TFPGObjectList) begin if CreateChildNodes then begin CreateChildNode; CurNode.Desc:=ctnSpecialize; end; // read identifier (the name of the generic) ReadNextAtom; AtomIsIdentifier(true); if CreateChildNodes then begin CreateChildNode; CurNode.Desc:=ctnSpecializeType; CurNode.EndPos:=CurPos.EndPos; end; ReadNextAtom; if Curpos.Flag=cafPoint then begin // first identifier was unitname, now read the type ReadNextAtom; AtomIsIdentifier(true); if CreateChildNodes then CurNode.EndPos:=CurPos.EndPos; ReadNextAtom; end; if CreateChildNodes then begin EndChildNode; // end ctnSpecializeType end; // read type list if not AtomIsChar('<') then RaiseCharExpectedButAtomFound('<'); if CreateChildNodes then begin CreateChildNode; CurNode.Desc:=ctnSpecializeParams; end; // read list of types repeat // read identifier (a parameter of the generic type) ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; if Curpos.Flag=cafPoint then begin // first identifier was unitname, now read the type ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; if AtomIsChar('>') then break else if CurPos.Flag=cafComma then begin // read next parameter end else RaiseCharExpectedButAtomFound('>'); until false; if CreateChildNodes then begin // close list CurNode.EndPos:=CurPos.EndPos; EndChildNode; // end ctnSpecializeParams // close specialize CurNode.EndPos:=CurPos.EndPos; EndChildNode; // end ctnSpecialize end; ReadNextAtom; end; function TPascalParserTool.WordIsPropertyEnd: boolean; var p: PChar; begin p:=@Src[CurPos.StartPos]; case UpChars[p^] of 'C': if UpAtomIs('CLASS') then exit(true); 'F': if UpAtomIs('FUNCTION') then exit(true); 'S': if UpAtomIs('STRICT') then exit(true); 'P': case UpChars[p[1]] of 'R': case UpChars[p[2]] of 'I': if UpAtomIs('PRIVATE') then exit(true); 'O': if UpAtomIs('PROTECTED') or UpAtomIs('PROCEDURE') then exit(true); end; 'U': if UpAtomIs('PUBLIC') or UpAtomIs('PUBLISHED') then exit(true); end; 'T': if UpAtomIs('TYPE') then exit(true); 'V': if UpAtomIs('VAR') then exit(true); end; Result:=false; end; procedure TPascalParserTool.ValidateToolDependencies; begin end; procedure TPascalParserTool.BuildSubTreeForProcHead(ProcNode: TCodeTreeNode); var HasForwardModifier, IsFunction, IsOperator, IsMethod: boolean; ParseAttr: TParseProcHeadAttributes; OldPhase: integer; IsProcType: Boolean; ProcHeadNode: TCodeTreeNode; begin if ProcNode.Desc=ctnProcedureHead then ProcNode:=ProcNode.Parent; if ProcNode.Desc=ctnMethodMap then begin exit; end; if (not (ProcNode.Desc in [ctnProcedure,ctnProcedureType])) then begin {$IFDEF CheckNodeTool} CTDumpStack; {$ENDIF} if ProcNode<>nil then begin DebugLn(['TPascalParserTool.BuildSubTreeForProcHead Desc=',ProcNode.DescAsString]); if ProcNode.FirstChild<>nil then DebugLn(['TPascalParserTool.BuildSubTreeForProcHead FirstChild=',ProcNode.FirstChild.DescAsString]); end; RaiseException('[TPascalParserTool.BuildSubTreeForProcHead] ' +'internal error: invalid ProcNode'); end; ProcHeadNode:=ProcNode.FirstChild; if (ProcHeadNode<>nil) and ((ProcHeadNode.SubDesc and ctnsNeedJITParsing)=0) then exit; OldPhase:=CurrentPhase; CurrentPhase:=CodeToolPhaseParse; try if (ProcHeadNode<>nil) and ((ctnsHasParseError and ProcHeadNode.SubDesc)>0) then RaiseNodeParserError(ProcHeadNode); IsMethod:=ProcNode.Parent.Desc in (AllClasses+AllClassSections); MoveCursorToNodeStart(ProcNode); ReadNextAtom; if UpAtomIs('CLASS') then ReadNextAtom; IsFunction:=UpAtomIs('FUNCTION'); IsOperator:=UpAtomIs('OPERATOR'); IsProcType:=ProcNode.Desc=ctnProcedureType; // read procedure head (= [name] + parameterlist + resulttype;) ReadNextAtom;// read first atom of head CurNode:=ProcHeadNode; if CurNode=nil then if ProcNode.Desc=ctnProcedureType then RaiseCharExpectedButAtomFound(';') else RaiseStringExpectedButAtomFound('identifier'); if not IsProcType then begin if not IsOperator then AtomIsIdentifier(true); ReadNextAtom; if (CurPos.Flag=cafPoint) then begin // read procedure name of a class method (the name after the . ) ReadNextAtom; AtomIsIdentifier(true); ReadNextAtom; end; end; // read rest of procedure head and build nodes HasForwardModifier:=false; ParseAttr:=[pphCreateNodes]; if IsMethod then Include(ParseAttr,pphIsMethod); if IsFunction then Include(ParseAttr,pphIsFunction); if IsOperator then Include(ParseAttr,pphIsOperator); if IsProcType then Include(ParseAttr,pphIsType); ReadTilProcedureHeadEnd(ParseAttr,HasForwardModifier); CurrentPhase:=OldPhase; ProcHeadNode.SubDesc:=ProcHeadNode.SubDesc and (not ctnsNeedJITParsing); except CurrentPhase:=OldPhase; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForProcHead ',MainFilename,' ERROR: ',LastErrorMessage); {$ENDIF} if (not IgnoreErrorAfterValid) or (not IgnoreErrorAfterPositionIsInFrontOfLastErrMessage) then raise; {$IFDEF ShowIgnoreErrorAfter} DebugLn('TPascalParserTool.BuildSubTreeForProcHead ',MainFilename,' IGNORING ERROR: ',LastErrorMessage); {$ENDIF} end; end; procedure TPascalParserTool.BuildSubTreeForProcHead(ProcNode: TCodeTreeNode; out FunctionResult: TCodeTreeNode); begin if ProcNode.Desc=ctnProcedureHead then ProcNode:=ProcNode.Parent; if ProcNode.Desc<>ctnProcedure then RaiseException('INTERNAL ERROR: TPascalParserTool.BuildSubTreeForProcHead with FunctionResult'); BuildSubTreeForProcHead(ProcNode); FunctionResult:=ProcNode.FirstChild.FirstChild; if (FunctionResult<>nil) and (FunctionResult.Desc=ctnParameterList) then FunctionResult:=FunctionResult.NextBrother; end; procedure TPascalParserTool.BuildSubTree(CleanCursorPos: integer); begin BuildSubTree(FindDeepestNodeAtPos(CleanCursorPos,false)); end; procedure TPascalParserTool.BuildSubTree(ANode: TCodeTreeNode); begin if ANode=nil then exit; case ANode.Desc of ctnClass,ctnClassInterface,ctnDispinterface,ctnObject, ctnObjCClass,ctnObjCCategory,ctnObjCProtocol,ctnCPPClass: BuildSubTreeForClass(ANode); ctnProcedure,ctnProcedureHead: BuildSubTreeForProcHead(ANode); ctnBeginBlock: BuildSubTreeForBeginBlock(ANode); end; end; function TPascalParserTool.NodeNeedsBuildSubTree(ANode: TCodeTreeNode ): boolean; begin Result:=false; if ANode=nil then exit; if ANode.Desc in (AllClasses+[ctnProcedureHead,ctnBeginBlock]) then begin Result:=(ANode.SubDesc and ctnsNeedJITParsing)>0; end; end; function TPascalParserTool.BuildSubTreeAndFindDeepestNodeAtPos(P: integer; ExceptionOnNotFound: boolean): TCodeTreeNode; begin Result:=BuildSubTreeAndFindDeepestNodeAtPos(Tree.Root,P,ExceptionOnNotFound); end; function TPascalParserTool.BuildSubTreeAndFindDeepestNodeAtPos( StartNode: TCodeTreeNode; P: integer; ExceptionOnNotFound: boolean ): TCodeTreeNode; var Node: TCodeTreeNode; begin Result:=FindDeepestNodeAtPos(StartNode,P,ExceptionOnNotFound); //debugln('TPascalParserTool.BuildSubTreeAndFindDeepestNodeAtPos A ',Result.DescAsString,' ',dbgs(NodeNeedsBuildSubTree(Result))); while NodeNeedsBuildSubTree(Result) do begin BuildSubTree(Result); Node:=FindDeepestNodeAtPos(Result,P,ExceptionOnNotFound); if Node=Result then exit; Result:=Node; //debugln('TPascalParserTool.BuildSubTreeAndFindDeepestNodeAtPos B ',Result.DescAsString,' ',dbgs(NodeNeedsBuildSubTree(Result))); end; end; function TPascalParserTool.FindInterfaceNode: TCodeTreeNode; begin Result:=Tree.Root; while (Result<>nil) and (Result.Desc<>ctnInterface) do Result:=Result.NextBrother; end; function TPascalParserTool.FindImplementationNode: TCodeTreeNode; begin Result:=Tree.Root; while (Result<>nil) and (Result.Desc<>ctnImplementation) do Result:=Result.NextBrother; end; function TPascalParserTool.FindInitializationNode: TCodeTreeNode; begin Result:=Tree.Root; while (Result<>nil) and (Result.Desc<>ctnInitialization) do Result:=Result.NextBrother; end; function TPascalParserTool.FindFinalizationNode: TCodeTreeNode; begin Result:=Tree.Root; while (Result<>nil) and (Result.Desc<>ctnFinalization) do Result:=Result.NextBrother; end; function TPascalParserTool.FindMainBeginEndNode: TCodeTreeNode; begin Result:=Tree.Root; if (Result=nil) then exit; if (Result.Desc in [ctnProgram,ctnLibrary]) then Result:=Result.LastChild else begin Result:=FindImplementationNode; if Result<>nil then Result:=Result.LastChild; end; if Result=nil then exit; if Result.Desc<>ctnBeginBlock then Result:=nil; end; function TPascalParserTool.FindFirstSectionChild: TCodeTreeNode; begin Result:=Tree.Root; while (Result<>nil) and (Result.FirstChild=nil) do Result:=Result.NextBrother; if (Result=nil) then exit; Result:=Result.FirstChild; end; end.