You've already forked lazarus-ccr
aarre
applications
bindings
components
ZVDateTimeCtrls
acs
beepfp
chelper
cconvconfig.pas
cconvert.lpi
cconvert.lpr
chelper.lpk
chelper.pas
codewriter.pas
converteridesettings.pas
cparsertypes.pas
cparserutils.pas
ctopasconvert.pas
ctopasexp.pas
extconvdialog.lfm
extconvdialog.pas
objcparsing.pas
textparsingutils.pas
tosourceeditor.pas
cmdline
cmdlinecfg
colorpalette
csvdocument
epiktimer
fpsound
fpspreadsheet
freetypepascal
geckoport
gradcontrols
iosdesigner
iphonelazext
jujiboutils
jvcllaz
kcontrols
lazbarcodes
manualdock
mplayer
multithreadprocs
nvidia-widgets
onguard
orpheus
powerpdf
rgbgraphics
richmemo
richview
rtfview
rx
smnetgradient
spktoolbar
svn
tdi
thtmlport
tparadoxdataset
tvplanit
virtualtreeview
virtualtreeview-new
xdev_toolkit
zlibar
examples
lclbindings
wst
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1296 8e941d3f-bd1b-0410-a28a-d453659cc2b4
625 lines
15 KiB
ObjectPascal
625 lines
15 KiB
ObjectPascal
{ The unit is part of Lazarus Chelper package
|
|
|
|
Copyright (C) 2010 Dmitry Boyarintsev skalogryz dot lists at gmail.com
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program 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 Library General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
}
|
|
unit
|
|
objcparsing;
|
|
|
|
interface
|
|
|
|
{$ifdef fpc}{$mode delphi}{$h+}{$endif}
|
|
|
|
uses
|
|
Classes, SysUtils, cparsertypes;
|
|
|
|
const
|
|
objcend = '@end';
|
|
|
|
type
|
|
|
|
{ TObjCClasses }
|
|
|
|
TObjCClasses = class(TEntity)
|
|
ClassList : TStringList;
|
|
constructor Create(AOffset: Integer=-1); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TObjCMethod }
|
|
|
|
TObjCMethodArg = record
|
|
RetType : TEntity;
|
|
TypeName : TNamePart;
|
|
Name : AnsiString;
|
|
end;
|
|
|
|
TObjCMethodOpt = (mo_Required, mo_Optional);
|
|
|
|
TObjCMethod = class(TEntity)
|
|
public
|
|
isClassMethod : Boolean;
|
|
Name : TStringList;
|
|
RetType : TEntity;
|
|
RetName : TNamePart;
|
|
Args : array of TObjCMethodArg;
|
|
Option : TObjCMethodOpt;
|
|
VarParams : Boolean;
|
|
constructor Create(AOffset: Integer=-1); override;
|
|
destructor Destroy; override;
|
|
procedure AddArg(const ArgType: TEntity; ArgTypeName: TNamePart; const Name: AnsiString);
|
|
end;
|
|
|
|
TObjCScope = (os_Private, os_Protected, os_Public, os_Package);
|
|
|
|
{ TObjCInstVar }
|
|
|
|
TObjCInstVar = class(TEntity)
|
|
public
|
|
scope : TObjCScope;
|
|
v : TVarFuncEntity;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TObjCInterface }
|
|
|
|
TObjCInterface = class(TEntity)
|
|
public
|
|
Name : AnsiString;
|
|
SuperClass : AnsiString;
|
|
isCategory : Boolean;
|
|
Protocols : TStringList;
|
|
Vars : TList;
|
|
Methods : TList;
|
|
constructor Create(AOffset: Integer=-1); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TObjCProtocol }
|
|
|
|
TObjCProtocol = class(TEntity)
|
|
public
|
|
Names : TStringList;
|
|
isForward : Boolean;
|
|
Protocols : TStringList;
|
|
Methods : TList;
|
|
constructor Create(AOffset: Integer=-1); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TObjCProperty }
|
|
|
|
TObjCProperty = class(TEntity)
|
|
public
|
|
Name : TNamePart;
|
|
RetType : TEntity;
|
|
SetterName : AnsiString;
|
|
GetterName : AnsiString;
|
|
Props : TStringList;
|
|
constructor Create(AOffset: Integer=-1); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
function ParseClassList(AParser: TTextParser): TObjCClasses;
|
|
function ParseInterface(AParser: TTextParser): TObjCInterface;
|
|
function ParseMethod(AParser: TTextParser): TObjCMethod;
|
|
function ParseProperty(AParser: TTextParser): TObjCProperty;
|
|
function ParseMethods(AParser: TTextParser; MethodsList: TList; const EndToken: AnsiString = objcend): Boolean;
|
|
function ParseProtocol(AParser: TTextParser): TEntity;
|
|
|
|
function ParseNextObjCEntity(AParser: TTextParser): TEntity;
|
|
|
|
function isObjCKeyword(const token: AnsiString): Boolean; inline;
|
|
function GetObjCKeyword(const token: AnsiString): AnsiString;
|
|
|
|
const
|
|
nk_Protocol = $1000;
|
|
|
|
implementation
|
|
|
|
function isObjCKeyword(const token: AnsiString): Boolean; inline;
|
|
begin
|
|
Result:=(token<>'') and (token[1]='@');
|
|
end;
|
|
|
|
function GetObjCKeyword(const token: AnsiString): AnsiString;
|
|
begin
|
|
if isObjCKeyword(token) then Result:=Copy(token, 2, length(token)-1)
|
|
else Result:=token;
|
|
end;
|
|
|
|
function ParseClassList(AParser: TTextParser): TObjCClasses;
|
|
var
|
|
cl : TObjCClasses;
|
|
begin
|
|
Result:=nil;
|
|
if AParser.Token<>'@class' then Exit;
|
|
try
|
|
cl:=TObjCClasses.Create(AParser.TokenPos);
|
|
AParser.NextToken;
|
|
while AParser.Token<>';' do begin
|
|
if AParser.TokenType<>tt_Ident then begin
|
|
ErrorExpect(AParser,'identifier');
|
|
cl.Free;
|
|
Exit;
|
|
end;
|
|
cl.ClassList.Add(AParser.Token);
|
|
AParser.NextToken;
|
|
if AParser.Token=',' then
|
|
AParser.NextToken
|
|
else if AParser.Token<>';' then begin
|
|
ErrorExpect(AParser,';');
|
|
cl.Free;
|
|
Exit;
|
|
end;
|
|
end;
|
|
if AParser.Token<>';' then ErrorExpect(AParser, ';');
|
|
Result:=cl;
|
|
finally
|
|
if not Assigned(Result) then cl.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParseInstVars(AParser: TTextParser; Vars: TList): Boolean;
|
|
var
|
|
v : TVarFuncEntity;
|
|
iv : TObjCInstVar;
|
|
s : AnsiString;
|
|
scope : TObjCScope;
|
|
begin
|
|
Result:=True;
|
|
if AParser.Token<>'{' then Exit;
|
|
|
|
Result:=False;
|
|
AParser.NextToken;
|
|
|
|
scope:=os_Protected;
|
|
while AParser.Token<>'}' do begin
|
|
if isObjCKeyword(AParser.Token) then begin
|
|
s:=GetObjCKeyword(APArser.Token);
|
|
if s='protected' then scope:=os_Protected
|
|
else if s='private' then scope:=os_Private
|
|
else if s='public' then scope:=os_Public
|
|
else if s='package' then scope:=os_Package
|
|
else begin
|
|
ErrorExpect(AParser,'}');
|
|
Exit;
|
|
end;
|
|
AParser.NextToken;
|
|
end else begin
|
|
v:=TVarFuncEntity.Create(AParser.TokenPos);
|
|
if not ParseNames(AParser, v.RetType, v.Names, [';']) then Exit;
|
|
iv:=TObjCInstVar.Create(v.Offset);
|
|
iv.v:=v;
|
|
iv.scope:=scope;
|
|
Vars.Add(iv);
|
|
if AParser.Token=';' then AParser.NextToken;
|
|
end;
|
|
end;
|
|
AParser.NextToken;
|
|
|
|
Result:=True;
|
|
end;
|
|
|
|
function ParseInterface(AParser: TTextParser): TObjCInterface;
|
|
var
|
|
itf : TObjCInterface;
|
|
i : Integer;
|
|
nm : AnsiString;
|
|
begin
|
|
Result:=nil;
|
|
if AParser.Token<>'@interface' then Exit;
|
|
i:=AParser.TokenPos;
|
|
AParser.NextToken;
|
|
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
|
|
itf:=TObjCInterface.Create(i);
|
|
try
|
|
itf.isCategory:=AParser.Token='(';
|
|
if itf.isCategory then begin
|
|
itf.SuperClass:=nm;
|
|
AParser.NextToken;
|
|
if not (ConsumeIdentifier(AParser, itf.Name) and ConsumeToken(AParser, ')')) then
|
|
Exit;
|
|
end else begin
|
|
itf.Name:=nm;
|
|
// super-class
|
|
if AParser.Token=':' then begin
|
|
AParser.NextToken;
|
|
if not ConsumeIdentifier(AParser, itf.SuperClass) then Exit;
|
|
end;
|
|
|
|
// protocols
|
|
if AParser.Token='<' then begin
|
|
AParser.NextToken;
|
|
while AParser.Token<>'>' do begin
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
itf.Protocols.Add(nm);
|
|
if AParser.Token=',' then AParser.NextToken
|
|
else if AParser.Token<>'>' then begin
|
|
ErrorExpect(AParser, '>');
|
|
Exit;
|
|
end;
|
|
end;
|
|
AParser.NextToken;
|
|
end;
|
|
|
|
ParseInstVars(AParser, itf.Vars);
|
|
end;
|
|
|
|
if not ParseMethods(AParser, itf.Methods, objcend) then Exit;
|
|
if AParser.Token<>objcend then ErrorExpect(AParser, objcend);
|
|
|
|
Result:=itf;
|
|
finally
|
|
if not Assigned(Result) then itf.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParseProtocol(AParser: TTextParser): TEntity;
|
|
var
|
|
p : TObjCProtocol;
|
|
nm : AnsiString;
|
|
begin
|
|
Result:=nil;
|
|
if AParser.Token<>'@protocol' then Exit;
|
|
p := TObjCProtocol.Create(AParser.TokenPos);
|
|
try
|
|
AParser.NextToken;
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
p.Names.Add(nm);
|
|
p.isForward:= (AParser.Token=';') or (AParser.Token=',');
|
|
if p.isForward then begin
|
|
while AParser.Token<>';' do begin
|
|
AParser.NextToken;
|
|
ConsumeIdentifier(AParser, nm);
|
|
p.Names.Add(nm);
|
|
end;
|
|
Result:=p;
|
|
if AParser.Token<>';' then ErrorExpect(AParser, ';');
|
|
Exit;
|
|
end;
|
|
|
|
if AParser.Token='<' then begin
|
|
AParser.NextToken;
|
|
while AParser.Token<>'>' do begin
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
p.Protocols.Add(nm);
|
|
if AParser.Token=',' then AParser.NextToken;
|
|
end;
|
|
if AParser.Token='>' then AParser.NextToken;
|
|
end;
|
|
|
|
if ParseMethods(AParser, p.Methods, objcend) then
|
|
Result:=p;
|
|
if AParser.Token<>objcend then ErrorExpect(AParser, objcend);
|
|
finally
|
|
if not Assigned(Result) then p.Free;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
PrevParseNextEntity : function (AParser: TTextParser): TEntity = nil;
|
|
PrevNamePart : function (AParser: TTextParser): TNamePart = nil;
|
|
|
|
function ParseNextObjCEntity(AParser: TTextParser): TEntity;
|
|
var
|
|
t : AnsiString;
|
|
begin
|
|
Result:=nil;
|
|
if (AParser.Token<>'') and (AParser.Token[1]='@') then begin
|
|
t:=GetObjCKeyword(AParser.Token);
|
|
if t='class' then Result:=ParseClassList(AParser)
|
|
else if t='interface' then Result:=ParseInterface(AParser)
|
|
else if t='protocol' then Result:=ParseProtocol(AParser);
|
|
end else begin
|
|
if Assigned(PrevParseNextEntity) then
|
|
Result:=PrevParseNextEntity(AParser)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
end;
|
|
|
|
{ TObjCClasses }
|
|
|
|
constructor TObjCClasses.Create(AOffset:Integer);
|
|
begin
|
|
inherited Create(AOffset);
|
|
ClassList := TStringList.Create;
|
|
end;
|
|
|
|
destructor TObjCClasses.Destroy;
|
|
begin
|
|
ClassList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TObjCInterface }
|
|
|
|
constructor TObjCInterface.Create(AOffset:Integer);
|
|
begin
|
|
Vars := TList.Create;
|
|
Methods := TList.Create;
|
|
Protocols := TStringList.Create;
|
|
inherited Create(AOffset);
|
|
end;
|
|
|
|
destructor TObjCInterface.Destroy;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i:=0 to Vars.Count-1 do TObject(Vars[i]).Free;
|
|
Vars.Free;
|
|
for i:=0 to Methods.Count-1 do TObject(Methods[i]).Free;
|
|
Methods.Free;
|
|
Protocols.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function ParseMethod(AParser: TTextParser): TObjCMethod;
|
|
var
|
|
m : TObjCMethod;
|
|
nm : AnsiString;
|
|
atype : TEntity;
|
|
atname : TNamePart;
|
|
aname : Ansistring;
|
|
prm : Boolean;
|
|
begin
|
|
Result:=nil;
|
|
if (AParser.Token<>'+') and (AParser.Token<>'-') then Exit;
|
|
|
|
m:=TObjCMethod.Create(AParser.TokenPos);
|
|
try
|
|
m.isClassMethod:=AParser.Token='+';
|
|
AParser.NextToken;
|
|
|
|
if AParser.Token='(' then begin
|
|
AParser.NextToken;
|
|
if not ParseName(AParser, m.RetType, m.RetName,[')']) then Exit;
|
|
if not ConsumeToken(AParser, ')') then Exit;
|
|
end;
|
|
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
|
|
if (AParser.Token=':') then begin
|
|
m.Name.Add(nm+':');
|
|
AParser.NextToken;
|
|
|
|
while (AParser.Token<>';') and (AParser.Token<>',') do begin
|
|
if AParser.Token='(' then begin
|
|
prm:=ConsumeToken(AParser, '(') and
|
|
ParseName(APArser, atype, atname,[')']) and
|
|
ConsumeToken(AParser, ')');
|
|
end else begin
|
|
prm:=True;
|
|
atype:=nil;
|
|
atname:=nil;
|
|
end;
|
|
if not prm then Exit;
|
|
ConsumeIdentifier(AParser, aname);
|
|
m.AddArg(atype, atname, aname);
|
|
|
|
// the next name starts
|
|
if AParser.TokenType=tt_Ident then ConsumeIdentifier(AParser, nm) else nm:='';
|
|
if (AParser.Token<>';') and (AParser.Token<>',') then begin
|
|
if not ConsumeToken(AParser,':') then Exit;
|
|
m.Name.Add(nm+':');
|
|
end;
|
|
end;
|
|
end else
|
|
m.Name.Add(nm);
|
|
|
|
if AParser.Token=',' then begin
|
|
AParser.NextToken;
|
|
if ConsumeToken(AParser,'...') then m.VarParams:=True
|
|
else ErrorExpect(AParser, '...');
|
|
end;
|
|
if not ConsumeToken(AParser, ';') then Exit;
|
|
|
|
Result:=m;
|
|
finally
|
|
if not Assigned(Result) then m.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParseMethods(AParser: TTextParser; MethodsList: TList; const EndToken: AnsiString): Boolean;
|
|
var
|
|
m : TObjCMethod;
|
|
p : TObjCProperty;
|
|
opt : TObjCMethodOpt;
|
|
s : AnsiString;
|
|
begin
|
|
Result:=False;
|
|
if not Assigned(MethodsList) or not Assigned(AParser) then Exit;
|
|
opt:=mo_Required;
|
|
while (AParser.Token<>EndToken) and (AParser.Token<>'') and (AParser.Token[1] in ['+','-','@']) do begin
|
|
if isObjCKeyword(AParser.Token) then begin
|
|
s:=GetObjCKeyword(AParser.Token);
|
|
if s='property' then begin
|
|
p:=ParseProperty(AParser);
|
|
MethodsList.Add(p);
|
|
end else begin
|
|
if s='optional' then opt:=mo_Optional
|
|
else opt:=mo_Required;
|
|
AParser.NextToken;
|
|
end;
|
|
end else begin
|
|
m:=ParseMethod(AParser);
|
|
if not Assigned(m) then Exit;
|
|
m.Option:=opt;
|
|
MethodsList.Add(m);
|
|
end;
|
|
end;
|
|
Result:=True;
|
|
end;
|
|
|
|
{ TObjCMethod }
|
|
|
|
constructor TObjCMethod.Create(AOffset:Integer);
|
|
begin
|
|
inherited Create(AOffset);
|
|
Name := TStringList.Create;
|
|
end;
|
|
|
|
destructor TObjCMethod.Destroy;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Name.Free;
|
|
RetType.Free;
|
|
RetName.Free;
|
|
for i:=0 to length(Args)-1 do begin
|
|
Args[i].RetType.Free;
|
|
Args[i].TypeName.Free;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TObjCMethod.AddArg(const ArgType:TEntity;ArgTypeName:TNamePart;const Name:AnsiString);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
i:=length(Args);
|
|
SetLength(Args, i+1);
|
|
Args[i].Name:=Name;
|
|
Args[i].RetType:=ArgType;
|
|
Args[i].TypeName:=ArgTypeName;
|
|
end;
|
|
|
|
{ TObjCInstVar }
|
|
|
|
|
|
destructor TObjCInstVar.Destroy;
|
|
begin
|
|
v.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TObjCProtocol }
|
|
|
|
constructor TObjCProtocol.Create(AOffset:Integer);
|
|
begin
|
|
inherited Create(AOffset);
|
|
Protocols := TStringList.Create;
|
|
Methods := TList.Create;
|
|
Names := TStringList.Create;
|
|
end;
|
|
|
|
destructor TObjCProtocol.Destroy;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i:=0 to Methods.Count-1 do TObject(Methods[i]).Free;
|
|
Methods.Free;
|
|
Protocols.Free;
|
|
Names.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TObjCProperty }
|
|
|
|
constructor TObjCProperty.Create(AOffset:Integer);
|
|
begin
|
|
inherited Create(AOffset);
|
|
Props:=TStringList.Create;
|
|
end;
|
|
|
|
destructor TObjCProperty.Destroy;
|
|
begin
|
|
RetType.Free;
|
|
Name.Free;
|
|
Props.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function ParseProperty(AParser: TTextParser): TObjCProperty;
|
|
var
|
|
p : TObjCProperty;
|
|
s : AnsiString;
|
|
nm : AnsiString;
|
|
begin
|
|
Result:=nil;
|
|
if AParser.Token<>'@property' then Exit;
|
|
AParser.NextToken;
|
|
p := TObjCProperty.Create(AParser.TokenPos);
|
|
try
|
|
if AParser.Token='(' then begin
|
|
AParser.NextToken;
|
|
while AParser.Token<>')' do begin
|
|
s:=AParser.Token;
|
|
if (s='setter') or (s='getter') then begin
|
|
AParser.NextToken;
|
|
if not ConsumeToken(AParser, '=') then Exit;
|
|
if not ConsumeIdentifier(AParser, nm) then Exit;
|
|
while (AParser.TokenType=tt_Ident) or (APArser.Token=':') do begin
|
|
nm:=nm+AParser.Token;
|
|
AParser.NextToken;
|
|
end;
|
|
if s='setter' then p.SetterName:=nm
|
|
else p.GetterName:=nm;
|
|
end else begin
|
|
if APArser.TokenType=tt_Ident then p.Props.Add(AParser.Token);
|
|
AParser.NextToken;
|
|
end;
|
|
if AParser.Token=',' then AParser.NextToken;
|
|
end;
|
|
if AParser.Token=')' then
|
|
AParser.NextToken
|
|
else begin
|
|
ErrorExpect(AParser,')');
|
|
Exit;
|
|
end;
|
|
if ParseName(AParser, p.RetType, p.Name,[';']) then begin
|
|
Result:=p;
|
|
if AParser.Token=';' then AParser.NextToken;
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
|
if not Assigned(Result) then p.Free;
|
|
end;
|
|
end;
|
|
|
|
function ParseObjCNamePart(AParser: TTextParser): TNamePart;
|
|
var
|
|
p : AnsiString;
|
|
begin
|
|
// skipping protocol adopted type definition
|
|
if AParser.Token='<' then begin
|
|
Result:=nil;
|
|
AParser.NextToken;
|
|
repeat
|
|
if not ConsumeIdentifier(AParser, p) then Exit;
|
|
if AParser.Token=',' then AParser.NextToken;
|
|
until AParser.Token='>';
|
|
|
|
if not ConsumeToken(AParser,'>') then Exit;
|
|
end;
|
|
Result:=PrevNamePart(AParser);
|
|
end;
|
|
|
|
initialization
|
|
PrevParseNextEntity:=ParseNextEntity;
|
|
ParseNextEntity:=ParseNextObjCEntity;
|
|
|
|
PrevNamePart:=ParseNamePart;
|
|
ParseNamePart:=ParseObjCNamePart;
|
|
|
|
end.
|
|
|