mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-24 06:00:15 +02:00
1348 lines
32 KiB
ObjectPascal
1348 lines
32 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TLFMTree - a tree structure for LFM files.
|
|
}
|
|
unit LFMTrees;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, TypInfo, AVL_Tree,
|
|
// LazUtils
|
|
LazUtilities,
|
|
// Codetools
|
|
FileProcs, BasicCodeTools, CodeCache;
|
|
|
|
type
|
|
{ TLFMTreeNode }
|
|
|
|
TLFMNodeType = (
|
|
lfmnObject,
|
|
lfmnProperty,
|
|
lfmnValue,
|
|
lfmnEnum
|
|
);
|
|
|
|
TLFMTree = class;
|
|
|
|
TLFMTreeNode = class
|
|
public
|
|
TheType: TLFMNodeType;
|
|
StartPos: integer;
|
|
EndPos: integer;
|
|
Parent: TLFMTreeNode;
|
|
FirstChild: TLFMTreeNode;
|
|
LastChild: TLFMTreeNode;
|
|
PrevSibling: TLFMTreeNode;
|
|
NextSibling: TLFMTreeNode;
|
|
Tree: TLFMTree;
|
|
constructor CreateVirtual; virtual;
|
|
destructor Destroy; override;
|
|
procedure Unbind;
|
|
procedure AddChild(ANode: TLFMTreeNode);
|
|
function GetIdentifier: string; virtual;
|
|
procedure FindIdentifier(out IdentStart, IdentEnd: integer);
|
|
function GetPath: string;
|
|
function Next(SkipChildren: Boolean = False): TLFMTreeNode;
|
|
end;
|
|
|
|
TLFMTreeNodeClass = class of TLFMTreeNode;
|
|
|
|
|
|
{ TLFMObjectNode - a LFM object }
|
|
|
|
TLFMObjectNode = class(TLFMTreeNode)
|
|
public
|
|
IsInherited: boolean;
|
|
IsInline: boolean;
|
|
ChildPos: Integer;
|
|
Name: string;
|
|
NamePosition: integer;
|
|
TypeUnitName: string;
|
|
TypeUnitNamePosition: integer;
|
|
TypeName: string;
|
|
TypeNamePosition: integer;
|
|
AncestorTool: TObject; // TFindDeclarationTool
|
|
AncestorNode: TObject; // TCodeTreeNode
|
|
AncestorContextValid: boolean;
|
|
constructor CreateVirtual; override;
|
|
function GetFullName(UnitNameSep: char = '/'; WithName: boolean = true): string;
|
|
function GetIdentifier: string; override;
|
|
end;
|
|
|
|
{ TLFMNameParts }
|
|
|
|
TLFMNameParts = class
|
|
private
|
|
FCount: integer;
|
|
FNames: ^String;
|
|
FNamePositions: PInteger;
|
|
function GetNamePositions(Index: integer): integer;
|
|
function GetNames(Index: integer): string;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Add(const Name: string; NamePosition: integer);
|
|
property Count: integer read FCount;
|
|
property Names[Index: integer]: string read GetNames;
|
|
property NamePositions[Index: integer]: integer read GetNamePositions;
|
|
end;
|
|
|
|
{ TLFMPropertyNode - a LFM property }
|
|
|
|
TLFMPropertyNode = class(TLFMTreeNode)
|
|
public
|
|
CompleteName: string;
|
|
NameParts: TLFMNameParts;
|
|
constructor CreateVirtual; override;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Add(const Name: string; NamePosition: integer);
|
|
end;
|
|
|
|
|
|
{ TLFMValueNode - a LFM value }
|
|
|
|
TLFMValueType = (
|
|
lfmvNone,
|
|
lfmvInteger,
|
|
lfmvFloat,
|
|
lfmvString,
|
|
lfmvSymbol,
|
|
lfmvSet,
|
|
lfmvList,
|
|
lfmvCollection,
|
|
lfmvBinary
|
|
);
|
|
|
|
TLFMValueNode = class(TLFMTreeNode)
|
|
public
|
|
ValueType: TLFMValueType;
|
|
constructor CreateVirtual; override;
|
|
function ReadString: string;
|
|
end;
|
|
|
|
|
|
{ TLFMValueNodeSymbol - a LFM value of type symbol }
|
|
|
|
TLFMSymbolType = (
|
|
lfmsNone,
|
|
lfmsTrue,
|
|
lfmsFalse,
|
|
lfmsNil,
|
|
lfmsIdentifier
|
|
);
|
|
|
|
TLFMValueNodeSymbol = class(TLFMValueNode)
|
|
public
|
|
SymbolType: TLFMSymbolType;
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMValueNodeSet - a LFM value of type set }
|
|
|
|
TLFMValueNodeSet = class(TLFMValueNode)
|
|
public
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMValueNodeList - a list of LFM values }
|
|
|
|
TLFMValueNodeList = class(TLFMValueNode)
|
|
public
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMValueNodeCollection - a LFM collection }
|
|
|
|
TLFMValueNodeCollection = class(TLFMValueNode)
|
|
public
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMValueNodeBinary - LFM binary data }
|
|
|
|
TLFMValueNodeBinary = class(TLFMValueNode)
|
|
public
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMEnumNode - an enum of a value of type set}
|
|
|
|
TLFMEnumNode = class(TLFMTreeNode)
|
|
public
|
|
constructor CreateVirtual; override;
|
|
end;
|
|
|
|
|
|
{ TLFMError }
|
|
|
|
TLFMErrorType = (
|
|
lfmeNoError,
|
|
lfmeParseError,
|
|
lfmeMissingRoot,
|
|
lfmeUnitNotFound,
|
|
lfmeIdentifierNotFound,
|
|
lfmeIdentifierNotPublished,
|
|
lfmeIdentifierMissingInCode,
|
|
lfmeObjectNameMissing,
|
|
lfmeObjectIncompatible,
|
|
lfmePropertyNameMissing,
|
|
lfmePropertyHasNoSubProperties,
|
|
lfmeEndNotFound
|
|
);
|
|
TLFMErrorTypes = set of TLFMErrorType;
|
|
|
|
TLFMError = class
|
|
public
|
|
Tree: TLFMTree;
|
|
Node: TLFMTreeNode;
|
|
NextError: TLFMError;
|
|
PrevError: TLFMError;
|
|
ErrorType: TLFMErrorType;
|
|
ErrorMessage: string;
|
|
Source: TCodeBuffer;
|
|
Position: integer;
|
|
Caret: TPoint;
|
|
constructor Create;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
function AsString: string;
|
|
procedure AddToTree(ATree: TLFMTree);
|
|
procedure Unbind;
|
|
function FindParentError: TLFMError;
|
|
function FindContextNode: TLFMTreeNode;
|
|
function IsMissingObjectType: boolean;
|
|
function GetNodePath: string;
|
|
end;
|
|
|
|
TLFMTrees = class;
|
|
|
|
{ TLFMTree }
|
|
|
|
TLFMTree = class
|
|
protected
|
|
Parser: TParser;
|
|
TokenStart: LongInt;
|
|
function NextToken: Char;
|
|
procedure ProcessValue;
|
|
procedure ProcessProperty;
|
|
procedure ProcessObject;
|
|
procedure CreateChildNode(NodeClass: TLFMTreeNodeClass);
|
|
procedure CloseChildNode;
|
|
public
|
|
Root: TLFMTreeNode;
|
|
CurNode: TLFMTreeNode;
|
|
LFMBuffer: TCodeBuffer;
|
|
LFMBufferChangeStep: integer;
|
|
FirstError: TLFMError;
|
|
LastError: TLFMError;
|
|
Trees: TLFMTrees;
|
|
constructor Create(TheTrees: TLFMTrees; aLFMBuf: TCodeBuffer);
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure ClearErrors;
|
|
function Parse(LFMBuf: TCodeBuffer = nil): boolean;
|
|
function ParseIfNeeded: boolean;
|
|
function UpdateNeeded: boolean;
|
|
function PositionToCaret(p: integer): TPoint;
|
|
procedure AddError(ErrorType: TLFMErrorType; LFMNode: TLFMTreeNode;
|
|
const ErrorMessage: string; ErrorPosition: integer);
|
|
function FindErrorAtLine(Line: integer): TLFMError;
|
|
function FindErrorAtNode(Node: TLFMTreeNode): TLFMError;
|
|
function FindError(ErrorTypes: TLFMErrorTypes): TLFMError;
|
|
function FirstErrorAsString: string;
|
|
|
|
function FindProperty(PropertyPath: string;
|
|
ContextNode: TLFMTreeNode): TLFMPropertyNode;
|
|
|
|
procedure WriteDebugReport;
|
|
end;
|
|
|
|
{ TLFMTrees }
|
|
|
|
TLFMTrees = class
|
|
private
|
|
FItems: TAVLTree;// tree of TLFMTree sorted for LFMBuffer
|
|
FClearing: Boolean;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function GetLFMTree(LFMBuffer: TCodeBuffer;
|
|
CreateIfNotExists: boolean): TLFMTree;
|
|
end;
|
|
|
|
TInstancePropInfo = record
|
|
Instance: TPersistent;
|
|
PropInfo: PPropInfo;
|
|
end;
|
|
PInstancePropInfo = ^TInstancePropInfo;
|
|
|
|
const
|
|
LFMErrorTypeNames: array[TLFMErrorType] of string = (
|
|
'NoError',
|
|
'ParseError',
|
|
'MissingRoot',
|
|
'UnitNotFound',
|
|
'IdentifierNotFound',
|
|
'IdentifierNotPublished',
|
|
'IdentifierMissingInCode',
|
|
'ObjectNameMissing',
|
|
'ObjectIncompatible',
|
|
'PropertyNameMissing',
|
|
'PropertyHasNoSubProperties',
|
|
'EndNotFound'
|
|
);
|
|
|
|
TLFMValueTypeNames: array[TLFMValueType] of string = (
|
|
'None',
|
|
'Integer',
|
|
'Float',
|
|
'String',
|
|
'Symbol',
|
|
'Set',
|
|
'List',
|
|
'Collection',
|
|
'Binary'
|
|
);
|
|
|
|
procedure FreeListOfPInstancePropInfo(List: TFPList);
|
|
function CompareLFMTreesByLFMBuffer(Data1, Data2: Pointer): integer;
|
|
function CompareLFMBufWithTree(Buf, Tree: Pointer): integer;
|
|
|
|
var
|
|
DefaultLFMTrees: TLFMTrees = nil;
|
|
|
|
implementation
|
|
|
|
|
|
procedure FreeListOfPInstancePropInfo(List: TFPList);
|
|
var
|
|
i: Integer;
|
|
p: PInstancePropInfo;
|
|
begin
|
|
if List=nil then exit;
|
|
for i:=0 to List.Count-1 do begin
|
|
p:=PInstancePropInfo(List[i]);
|
|
Dispose(p);
|
|
end;
|
|
List.Free;
|
|
end;
|
|
|
|
function CompareLFMTreesByLFMBuffer(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=ComparePointers(TLFMTree(Data1).LFMBuffer,TLFMTree(Data2).LFMBuffer);
|
|
end;
|
|
|
|
function CompareLFMBufWithTree(Buf, Tree: Pointer): integer;
|
|
begin
|
|
Result:=ComparePointers(Buf,TLFMTree(Tree).LFMBuffer);
|
|
end;
|
|
|
|
|
|
{ TLFMTree }
|
|
|
|
constructor TLFMTree.Create;
|
|
begin
|
|
end;
|
|
|
|
destructor TLFMTree.Destroy;
|
|
begin
|
|
Clear;
|
|
if (Trees<>nil) and (not Trees.FClearing) then Trees.FItems.Remove(Self);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLFMTree.Clear;
|
|
begin
|
|
// do not set LFMBuffer to nil
|
|
TokenStart:=0;
|
|
CurNode:=nil;
|
|
ClearErrors;
|
|
while Root<>nil do Root.Free;
|
|
end;
|
|
|
|
procedure TLFMTree.ClearErrors;
|
|
begin
|
|
while FirstError<>nil do FirstError.Free;
|
|
end;
|
|
|
|
function TLFMTree.Parse(LFMBuf: TCodeBuffer = nil): boolean;
|
|
var
|
|
LFMStream: TMemoryStream;
|
|
Src: String;
|
|
begin
|
|
Result:=false;
|
|
Clear;
|
|
if LFMBuf<>LFMBuffer then begin
|
|
DebugLn(['TLFMTree.Parse New=',LFMBuf.Filename]);
|
|
DebugLn(['TLFMTree.Parse Old=',LFMBuffer.Filename]);
|
|
if Trees<>nil then
|
|
raise Exception.Create('TLFMTree.Parse: changing LFMBuffer in Tree is not allowed');
|
|
LFMBuffer:=LFMBuf;
|
|
end;
|
|
LFMBufferChangeStep:=LFMBuffer.ChangeStep;
|
|
|
|
LFMStream:=TMemoryStream.Create;
|
|
Src:=LFMBuffer.Source;
|
|
if Src<>'' then begin
|
|
LFMStream.Write(Src[1],length(Src));
|
|
LFMStream.Position:=0;
|
|
end;
|
|
Parser := TParser.Create(LFMStream);
|
|
try
|
|
try
|
|
repeat
|
|
ProcessObject;
|
|
until (not Parser.TokenSymbolIs('OBJECT'))
|
|
and (not Parser.TokenSymbolIs('INHERITED'))
|
|
and (not Parser.TokenSymbolIs('INLINE'));
|
|
Result:=true;
|
|
except
|
|
on E: EParserError do begin
|
|
AddError(lfmeParseError,CurNode,E.Message,Parser.SourcePos);
|
|
end;
|
|
end;
|
|
finally
|
|
Parser.Free;
|
|
Parser:=nil;
|
|
LFMStream.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLFMTree.ParseIfNeeded: boolean;
|
|
begin
|
|
if not UpdateNeeded then exit(true);
|
|
Result:=Parse(LFMBuffer);
|
|
end;
|
|
|
|
function TLFMTree.UpdateNeeded: boolean;
|
|
begin
|
|
Result:=(LFMBuffer=nil) or (LFMBuffer.ChangeStep<>LFMBufferChangeStep)
|
|
or (FirstError<>nil);
|
|
end;
|
|
|
|
function TLFMTree.PositionToCaret(p: integer): TPoint;
|
|
begin
|
|
Result:=Point(0,0);
|
|
LFMBuffer.AbsoluteToLineCol(p,Result.Y,Result.X);
|
|
end;
|
|
|
|
procedure TLFMTree.AddError(ErrorType: TLFMErrorType;
|
|
LFMNode: TLFMTreeNode; const ErrorMessage: string; ErrorPosition: integer);
|
|
var
|
|
NewError: TLFMError;
|
|
begin
|
|
NewError:=TLFMError.Create;
|
|
NewError.Node:=LFMNode;
|
|
NewError.ErrorType:=ErrorType;
|
|
NewError.ErrorMessage:=ErrorMessage;
|
|
NewError.Source:=LFMBuffer;
|
|
NewError.Position:=ErrorPosition;
|
|
NewError.Caret:=PositionToCaret(NewError.Position);
|
|
//DebugLn('TLFMTree.AddError ',NewError.AsString, ' NodePath=',NewError.GetNodePath);
|
|
NewError.AddToTree(Self);
|
|
end;
|
|
|
|
function TLFMTree.FindErrorAtLine(Line: integer): TLFMError;
|
|
begin
|
|
Result:=FirstError;
|
|
while Result<>nil do begin
|
|
if (Result.Caret.Y=Line) and (Line>=1) then exit;
|
|
Result:=Result.NextError;
|
|
end;
|
|
end;
|
|
|
|
function TLFMTree.FindErrorAtNode(Node: TLFMTreeNode): TLFMError;
|
|
begin
|
|
Result:=FirstError;
|
|
while Result<>nil do begin
|
|
if (Result.Node=Node) and (Node<>nil) then exit;
|
|
Result:=Result.NextError;
|
|
end;
|
|
end;
|
|
|
|
function TLFMTree.FindError(ErrorTypes: TLFMErrorTypes): TLFMError;
|
|
begin
|
|
Result:=FirstError;
|
|
while (Result<>nil) and (not (Result.ErrorType in ErrorTypes)) do
|
|
Result:=Result.NextError;
|
|
end;
|
|
|
|
function TLFMTree.FirstErrorAsString: string;
|
|
begin
|
|
Result:='';
|
|
if FirstError<>nil then Result:=FirstError.ErrorMessage;
|
|
end;
|
|
|
|
function TLFMTree.FindProperty(PropertyPath: string; ContextNode: TLFMTreeNode
|
|
): TLFMPropertyNode;
|
|
var
|
|
Node: TLFMTreeNode;
|
|
ObjNode: TLFMObjectNode;
|
|
p: LongInt;
|
|
FirstPart: String;
|
|
RestParts: String;
|
|
begin
|
|
if ContextNode=nil then
|
|
Node:=Root
|
|
else
|
|
Node:=ContextNode.FirstChild;
|
|
p:=System.Pos('.',PropertyPath);
|
|
FirstPart:=copy(PropertyPath,1,p-1);
|
|
RestParts:=copy(PropertyPath,p+1,length(PropertyPath));
|
|
while Node<>nil do begin
|
|
if Node is TLFMPropertyNode then begin
|
|
Result:=TLFMPropertyNode(Node);
|
|
if SysUtils.CompareText(Result.CompleteName,PropertyPath)=0 then
|
|
exit;
|
|
end else if (Node is TLFMObjectNode)
|
|
and (RestParts<>'') then begin
|
|
ObjNode:=TLFMObjectNode(Node);
|
|
if CompareIdentifiers(PChar(ObjNode.Name),PChar(FirstPart))=0 then
|
|
begin
|
|
Result:=FindProperty(RestParts,ObjNode);
|
|
exit;
|
|
end;
|
|
end;
|
|
Node:=Node.NextSibling;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TLFMTree.WriteDebugReport;
|
|
var
|
|
Src: string;
|
|
|
|
procedure WriteNode(const Prefix: string; Node: TLFMTreeNode);
|
|
var
|
|
Child: TLFMTreeNode;
|
|
EndPos: LongInt;
|
|
begin
|
|
if Node=nil then exit;
|
|
Child:=Node.FirstChild;
|
|
EndPos:=Node.EndPos;
|
|
if (Child<>nil) and (EndPos>Child.StartPos) then
|
|
EndPos:=Child.StartPos;
|
|
DebugLn([Prefix,dbgstr(copy(Src,Node.StartPos,EndPos-Node.StartPos))]);
|
|
while Child<>nil do begin
|
|
WriteNode(Prefix+' ',Child);
|
|
Child:=Child.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if LFMBuffer=nil then begin
|
|
DebugLn(['TLFMTree.WriteDebugReport LFMBuffer=nil']);
|
|
end;
|
|
DebugLn(['TLFMTree.WriteDebugReport ',LFMBuffer.Filename]);
|
|
Src:=LFMBuffer.Source;
|
|
WriteNode('',Root);
|
|
end;
|
|
|
|
function TLFMTree.NextToken: Char;
|
|
begin
|
|
TokenStart:=Parser.SourcePos+1;
|
|
while (TokenStart<=LFMBuffer.SourceLength)
|
|
and (LFMBuffer.Source[TokenStart] in [' ',#9,#10,#13]) do
|
|
inc(TokenStart);
|
|
Result:=Parser.NextToken;
|
|
end;
|
|
|
|
procedure TLFMTree.ProcessValue;
|
|
var
|
|
s: String;
|
|
MemStream: TMemoryStream;
|
|
SymbolNode: TLFMValueNodeSymbol;
|
|
begin
|
|
case Parser.Token of
|
|
|
|
toInteger:
|
|
begin
|
|
CreateChildNode(TLFMValueNode);
|
|
TLFMValueNode(CurNode).ValueType:=lfmvInteger;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
toFloat:
|
|
begin
|
|
CreateChildNode(TLFMValueNode);
|
|
TLFMValueNode(CurNode).ValueType:=lfmvFloat;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
Classes.toString, toWString:
|
|
begin
|
|
CreateChildNode(TLFMValueNode);
|
|
TLFMValueNode(CurNode).ValueType:=lfmvString;
|
|
while NextToken = '+' do begin
|
|
NextToken; // Get next string fragment
|
|
if not (Parser.Token in [Classes.toString,toWString]) then
|
|
Parser.CheckToken(Classes.toString);
|
|
end;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
toSymbol:
|
|
begin
|
|
CreateChildNode(TLFMValueNodeSymbol);
|
|
SymbolNode:=TLFMValueNodeSymbol(CurNode);
|
|
if SymbolNode=nil then ;
|
|
s := Parser.TokenString;
|
|
if SysUtils.CompareText(s, 'End') = 0 then
|
|
SymbolNode.SymbolType:=lfmsNone
|
|
else if SysUtils.CompareText(s, 'True') = 0 then
|
|
SymbolNode.SymbolType:=lfmsTrue
|
|
else if SysUtils.CompareText(s, 'False') = 0 then
|
|
SymbolNode.SymbolType:=lfmsFalse
|
|
else if SysUtils.CompareText(s, 'nil') = 0 then
|
|
SymbolNode.SymbolType:=lfmsNil
|
|
else
|
|
begin
|
|
SymbolNode.SymbolType:=lfmsIdentifier;
|
|
Parser.TokenComponentIdent;
|
|
end;
|
|
if SymbolNode.SymbolType<>lfmsNone then
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
// Set
|
|
'[':
|
|
begin
|
|
CreateChildNode(TLFMValueNodeSet);
|
|
NextToken;
|
|
if Parser.Token <> ']' then
|
|
while True do
|
|
begin
|
|
CreateChildNode(TLFMEnumNode);
|
|
Parser.CheckToken(toSymbol);
|
|
CloseChildNode;
|
|
NextToken;
|
|
if Parser.Token = ']' then
|
|
break;
|
|
Parser.CheckToken(',');
|
|
NextToken;
|
|
end;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
// List
|
|
'(':
|
|
begin
|
|
CreateChildNode(TLFMValueNodeList);
|
|
NextToken;
|
|
while Parser.Token <> ')' do
|
|
ProcessValue;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
// Collection
|
|
'<':
|
|
begin
|
|
CreateChildNode(TLFMValueNodeCollection);
|
|
NextToken;
|
|
while Parser.Token <> '>' do
|
|
begin
|
|
Parser.CheckTokenSymbol('item');
|
|
NextToken;
|
|
CreateChildNode(TLFMValueNodeList);
|
|
while not Parser.TokenSymbolIs('end') do
|
|
ProcessProperty;
|
|
NextToken; // Skip 'end'
|
|
CloseChildNode;
|
|
end;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
// Binary data
|
|
'{':
|
|
begin
|
|
CreateChildNode(TLFMValueNodeBinary);
|
|
MemStream := TMemoryStream.Create;
|
|
try
|
|
Parser.HexToBinary(MemStream);
|
|
finally
|
|
MemStream.Free;
|
|
end;
|
|
NextToken;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
else
|
|
Parser.Error('invalid property');
|
|
end;
|
|
end;
|
|
|
|
procedure TLFMTree.ProcessProperty;
|
|
var
|
|
PropertyNode: TLFMPropertyNode;
|
|
begin
|
|
CreateChildNode(TLFMPropertyNode);
|
|
PropertyNode:=TLFMPropertyNode(CurNode);
|
|
if PropertyNode=nil then ;
|
|
// Get name of property
|
|
Parser.CheckToken(toSymbol);
|
|
PropertyNode.Add(Parser.TokenString,TokenStart);
|
|
while True do begin
|
|
NextToken;
|
|
if Parser.Token <> '.' then break;
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
PropertyNode.Add(Parser.TokenString,TokenStart);
|
|
end;
|
|
Parser.CheckToken('=');
|
|
NextToken;
|
|
ProcessValue;
|
|
CloseChildNode;
|
|
end;
|
|
|
|
procedure TLFMTree.ProcessObject;
|
|
var
|
|
ObjectNode: TLFMObjectNode;
|
|
ObjectStartLine: LongInt;
|
|
HasDot: Boolean;
|
|
begin
|
|
CreateChildNode(TLFMObjectNode);
|
|
ObjectNode:=TLFMObjectNode(CurNode);
|
|
if Parser.TokenSymbolIs('OBJECT') then
|
|
ObjectNode.IsInherited := False
|
|
else if Parser.TokenSymbolIs('INHERITED') then
|
|
ObjectNode.IsInherited := True
|
|
else begin
|
|
Parser.CheckTokenSymbol('INLINE');
|
|
ObjectNode.IsInline := True;
|
|
end;
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
if not Parser.TokenSymbolIs('END') then begin
|
|
ObjectStartLine:=Parser.SourceLine;
|
|
|
|
// read TypeName
|
|
// or ClassName.TypeName
|
|
// or Namespace.UnitName/ClassName.TypeName
|
|
// or Name:TypeName
|
|
// or Name:TypeName[decimal]
|
|
// or Name:UnitName/TypeName
|
|
// or Name:Namespace.UnitName/ClassName.TypeName
|
|
ObjectNode.Name := '';
|
|
ObjectNode.TypeName := Parser.TokenString;
|
|
ObjectNode.TypeNamePosition:=TokenStart;
|
|
ObjectNode.ChildPos := -1;
|
|
NextToken;
|
|
HasDot:=false;
|
|
while Parser.Token = '.' do begin
|
|
HasDot:=true;
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
ObjectNode.TypeName := ObjectNode.TypeName+'.'+Parser.TokenString;
|
|
NextToken;
|
|
end;
|
|
|
|
if (not HasDot) and (Parser.Token = ':') then begin
|
|
// Name:TypeName
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
ObjectNode.Name := ObjectNode.TypeName;
|
|
ObjectNode.NamePosition:=ObjectNode.TypeNamePosition;
|
|
ObjectNode.TypeName := Parser.TokenString;
|
|
ObjectNode.TypeNamePosition:=TokenStart;
|
|
NextToken;
|
|
while Parser.Token = '.' do begin
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
ObjectNode.TypeName := ObjectNode.TypeName+'.'+Parser.TokenString;
|
|
NextToken;
|
|
end;
|
|
end;
|
|
|
|
if Parser.Token = '/' then begin
|
|
// TypeUnitName/TypeName
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
ObjectNode.TypeUnitName := ObjectNode.TypeName;
|
|
ObjectNode.TypeUnitNamePosition:=ObjectNode.TypeNamePosition;
|
|
ObjectNode.TypeName := Parser.TokenString;
|
|
ObjectNode.TypeNamePosition:=TokenStart;
|
|
NextToken;
|
|
while Parser.Token = '.' do begin
|
|
NextToken;
|
|
Parser.CheckToken(toSymbol);
|
|
ObjectNode.TypeName := ObjectNode.TypeName+'.'+Parser.TokenString;
|
|
NextToken;
|
|
end;
|
|
end;
|
|
|
|
if Parser.Token = '[' then begin
|
|
NextToken;
|
|
ObjectNode.ChildPos := Parser.TokenInt;
|
|
NextToken;
|
|
Parser.CheckToken(']');
|
|
NextToken;
|
|
end;
|
|
|
|
// read property list
|
|
while not (Parser.TokenSymbolIs('END')
|
|
or Parser.TokenSymbolIs('OBJECT')
|
|
or Parser.TokenSymbolIs('INHERITED')
|
|
or Parser.TokenSymbolIs('INLINE')) do
|
|
ProcessProperty;
|
|
|
|
// read child objects
|
|
while not Parser.TokenSymbolIs('END') do begin
|
|
if Parser.Token=toEOF then begin
|
|
Parser.Error('END not found for'
|
|
+' object='+ObjectNode.GetFullName
|
|
+' starting at line '+IntToStr(ObjectStartLine));
|
|
end;
|
|
ProcessObject;
|
|
end;
|
|
end;
|
|
NextToken; // Skip 'END' token
|
|
|
|
CloseChildNode;
|
|
end;
|
|
|
|
procedure TLFMTree.CreateChildNode(NodeClass: TLFMTreeNodeClass);
|
|
var
|
|
NewNode: TLFMTreeNode;
|
|
begin
|
|
NewNode:=NodeClass.CreateVirtual;
|
|
NewNode.Tree:=Self;
|
|
NewNode.StartPos:=TokenStart;
|
|
NewNode.EndPos:=0;
|
|
if CurNode<>nil then begin
|
|
CurNode.AddChild(NewNode);
|
|
end else begin
|
|
Root:=NewNode;
|
|
end;
|
|
CurNode:=NewNode;
|
|
end;
|
|
|
|
procedure TLFMTree.CloseChildNode;
|
|
begin
|
|
if CurNode.EndPos<1 then
|
|
CurNode.EndPos:=TokenStart;
|
|
CurNode:=CurNode.Parent;
|
|
end;
|
|
|
|
constructor TLFMTree.Create(TheTrees: TLFMTrees; aLFMBuf: TCodeBuffer);
|
|
begin
|
|
if (TheTrees=nil)
|
|
or (aLFMBuf=nil) then
|
|
raise Exception.Create('TLFMTree.Create need tree and buffer');
|
|
Trees:=TheTrees;
|
|
Trees.FItems.Add(Self);
|
|
LFMBuffer:=aLFMBuf;
|
|
LFMBufferChangeStep:=LFMBuffer.ChangeStep;
|
|
if LFMBufferChangeStep=Low(LFMBufferChangeStep) then
|
|
LFMBufferChangeStep:=High(LFMBufferChangeStep)
|
|
else
|
|
dec(LFMBufferChangeStep);
|
|
end;
|
|
|
|
{ TLFMTreeNode }
|
|
|
|
constructor TLFMTreeNode.CreateVirtual;
|
|
begin
|
|
|
|
end;
|
|
|
|
destructor TLFMTreeNode.Destroy;
|
|
begin
|
|
while FirstChild<>nil do FirstChild.Free;
|
|
Unbind;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLFMTreeNode.Unbind;
|
|
begin
|
|
if Parent<>nil then begin
|
|
if Parent.FirstChild=Self then Parent.FirstChild:=NextSibling;
|
|
if Parent.LastChild=Self then Parent.LastChild:=PrevSibling;
|
|
Parent:=nil;
|
|
end;
|
|
if Tree<>nil then begin
|
|
if Tree.Root=Self then Tree.Root:=NextSibling;
|
|
Tree:=nil;
|
|
end;
|
|
if NextSibling<>nil then NextSibling.PrevSibling:=PrevSibling;
|
|
if PrevSibling<>nil then PrevSibling.NextSibling:=NextSibling;
|
|
NextSibling:=nil;
|
|
PrevSibling:=nil;
|
|
end;
|
|
|
|
procedure TLFMTreeNode.AddChild(ANode: TLFMTreeNode);
|
|
begin
|
|
if ANode=nil then exit;
|
|
ANode.Unbind;
|
|
ANode.Parent:=Self;
|
|
ANode.Tree:=Tree;
|
|
ANode.PrevSibling:=LastChild;
|
|
LastChild:=ANode;
|
|
if FirstChild=nil then FirstChild:=ANode;
|
|
if ANode.PrevSibling<>nil then
|
|
ANode.PrevSibling.NextSibling:=ANode;
|
|
end;
|
|
|
|
function TLFMTreeNode.GetIdentifier: string;
|
|
var
|
|
IdentStart, IdentEnd: integer;
|
|
begin
|
|
Result:='';
|
|
if (Tree=nil) or (Tree.LFMBuffer=nil) or (StartPos<1) then exit;
|
|
FindIdentifier(IdentStart,IdentEnd);
|
|
if IdentStart<1 then exit;
|
|
Result:=copy(Tree.LFMBuffer.Source,IdentStart,IdentEnd-IdentStart);
|
|
end;
|
|
|
|
procedure TLFMTreeNode.FindIdentifier(out IdentStart, IdentEnd: integer);
|
|
var
|
|
Src: String;
|
|
SrcLen: Integer;
|
|
begin
|
|
IdentStart:=-1;
|
|
IdentEnd:=-1;
|
|
if (Tree=nil) or (Tree.LFMBuffer=nil) or (StartPos<1) then exit;
|
|
Src:=Tree.LFMBuffer.Source;
|
|
SrcLen:=length(Src);
|
|
IdentStart:=StartPos;
|
|
while (IdentStart<=SrcLen) and (Src[IdentStart] in [#0..#32]) do
|
|
inc(IdentStart);
|
|
IdentEnd:=IdentStart;
|
|
while (IdentEnd<=SrcLen)
|
|
and (Src[IdentEnd] in ['A'..'Z','a'..'z','0'..'9','_','.']) do
|
|
inc(IdentEnd);
|
|
|
|
if TheType=lfmnObject then begin
|
|
// skip object/inherited/inline
|
|
IdentStart:=IdentEnd;
|
|
while (IdentStart<=SrcLen) and (Src[IdentStart] in [#0..#32]) do
|
|
inc(IdentStart);
|
|
IdentEnd:=IdentStart;
|
|
while (IdentEnd<=SrcLen)
|
|
and (Src[IdentEnd] in ['A'..'Z','a'..'z','0'..'9','_','.']) do
|
|
inc(IdentEnd);
|
|
end;
|
|
//debugln('TLFMTreeNode.FindIdentifier ',copy(Src,IdentStart,IdentEnd-IdentStart),' ',DbgStr(copy(Src,StartPos,20)));
|
|
|
|
if IdentEnd<=IdentStart then begin
|
|
IdentStart:=-1;
|
|
IdentEnd:=-1;
|
|
end;
|
|
end;
|
|
|
|
function TLFMTreeNode.GetPath: string;
|
|
var
|
|
ANode: TLFMTreeNode;
|
|
PrependStr: String;
|
|
begin
|
|
Result:='';
|
|
ANode:=Self;
|
|
while ANode<>nil do begin
|
|
PrependStr:=ANode.GetIdentifier;
|
|
{PrependStr:=PrependStr+'('+dbgs(ANode.StartPos)+','+dbgs(ANode.EndPos)+')';
|
|
if (ANode.Tree<>nil) then begin
|
|
if (ANode.Tree.LFMBuffer<>nil) then begin
|
|
PrependStr:=PrependStr+'"'+DbgStr(copy(ANode.Tree.LFMBuffer.Source,ANode.StartPos,20))+'"';
|
|
end else begin
|
|
PrependStr:=PrependStr+'noLFMBuf';
|
|
end;
|
|
end else begin
|
|
PrependStr:=PrependStr+'noTree';
|
|
end;}
|
|
if PrependStr<>'' then begin
|
|
if Result<>'' then begin
|
|
if ANode is TLFMObjectNode then
|
|
Result:='.'+Result
|
|
else
|
|
Result:='/'+Result;
|
|
end;
|
|
Result:=PrependStr+Result;
|
|
end;
|
|
ANode:=ANode.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TLFMTreeNode.Next(SkipChildren: Boolean = False): TLFMTreeNode;
|
|
begin
|
|
if not SkipChildren and (FirstChild <> nil) then
|
|
Result := FirstChild
|
|
else
|
|
begin
|
|
Result := Self;
|
|
while Result <> nil do
|
|
begin
|
|
if Result.NextSibling <> nil then
|
|
begin
|
|
Result := Result.NextSibling;
|
|
Exit;
|
|
end;
|
|
Result := Result.Parent;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TLFMObjectNode }
|
|
|
|
constructor TLFMObjectNode.CreateVirtual;
|
|
begin
|
|
TheType:=lfmnObject;
|
|
ChildPos:=-1;
|
|
end;
|
|
|
|
function TLFMObjectNode.GetFullName(UnitNameSep: char; WithName: boolean
|
|
): string;
|
|
begin
|
|
Result:=TypeUnitName;
|
|
if TypeName<>'' then begin
|
|
if Result<>'' then
|
|
Result:=Result+UnitNameSep+TypeName
|
|
else
|
|
Result:=TypeName;
|
|
end;
|
|
if (not WithName) or (Name='') then exit;
|
|
if Result<>'' then
|
|
Result:=Name+':'+Result
|
|
else
|
|
Result:=Name+':MissingLFMType';
|
|
end;
|
|
|
|
function TLFMObjectNode.GetIdentifier: string;
|
|
begin
|
|
Result:=GetFullName;
|
|
end;
|
|
|
|
{ TLFMPropertyNode }
|
|
|
|
constructor TLFMPropertyNode.CreateVirtual;
|
|
begin
|
|
TheType:=lfmnProperty;
|
|
end;
|
|
|
|
destructor TLFMPropertyNode.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLFMPropertyNode.Clear;
|
|
begin
|
|
CompleteName:='';
|
|
NameParts.Free;
|
|
NameParts:=nil;
|
|
end;
|
|
|
|
procedure TLFMPropertyNode.Add(const Name: string; NamePosition: integer);
|
|
begin
|
|
if NameParts=nil then NameParts:=TLFMNameParts.Create;
|
|
NameParts.Add(Name,NamePosition);
|
|
if CompleteName<>'' then
|
|
CompleteName:=CompleteName+'.'+Name
|
|
else
|
|
CompleteName:=Name;
|
|
end;
|
|
|
|
{ TLFMValueNode }
|
|
|
|
constructor TLFMValueNode.CreateVirtual;
|
|
begin
|
|
TheType:=lfmnValue;
|
|
ValueType:=lfmvNone;
|
|
end;
|
|
|
|
function TLFMValueNode.ReadString: string;
|
|
var
|
|
p: LongInt;
|
|
Src: String;
|
|
i: integer;
|
|
AtomStart: LongInt;
|
|
begin
|
|
Result:='';
|
|
if ValueType<>lfmvString then exit;
|
|
p:=StartPos;
|
|
AtomStart:=p;
|
|
Src:=Tree.LFMBuffer.Source;
|
|
repeat
|
|
ReadRawNextPascalAtom(Src,p,AtomStart);
|
|
if AtomStart>length(Src) then exit;
|
|
if Src[AtomStart]='''' then begin
|
|
Result:=Result+copy(Src,AtomStart+1,p-AtomStart-2)
|
|
end else if Src[AtomStart]='+' then begin
|
|
// skip
|
|
end else if Src[AtomStart]='#' then begin
|
|
i:=StrToIntDef(copy(Src,AtomStart+1,p-AtomStart-1),-1);
|
|
if (i<0) or (i>255) then exit;
|
|
Result:=Result+chr(i);
|
|
end else
|
|
exit;
|
|
until false;
|
|
end;
|
|
|
|
{ TLFMValueNodeSymbol }
|
|
|
|
constructor TLFMValueNodeSymbol.CreateVirtual;
|
|
begin
|
|
inherited CreateVirtual;
|
|
ValueType:=lfmvSymbol;
|
|
SymbolType:=lfmsIdentifier;
|
|
end;
|
|
|
|
{ TLFMValueNodeSet }
|
|
|
|
constructor TLFMValueNodeSet.CreateVirtual;
|
|
begin
|
|
inherited CreateVirtual;
|
|
ValueType:=lfmvSet;
|
|
end;
|
|
|
|
{ TLFMEnumNode }
|
|
|
|
constructor TLFMEnumNode.CreateVirtual;
|
|
begin
|
|
TheType:=lfmnEnum;
|
|
end;
|
|
|
|
{ TLFMValueNodeList }
|
|
|
|
constructor TLFMValueNodeList.CreateVirtual;
|
|
begin
|
|
inherited CreateVirtual;
|
|
ValueType:=lfmvList;
|
|
end;
|
|
|
|
{ TLFMValueNodeCollection }
|
|
|
|
constructor TLFMValueNodeCollection.CreateVirtual;
|
|
begin
|
|
inherited CreateVirtual;
|
|
ValueType:=lfmvCollection;
|
|
end;
|
|
|
|
{ TLFMValueNodeBinary }
|
|
|
|
constructor TLFMValueNodeBinary.CreateVirtual;
|
|
begin
|
|
inherited CreateVirtual;
|
|
ValueType:=lfmvBinary;
|
|
end;
|
|
|
|
{ TLFMError }
|
|
|
|
constructor TLFMError.Create;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
procedure TLFMError.Clear;
|
|
begin
|
|
ErrorType:=lfmeNoError;
|
|
Source:=nil;
|
|
end;
|
|
|
|
destructor TLFMError.Destroy;
|
|
begin
|
|
Unbind;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLFMError.AsString: string;
|
|
begin
|
|
Result:=LFMErrorTypeNames[ErrorType]+': '+ErrorMessage;
|
|
if Source<>nil then begin
|
|
Result:=Result+'. '+ExtractFileName(Source.Filename);
|
|
Result:=Result+' ('+IntToStr(Caret.Y)+','+IntToStr(Caret.X)+')';
|
|
end;
|
|
end;
|
|
|
|
procedure TLFMError.AddToTree(ATree: TLFMTree);
|
|
begin
|
|
if Tree=ATree then exit;
|
|
Unbind;
|
|
if ATree=nil then exit;
|
|
Tree:=ATree;
|
|
PrevError:=Tree.LastError;
|
|
Tree.LastError:=Self;
|
|
if PrevError<>nil then PrevError.NextError:=Self;
|
|
if Tree.FirstError=nil then Tree.FirstError:=Self;
|
|
end;
|
|
|
|
procedure TLFMError.Unbind;
|
|
begin
|
|
if Tree<>nil then begin
|
|
if Tree.FirstError=Self then Tree.FirstError:=NextError;
|
|
if Tree.LastError=Self then Tree.LastError:=PrevError;
|
|
Tree:=nil;
|
|
end;
|
|
if NextError<>nil then NextError.PrevError:=PrevError;
|
|
if PrevError<>nil then PrevError.NextError:=NextError;
|
|
PrevError:=nil;
|
|
NextError:=nil;
|
|
end;
|
|
|
|
function TLFMError.FindParentError: TLFMError;
|
|
var
|
|
CurNode: TLFMTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
if (Node=nil) or (Tree=nil) then exit;
|
|
CurNode:=Node.Parent;
|
|
while CurNode<>nil do begin
|
|
Result:=Tree.FindErrorAtNode(CurNode);
|
|
if Result<>nil then exit;
|
|
CurNode:=CurNode.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TLFMError.FindContextNode: TLFMTreeNode;
|
|
begin
|
|
Result:=Node;
|
|
while (Result<>nil)
|
|
and (not (Result.TheType in [lfmnProperty,lfmnObject])) do
|
|
Result:=Result.Parent;
|
|
end;
|
|
|
|
function TLFMError.IsMissingObjectType: boolean;
|
|
var
|
|
ObjNode: TLFMObjectNode;
|
|
begin
|
|
Result:=(ErrorType in [lfmeIdentifierNotFound,lfmeMissingRoot])
|
|
and (Node is TLFMObjectNode);
|
|
if not Result then exit;
|
|
ObjNode:=TLFMObjectNode(Node);
|
|
if ObjNode.TypeName='' then
|
|
exit(false);
|
|
if (Position>=ObjNode.TypeNamePosition)
|
|
and (Position<ObjNode.TypeNamePosition+length(ObjNode.TypeName)) then
|
|
exit(true);
|
|
if (ObjNode.TypeUnitName<>'')
|
|
and (Position>=ObjNode.TypeUnitNamePosition)
|
|
and (Position<ObjNode.TypeUnitNamePosition+length(ObjNode.TypeUnitName)) then
|
|
exit(true);
|
|
Result:=false;
|
|
end;
|
|
|
|
function TLFMError.GetNodePath: string;
|
|
begin
|
|
if Node<>nil then
|
|
Result:=Node.GetPath
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
{ TLFMNameParts }
|
|
|
|
function TLFMNameParts.GetNamePositions(Index: integer): integer;
|
|
begin
|
|
Result:=FNamePositions[Index];
|
|
end;
|
|
|
|
function TLFMNameParts.GetNames(Index: integer): string;
|
|
begin
|
|
Result:=FNames[Index];
|
|
end;
|
|
|
|
destructor TLFMNameParts.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLFMNameParts.Clear;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
ReAllocMem(FNamePositions,0);
|
|
for i:=0 to FCount-1 do FNames[i]:='';
|
|
ReAllocMem(FNames,0);
|
|
end;
|
|
|
|
procedure TLFMNameParts.Add(const Name: string; NamePosition: integer);
|
|
var
|
|
p: PPChar;
|
|
begin
|
|
inc(FCount);
|
|
ReAllocMem(FNamePositions,SizeOf(Integer)*FCount);
|
|
FNamePositions[FCount-1]:=NamePosition;
|
|
ReAllocMem(FNames,SizeOf(PChar)*FCount);
|
|
p:=PPChar(FNames);
|
|
p[FCount-1]:=nil;
|
|
FNames[FCount-1]:=Name;
|
|
end;
|
|
|
|
{ TLFMTrees }
|
|
|
|
constructor TLFMTrees.Create;
|
|
begin
|
|
FItems:=TAVLTree.Create(@CompareLFMTreesByLFMBuffer);
|
|
end;
|
|
|
|
destructor TLFMTrees.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FItems);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLFMTrees.Clear;
|
|
begin
|
|
FClearing:=true;
|
|
FItems.FreeAndClear;
|
|
FClearing:=false;
|
|
end;
|
|
|
|
function TLFMTrees.GetLFMTree(LFMBuffer: TCodeBuffer; CreateIfNotExists: boolean
|
|
): TLFMTree;
|
|
var
|
|
AVLNode: TAVLTreeNode;
|
|
begin
|
|
AVLNode:=FItems.FindKey(LFMBuffer,@CompareLFMBufWithTree);
|
|
if AVLNode<>nil then
|
|
Result:=TLFMTree(AVLNode.Data)
|
|
else if CreateIfNotExists then
|
|
Result:=TLFMTree.Create(Self,LFMBuffer)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
finalization
|
|
FreeAndNil(DefaultLFMTrees);
|
|
|
|
end.
|
|
|