MG: started find declaration for identifiers

git-svn-id: trunk@589 -
This commit is contained in:
lazarus 2002-01-13 12:35:55 +00:00
parent 95234d77e0
commit 48e8bf1d18
5 changed files with 284 additions and 20 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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