lazarus/components/codetools/pascalparsertool.pas
mattias 5f4f03d6e9 IDE: using new codetools FPC caches
IDE: fixed macro FPCVer for multiple versions used by fpc.exe
IDE: fixed rescan of FPC sources if not changed, bug #16824
codetools: replaced fpc source heuristic with rule set, needed for bug #13912, #14572
IDE: fixed unneeded rescan of fpc sources if only target changed, needed for 12828
IDE: fixed calling compiler on every start, needed by lazarus on a stick
codetools: fixed search for fpc units without ppu, needed for 15534
IDE: implemented cache for fpc include files, needed by debugger
lazbuild: fixed using non default lclwidgettype of lpi
IDE: fixed auto update if fpc.cfg or target compiler changed, needed for 16824

git-svn-id: trunk@26796 -
2010-07-24 08:12:27 +00:00

5085 lines
155 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 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<SrcLen then
Maxpos:=BeginNode.EndPos
else
MaxPos:=SrcLen;
repeat
ReadNextAtom;
if BlockStatementStartKeyWordFuncList.DoItCaseInsensitive(Src,CurPos.StartPos,
CurPos.EndPos-CurPos.StartPos) then
begin
if not ReadTilBlockEnd(false,true) then RaiseEndOfSourceExpected;
end else if UpAtomIs('WITH') then
ReadWithStatement(true,true);
until (CurPos.StartPos>=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 <identifier>
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 <id or number>
dispid <id>
enumerator <id>
}
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 <id or number>;
external;
external <id>;
external name <id> delayed;
external <id or number> name <id>;
external <id or number> index <id>;
[alias: <string constant>]
[external name <string constant>]
[internconst:in_const_round, external name 'FPC_ROUND'];
dispid <id>;
}
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 <id or number>;
external;
external <id>;
external name <id> delayed;
external <id or number> name <id>;
external <id or number> index <id>;
[alias: <string constant>]
[external name <string constant>]
[internconst:in_const_round, external name 'FPC_ROUND'];
dispid <id>;
enumerator <id>
}
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 <id or number>, read <id>, write <id>, stored <id>, default <constant>,
implements <id>[,<id>...], nodefault
for dispinterfaces:
dispid <number>, readonly, writeonly
property modifiers after semicolon:
default, deprecated, enumerator <id>
}
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 <id>;
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<List> = 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 <id>;
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 = <Const>; or Name : type = <Const>;
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 <type>'
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<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<integer,string>;
// type TListOfChar = specialize Classes.TGenericList<integer,objpas.integer>;
// type l = class(specialize TFPGObjectList<TControl>)
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.