mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-23 08:19:41 +02:00
MG: started find declaration for identifiers
git-svn-id: trunk@589 -
This commit is contained in:
parent
95234d77e0
commit
48e8bf1d18
@ -102,6 +102,8 @@ const
|
||||
ctnPointerType = 82;
|
||||
ctnClassOfType = 83;
|
||||
|
||||
ctnWithVariable = 90;
|
||||
ctnWithStatement = 91;
|
||||
|
||||
|
||||
// combined values
|
||||
@ -278,6 +280,9 @@ begin
|
||||
ctnPointerType: Result:='Pointer ''^'' Type';
|
||||
ctnClassOfType: Result:='Class Of Type';
|
||||
|
||||
ctnWithVariable: Result:='With Variable';
|
||||
ctnWithStatement: Result:='With Statement'
|
||||
|
||||
else
|
||||
Result:='invalid descriptor';
|
||||
end;
|
||||
|
@ -91,14 +91,14 @@ type
|
||||
|
||||
function UpdateNeeded(OnlyInterfaceNeeded: boolean): boolean;
|
||||
procedure BeginParsing(DeleteNodes, OnlyInterfaceNeeded: boolean); virtual;
|
||||
procedure MoveCursorToNodeStart(ANode: TCodeTreeNode); virtual;
|
||||
procedure MoveCursorToCleanPos(ACleanPos: integer); virtual;
|
||||
procedure MoveCursorToNodeStart(ANode: TCodeTreeNode);
|
||||
procedure MoveCursorToCleanPos(ACleanPos: integer);
|
||||
function ReadTilSection(SectionType: TCodeTreeNodeDesc): boolean;
|
||||
function ReadTilBracketClose(ExceptionOnNotFound: boolean): boolean;
|
||||
function ReadBackTilBracketClose(ExceptionOnNotFound: boolean): boolean;
|
||||
function DoAtom: boolean; virtual;
|
||||
procedure ReadNextAtom; virtual;
|
||||
procedure UndoReadNextAtom; virtual;
|
||||
procedure ReadNextAtom;
|
||||
procedure UndoReadNextAtom;
|
||||
function AtomIs(const AnAtom: shortstring): boolean;
|
||||
function UpAtomIs(const AnAtom: shortstring): boolean;
|
||||
function ReadNextAtomIs(const AnAtom: shortstring): boolean;
|
||||
@ -120,10 +120,10 @@ type
|
||||
const ASource: string): integer;
|
||||
function CompareNodeUpSrc(ANode: TCodeTreeNode;
|
||||
const ASource: string): integer;
|
||||
procedure ReadPriorAtom; virtual;
|
||||
procedure ReadPriorAtom;
|
||||
|
||||
procedure CreateChildNode; virtual;
|
||||
procedure EndChildNode; virtual;
|
||||
procedure CreateChildNode;
|
||||
procedure EndChildNode;
|
||||
|
||||
procedure Clear; virtual;
|
||||
function NodeDescToStr(Desc: integer): string;
|
||||
|
@ -55,6 +55,13 @@ type
|
||||
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
|
||||
function IsIncludeDirectiveAtPos(CleanPos, CleanCodePosInFront: integer;
|
||||
var IncludeCode: TCodeBuffer): boolean;
|
||||
function FindDeclarationOfIdentifier(DeepestNode: TCodeTreeNode;
|
||||
IdentifierStartPos, IdentifierEndPos: integer;
|
||||
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
|
||||
function FindIdentifierInContext(IdentifierStartPos,
|
||||
IdentifierEndPos: integer; ContextNode: TCodeTreeNode;
|
||||
SearchInParentNodes: boolean;
|
||||
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
|
||||
public
|
||||
function FindDeclaration(CursorPos: TCodeXYPosition;
|
||||
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
|
||||
@ -97,16 +104,27 @@ writeln('TFindDeclarationTool.FindDeclaration C CleanCursorPos=',CleanCursorPos)
|
||||
exit;
|
||||
end;
|
||||
if CursorNode.Desc=ctnUsesSection then begin
|
||||
// find used unit
|
||||
Result:=FindDeclarationInUsesSection(CursorNode,CleanCursorPos,
|
||||
NewPos,NewTopLine);
|
||||
end else begin
|
||||
{ ToDo:
|
||||
1. if in begin..end block then parse for with and case statements
|
||||
2. search recursively and create/fill caches
|
||||
3. if possible use ppu files
|
||||
4. save visible identifiers for identifier completion
|
||||
...
|
||||
}
|
||||
if CursorNode.Desc=ctnBeginBlock then
|
||||
BuildSubTreeForBeginBlock(CursorNode);
|
||||
MoveCursorToCleanPos(CleanCursorPos);
|
||||
while (CurPos.StartPos>=1) and (IsIdentChar[Src[CurPos.StartPos]]) do
|
||||
dec(CurPos.StartPos);
|
||||
if (CurPos.StartPos>=1) and (IsIdentStartChar[Src[CurPos.StartPos]]) then
|
||||
begin
|
||||
CurPos.EndPos:=CurPos.StartPos;
|
||||
while (CurPos.EndPos<=SrcLen) and IsIdentChar[Src[CurPos.EndPos]] do
|
||||
inc(CurPos.EndPos);
|
||||
// find declaration of identifier
|
||||
Result:=FindDeclarationOfIdentifier(CursorNode,
|
||||
CurPos.StartPos,CurPos.EndPos,NewPos,NewTopLine);
|
||||
end else begin
|
||||
// find declaration of not identifier
|
||||
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -362,6 +380,88 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TFindDeclarationTool.FindDeclarationOfIdentifier(
|
||||
DeepestNode: TCodeTreeNode; IdentifierStartPos, IdentifierEndPos: integer;
|
||||
var NewPos: TCodeXYPosition; var NewTopLine: integer): boolean;
|
||||
{ searches an identifier in clean code, parses code in front of identifier
|
||||
For example:
|
||||
A^.B().C[].Identifier
|
||||
}
|
||||
begin
|
||||
Result:=false;
|
||||
MoveCursorToCleanPos(IdentifierStartPos);
|
||||
ReadPriorAtom;
|
||||
if AtomIsChar('.') then begin
|
||||
// first search context, then search in context
|
||||
|
||||
// ToDo
|
||||
|
||||
end else if UpAtomIs('INHERITED') then begin
|
||||
// first search ancestor, then search in ancestor
|
||||
|
||||
// ToDo
|
||||
|
||||
end else begin
|
||||
// context is DeepestNode
|
||||
Result:=FindIdentifierInContext(IdentifierStartPos,IdentifierEndPos,
|
||||
DeepestNode,true,NewPos,NewTopLine);
|
||||
end;
|
||||
{ ToDo:
|
||||
|
||||
- Difficulties:
|
||||
1. Searching recursively
|
||||
- ParentNodes
|
||||
- Ancestor Classes/Objects/Interfaces
|
||||
- with statements
|
||||
- operators: '.', '()', 'A()', '^', 'inherited'
|
||||
2. Searching enums must be searched in sub nodes
|
||||
-> all classes node trees must be built
|
||||
3. Searching in used units (interface USES and implementation USES)
|
||||
4. Searching forward for pointer types e.g. ^Tralala
|
||||
5. Mass Search: searching a compatible proc will result
|
||||
in searching every parameter type of every reachable proc
|
||||
(implementation section + interface section
|
||||
+ used interface sections + class and ancestor methods)
|
||||
How can this be achieved in good time?
|
||||
-> Caching
|
||||
- Caching:
|
||||
Where:
|
||||
For each section node (Interface, Implementation, ...)
|
||||
For each BeginBlock
|
||||
Entries: (What, Declaration Pos)
|
||||
What: Identifier -> Ansistring (to reduce memory usage,
|
||||
maintain a list of all identifier ansistrings)
|
||||
Pos: Code+SrcPos
|
||||
1. Source: TCodeTreeNode
|
||||
2. PPU, PPW, DFU, ...:
|
||||
}
|
||||
|
||||
end;
|
||||
|
||||
function TFindDeclarationTool.FindIdentifierInContext(IdentifierStartPos,
|
||||
IdentifierEndPos: integer; ContextNode: TCodeTreeNode;
|
||||
SearchInParentNodes: boolean; var NewPos: TCodeXYPosition;
|
||||
var NewTopLine: integer): boolean;
|
||||
{ searches an identifier in context node
|
||||
It does not care about code in front
|
||||
}
|
||||
begin
|
||||
Result:=false;
|
||||
if ContextNode<>nil then begin
|
||||
case ContextNode.Desc of
|
||||
ctnBeginBlock:
|
||||
begin
|
||||
if not SearchInParentNodes then exit;
|
||||
|
||||
// ToDo
|
||||
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
// DeepestNode=nil
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
ToDo:
|
||||
- ReadBackTilBlockEnd: case could also be in a record, then it should not
|
||||
close the block
|
||||
- BuildSubTreeForBeginBlock: building case statement nodes
|
||||
|
||||
}
|
||||
unit PascalParserTool;
|
||||
@ -88,6 +89,7 @@ type
|
||||
PackedTypesKeyWordFuncList: TKeyWordFunctionList;
|
||||
InnerClassKeyWordFuncList: TKeyWordFunctionList;
|
||||
ClassVarTypeKeyWordFuncList: TKeyWordFunctionList;
|
||||
BlockStatementStartKeyWordFuncList: TKeyWordFunctionList;
|
||||
ExtractMemStream: TMemoryStream;
|
||||
ExtractSearchPos: integer;
|
||||
ExtractFoundPos: integer;
|
||||
@ -138,6 +140,7 @@ type
|
||||
procedure BuildPackedTypesKeyWordFunctions; virtual;
|
||||
procedure BuildInnerClassKeyWordFunctions; virtual;
|
||||
procedure BuildClassVarTypeKeyWordFunctions; virtual;
|
||||
procedure BuildBlockStatementStartKeyWordFuncList; virtual;
|
||||
function UnexpectedKeyWord: boolean;
|
||||
// read functions
|
||||
function ReadTilProcedureHeadEnd(IsMethod, IsFunction, IsType: boolean;
|
||||
@ -150,8 +153,14 @@ type
|
||||
Attr: TProcHeadAttributes): boolean;
|
||||
function ReadUsesSection(ExceptionOnError: boolean): boolean;
|
||||
function ReadSubRange(ExceptionOnError: boolean): boolean;
|
||||
function ReadTilBlockEnd(StopOnBlockMiddlePart: boolean): boolean;
|
||||
function ReadTilBlockEnd(StopOnBlockMiddlePart,
|
||||
CreateNodes: boolean): boolean;
|
||||
function ReadBackTilBlockEnd(StopOnBlockMiddlePart: boolean): boolean;
|
||||
function ReadTilVariableEnd(ExceptionOnError: boolean): boolean;
|
||||
function ReadTilStatementEnd(ExceptionOnError,
|
||||
CreateNodes: boolean): boolean;
|
||||
function ReadWithStatement(ExceptionOnError,
|
||||
CreateNodes: boolean): boolean;
|
||||
public
|
||||
CurSection: TCodeTreeNodeDesc;
|
||||
|
||||
@ -162,9 +171,10 @@ type
|
||||
function CleanPosIsInComment(CleanPos, CleanCodePosInFront: integer;
|
||||
var CommentStart, CommentEnd: integer): boolean;
|
||||
procedure BuildTree(OnlyInterfaceNeeded: boolean); virtual;
|
||||
procedure BuildSubTreeForClass(ClassNode: TCodeTreeNode); virtual;
|
||||
procedure BuildTreeAndGetCleanPos(OnlyInterfaceNeeded: boolean;
|
||||
CursorPos: TCodeXYPosition; var CleanCursorPos: integer);
|
||||
procedure BuildSubTreeForClass(ClassNode: TCodeTreeNode); virtual;
|
||||
procedure BuildSubTreeForBeginBlock(BeginNode: TCodeTreeNode); virtual;
|
||||
function DoAtom: boolean; override;
|
||||
function ExtractPropName(PropNode: TCodeTreeNode;
|
||||
InUpperCase: boolean): string;
|
||||
@ -293,6 +303,10 @@ begin
|
||||
ClassVarTypeKeyWordFuncList:=TKeyWordFunctionList.Create;
|
||||
BuildClassVarTypeKeyWordFunctions;
|
||||
AddKeyWordFuncList(ClassVarTypeKeyWordFuncList);
|
||||
// keywords for statements
|
||||
BlockStatementStartKeyWordFuncList:=TKeyWordFunctionList.Create;
|
||||
BuildBlockStatementStartKeyWordFuncList;
|
||||
AddKeyWordFuncList(BlockStatementStartKeyWordFuncList);
|
||||
end;
|
||||
|
||||
destructor TPascalParserTool.Destroy;
|
||||
@ -425,6 +439,17 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPascalParserTool.BuildBlockStatementStartKeyWordFuncList;
|
||||
begin
|
||||
with BlockStatementStartKeyWordFuncList do begin
|
||||
Add('BEGIN' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
||||
Add('REPEAT',{$ifdef FPC}@{$endif}AllwaysTrue);
|
||||
Add('TRY' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
||||
Add('ASM' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
||||
Add('CASE' ,{$ifdef FPC}@{$endif}AllwaysTrue);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPascalParserTool.UnexpectedKeyWord: boolean;
|
||||
begin
|
||||
Result:=false;
|
||||
@ -570,6 +595,38 @@ begin
|
||||
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
|
||||
var MaxPos: integer;
|
||||
begin
|
||||
if BeginNode=nil then
|
||||
RaiseException(
|
||||
'TPascalParserTool.BuildSubTreeForBeginBlock: BeginNode=nil');
|
||||
if BeginNode.FirstChild<>nil then
|
||||
// block already parsed
|
||||
exit;
|
||||
// set CursorPos on 'begin'
|
||||
MoveCursorToNodeStart(BeginNode);
|
||||
ReadNextAtom;
|
||||
if not UpAtomIs('BEGIN') then
|
||||
RaiseException(
|
||||
'TPascalParserTool.BuildSubTreeForBeginBlock: begin expected, but '
|
||||
+GetAtom+' found');
|
||||
if BeginNode.EndPos<SrcLen then
|
||||
Maxpos:=BeginNode.EndPos
|
||||
else
|
||||
MaxPos:=SrcLen;
|
||||
repeat
|
||||
ReadNextAtom;
|
||||
if UpAtomIs('WITH') then
|
||||
ReadWithStatement(true,true);
|
||||
if UpAtomIs('CASE') then begin
|
||||
// ToDo
|
||||
end;
|
||||
until (CurPos.StartPos>=MaxPos);
|
||||
end;
|
||||
|
||||
function TPascalParserTool.GetSourceType: TCodeTreeNodeDesc;
|
||||
begin
|
||||
if Tree.Root<>nil then
|
||||
@ -1485,7 +1542,8 @@ begin
|
||||
end;
|
||||
|
||||
function TPascalParserTool.ReadTilBlockEnd(
|
||||
StopOnBlockMiddlePart: boolean): boolean;
|
||||
StopOnBlockMiddlePart, CreateNodes: boolean): boolean;
|
||||
// after reading cursor will be on the keyword ending the block (e.g. 'end')
|
||||
var BlockType: TEndBlockType;
|
||||
TryType: TTryType;
|
||||
begin
|
||||
@ -1525,7 +1583,7 @@ begin
|
||||
if BlockType=ebtAsm then
|
||||
RaiseException('syntax error: unexpected keyword "'+GetAtom+'" found');
|
||||
if (BlockType<>ebtRecord) or (not UpAtomIs('CASE')) then
|
||||
ReadTilBlockEnd(false);
|
||||
ReadTilBlockEnd(false,CreateNodes);
|
||||
end else if UpAtomIs('UNTIL') then begin
|
||||
if BlockType=ebtRepeat then
|
||||
break;
|
||||
@ -1545,6 +1603,8 @@ begin
|
||||
end else
|
||||
RaiseException(
|
||||
'syntax error: "end" expected, but "'+GetAtom+'" found');
|
||||
end else if CreateNodes and UpAtomIs('WITH') then begin
|
||||
ReadWithStatement(true,CreateNodes);
|
||||
end;
|
||||
until false;
|
||||
end;
|
||||
@ -1617,6 +1677,105 @@ begin
|
||||
until false;
|
||||
end;
|
||||
|
||||
function TPascalParserTool.ReadTilVariableEnd(
|
||||
ExceptionOnError: boolean): boolean;
|
||||
{ Examples:
|
||||
A
|
||||
A.B^.C[...].D(...).E
|
||||
(...).A
|
||||
@B
|
||||
|
||||
}
|
||||
begin
|
||||
while AtomIsChar('@') do
|
||||
ReadNextAtom;
|
||||
while UpAtomIs('INHERITED') do
|
||||
ReadNextAtom;
|
||||
Result:=(AtomIsIdentifier(false) or AtomIsChar('(') or AtomIsChar('['));
|
||||
if not Result then exit;
|
||||
repeat
|
||||
if AtomIsIdentifier(false) then
|
||||
ReadNextAtom;
|
||||
if AtomIsChar('(') or AtomIsChar('[') then begin
|
||||
Result:=ReadTilBracketClose(ExceptionOnError);
|
||||
if not Result then exit;
|
||||
end;
|
||||
if AtomIsChar('.') 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:=false;
|
||||
if BlockStatementStartKeyWordFuncList.DoItUppercase(UpperSrc,CurPos.StartPos,
|
||||
CurPos.EndPos-CurPos.StartPos) then
|
||||
begin
|
||||
if not ReadTilBlockEnd(ExceptionOnError,CreateNodes) then exit;
|
||||
ReadNextAtom;
|
||||
if not AtomIsChar(';') then UndoReadNextAtom;
|
||||
end else if UpAtomIs('WITH') then begin
|
||||
if not ReadWithStatement(ExceptionOnError,CreateNodes) then exit;
|
||||
end else begin
|
||||
// read till semicolon or 'end'
|
||||
while (not AtomIsChar(';')) do begin
|
||||
ReadNextAtom;
|
||||
if UpAtomIs('END') then begin
|
||||
UndoReadNextAtom;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
Result:=true;
|
||||
end;
|
||||
|
||||
function TPascalParserTool.ReadWithStatement(ExceptionOnError,
|
||||
CreateNodes: boolean): boolean;
|
||||
begin
|
||||
ReadNextAtom;
|
||||
if CreateNodes then begin
|
||||
CreateChildNode;
|
||||
CurNode.Desc:=ctnWithVariable
|
||||
end;
|
||||
ReadTilVariableEnd(true);
|
||||
while AtomIsChar(',') do begin
|
||||
CurNode.EndPos:=LastAtoms.GetValueAt(0).EndPos;
|
||||
if CreateNodes then
|
||||
EndChildNode;
|
||||
ReadNextAtom;
|
||||
if CreateNodes then begin
|
||||
CreateChildNode;
|
||||
CurNode.Desc:=ctnWithVariable
|
||||
end;
|
||||
ReadTilVariableEnd(true);
|
||||
end;
|
||||
if not UpAtomIs('DO') then begin
|
||||
if ExceptionOnError then
|
||||
RaiseException('syntax error: do expected, but '+GetAtom+' found')
|
||||
else begin
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
ReadNextAtom;
|
||||
if CreateNodes then begin
|
||||
CreateChildNode;
|
||||
CurNode.Desc:=ctnWithStatement;
|
||||
end;
|
||||
ReadTilStatementEnd(true,CreateNodes);
|
||||
if CreateNodes then begin
|
||||
CurNode.EndPos:=CurPos.StartPos;
|
||||
EndChildNode; // ctnWithStatement
|
||||
CurNode.EndPos:=CurPos.StartPos;
|
||||
EndChildNode; // ctnWithVariable
|
||||
end;
|
||||
Result:=true;
|
||||
end;
|
||||
|
||||
function TPascalParserTool.KeyWordFuncBeginEnd: boolean;
|
||||
// Keyword: begin, asm
|
||||
var BeginKeyWord: shortstring;
|
||||
@ -1632,7 +1791,7 @@ begin
|
||||
CurNode.Desc:=ctnAsmBlock;
|
||||
end;
|
||||
// search "end"
|
||||
ReadTilBlockEnd(false);
|
||||
ReadTilBlockEnd(false,false);
|
||||
// close node
|
||||
if ChildNodeCreated then begin
|
||||
CurNode.EndPos:=CurPos.EndPos;
|
||||
|
@ -1008,7 +1008,7 @@ writeln('TStandardCodeTool.FindBlockCounterPart C Word=',GetAtom);
|
||||
if UpAtomIs('BEGIN') or UpAtomIs('CASE') or UpAtomIs('ASM')
|
||||
or UpAtomIs('RECORD') or UpAtomIs('TRY') or UpAtomIs('REPEAT') then begin
|
||||
// read forward till END, FINALLY, EXCEPT
|
||||
ReadTilBlockEnd(true);
|
||||
ReadTilBlockEnd(true,false);
|
||||
end else if UpAtomIs('END') or UpAtomIs('FINALLY') or UpAtomIs('EXCEPT')
|
||||
or UpAtomIs('UNTIL') then
|
||||
begin
|
||||
|
Loading…
Reference in New Issue
Block a user