chelper: started objc syntax support

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1283 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
skalogryz
2010-08-14 20:17:44 +00:00
parent 2c90d92b2e
commit 2a5f151adc
5 changed files with 507 additions and 12 deletions

View File

@ -31,12 +31,17 @@
<LaunchingApplication PathPlusParams="/usr/X11/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/> <LaunchingApplication PathPlusParams="/usr/X11/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local> </local>
</RunParams> </RunParams>
<Units Count="1"> <Units Count="2">
<Unit0> <Unit0>
<Filename Value="cconvert.lpr"/> <Filename Value="cconvert.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="cconvert"/> <UnitName Value="cconvert"/>
</Unit0> </Unit0>
<Unit1>
<Filename Value="objctopasconvert.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="objctopasconvert"/>
</Unit1>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

@ -22,7 +22,8 @@ program cconvert;
uses uses
SysUtils,Classes, SysUtils,Classes,
ctopasconvert,cparserutils,cconvconfig; ctopasconvert,cparsertypes,cparserutils,cconvconfig, objcparsing,
objctopasconvert;
var var
ConfigFile : AnsiString = ''; ConfigFile : AnsiString = '';

View File

@ -20,12 +20,7 @@ unit cparsertypes;
interface interface
{$ifdef fpc}{$mode delphi}{$h+} {$ifdef fpc}{$mode delphi}{$h+}{$endif}
{$else}
{$warn unsafe_code off}
{$warn unsafe_type off}
{$warn unsafe_cast off}
{$endif}
uses uses
Classes, SysUtils, TextParsingUtils; Classes, SysUtils, TextParsingUtils;
@ -232,7 +227,11 @@ type
end; end;
// parsing function // parsing function
function ParseNextEntity(AParser: TTextParser): TEntity; var
ParseNextEntity: function (AParser: TTextParser): TEntity = nil;
function ParseNextCEntity(AParser: TTextParser): TEntity;
function ParseCExpression(AParser: TTextParser; var ExpS: AnsiString): Boolean; function ParseCExpression(AParser: TTextParser; var ExpS: AnsiString): Boolean;
procedure ParseCNumeric(const S: AnsiString; var idx: integer; var NumStr: AnsiSTring); procedure ParseCNumeric(const S: AnsiString; var idx: integer; var NumStr: AnsiSTring);
function ParseCString(const S: AnsiString; var idx: Integer; var CStr: AnsiString): Boolean; function ParseCString(const S: AnsiString; var idx: Integer; var CStr: AnsiString): Boolean;
@ -280,6 +279,7 @@ type
procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString); procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString);
function ConsumeToken(Parser: TTextParser; const Token: AnsiString): Boolean; function ConsumeToken(Parser: TTextParser; const Token: AnsiString): Boolean;
function ConsumeIdentifier(Parser: TTextParser; var Id: AnsiString): Boolean;
function ParseCType(Parser: TTextParser): TEntity; function ParseCType(Parser: TTextParser): TEntity;
@ -1455,7 +1455,7 @@ begin
end; end;
function ParseNextEntity(AParser: TTextParser): TEntity; function ParseNextCEntity(AParser: TTextParser): TEntity;
var var
s : AnsiString; s : AnsiString;
tt : TTokenType; tt : TTokenType;
@ -1465,7 +1465,7 @@ var
v : TVarFuncEntity; v : TVarFuncEntity;
begin begin
Result := nil; Result := nil;
if not AParser.FindNextToken(s, tt) then Exit; s:=AParser.Token;
if s = 'typedef' then begin if s = 'typedef' then begin
Result:=ParseTypeDef(AParser); Result:=ParseTypeDef(AParser);
@ -1507,6 +1507,16 @@ begin
else Parser.SetError('Token expected: '+Token); else Parser.SetError('Token expected: '+Token);
end; end;
function ConsumeIdentifier(Parser: TTextParser; var Id: AnsiString): Boolean;
begin
Result:=Parser.TokenType=tt_Ident;
if Result then begin
id:=Parser.Token;
Parser.NextToken;
end else
Parser.SetError('Identifier expected');
end;
function ParseCType(Parser: TTextParser): TEntity; function ParseCType(Parser: TTextParser): TEntity;
var var
simple : TSimpleType; simple : TSimpleType;
@ -2011,4 +2021,8 @@ begin
inherited Destroy; inherited Destroy;
end; end;
initialization
ParseNextEntity:=@ParseNextCEntity;
end. end.

View File

@ -18,13 +18,15 @@
} }
unit ctopasconvert; unit ctopasconvert;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
interface interface
uses uses
Classes, SysUtils, Classes, SysUtils,
cparsertypes, TextParsingUtils, codewriter, cparserutils; cparsertypes, TextParsingUtils, codewriter, cparserutils,
objcparsing;
type type
@ -140,6 +142,10 @@ type
procedure WriteExp(x: TExpression); procedure WriteExp(x: TExpression);
procedure WritePreprocessor(cent: TCPrepDefine); procedure WritePreprocessor(cent: TCPrepDefine);
function GetObjCMethodName(names: TStrings): AnsiString;
procedure WriteObjCMethod(m: TObjCMethod);
procedure WriteObjCInterface(cent: TObjCInterface);
procedure PushWriter; procedure PushWriter;
procedure PopWriter; procedure PopWriter;
public public
@ -279,6 +285,8 @@ begin
AParser.OnPrecompile:=@cmt.OnPrecompiler; AParser.OnPrecompile:=@cmt.OnPrecompiler;
Result:=nil; Result:=nil;
AParser.NextToken;
ent := ParseNextEntity(AParser); ent := ParseNextEntity(AParser);
entidx:=AParser.Index; entidx:=AParser.Index;
@ -582,6 +590,75 @@ begin
end; end;
end; end;
function TCodeConvertor.GetObjCMethodName(names:TStrings):AnsiString;
var
i : Integer;
begin
Result:='';
for i:=0 to names.Count-1 do Result:=Result+names[i];
for i:=1 to length(Result) do if Result[i]=':' then Result[i]:='_';
end;
procedure TCodeConvertor.WriteObjCMethod(m: TObjCMethod);
var
ret : AnsiString;
i : INteger;
begin
if m.RetType=nil then ret:='id' else ret:=GetPasTypeName(m.RetType, m.RetName);
if ret='' then wr.W('procedure ')
else wr.W('function ');
wr.W( GetObjCMethodName(m.Name) );
if length(m.Args)>0 then begin
wr.W('(');
for i:=0 to length(m.Args)-1 do begin
if m.Args[i].Name='' then wr.W(cfg.ParamPrefix+IntToStr(i))
else wr.W(m.Args[i].Name);
wr.W(': ');
wr.W(GetPasTypeName(m.Args[i].RetType, m.Args[i].TypeName));
if i<length(m.Args)-1 then wr.W('; ');
end;
wr.W(')');
end;
if ret<>'' then wr.W(': '+ ret);
wr.W(';');
wr.W(' message ''');
for i:=0 to m.Name.Count-1 do wr.W(m.Name[i]);
wr.Wln(''';');
end;
procedure TCodeConvertor.WriteObjCInterface(cent:TObjCInterface);
var
i : Integer;
m : TObjCMethod;
begin
SetPasSection(wr, 'type');
if cent.isCategory then begin
wr.W(cent.Name + ' = objccategory')
end else begin
wr.W(cent.Name + ' = objcclass');
if cent.SuperClass<>'' then wr.W('('+cent.SuperClass);
if cent.Protocols.Count>0 then begin
if cent.SuperClass='' then wr.W('(id, ')
else wr.W(', ');
for i:=0 to cent.Protocols.Count-2 do wr.W(cent.Protocols[i]+', ');
wr.W(cent.Protocols[cent.Protocols.Count-1]);
end;
if (cent.SuperClass<>'') or (cent.Protocols.Count>0) then wr.Wln(')');
end;
wr.Wln('public');
wr.IncIdent;
for i:=0 to cent.Methods.Count-1 do begin
m:=TObjCMethod(cent.Methods[i]);
WriteObjCMethod(m)
end;
wr.DecIdent;
wr.Wln('end;')
end;
procedure TCodeConvertor.PushWriter; procedure TCodeConvertor.PushWriter;
begin begin
if not Assigned(fWriters) then fWriters:=TList.Create; if not Assigned(fWriters) then fWriters:=TList.Create;
@ -900,6 +977,8 @@ begin
WriteCommentToPas(cent as TComment) WriteCommentToPas(cent as TComment)
else if cent is TCPrepDefine then else if cent is TCPrepDefine then
WritePreprocessor(cent as TCPrepDefine) WritePreprocessor(cent as TCPrepDefine)
else if cent is TObjCInterface then
WriteObjCInterface(cent as TObjCInterface)
else begin else begin
if DebugEntities then if DebugEntities then
wr.Wln(cent.ClassName); wr.Wln(cent.ClassName);

View File

@ -0,0 +1,396 @@
unit
objcparsing;
interface
{$ifdef fpc}{$mode delphi}{$h+}{$endif}
uses
Classes, SysUtils, cparsertypes;
type
{ TObjCClasses }
TObjCClasses = class(TEntity)
Classes : TStringList;
constructor Create(AOffset: Integer=-1); override;
destructor Destroy; override;
end;
{ TObjCMethod }
TObjCMethodArg = record
RetType : TEntity;
TypeName : TNamePart;
Name : AnsiString;
end;
TObjCMethod = class(TEntity)
public
isClassMethod : Boolean;
Name : TStringList;
RetType : TEntity;
RetName : TNamePart;
Args : array of TObjCMethodArg;
constructor Create(AOffset: Integer=-1); override;
destructor Destroy; override;
procedure AddArg(const ArgType: TEntity; ArgTypeName: TNamePart; const Name: AnsiString);
end;
{ TObjCInterface }
TObjCInterface = class(TEntity)
public
Name : AnsiString;
SuperClass : AnsiString;
isCategory : Boolean;
Protocols : TStringList;
ProtVars : TList;
PrivVars : TList;
PubVars : TList;
PackVars : TList;
Methods : TList;
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 ParseMethods(AParser: TTextParser; MethodsList: TList; const EndToken: AnsiString): Boolean;
function ParserProtocol(AParser: TTextParser): TEntity;
function ParseNextObjCEntity(AParser: TTextParser): TEntity;
function isObjCKeyword(const token: AnsiString): Boolean; inline;
function GetObjCKeyword(const token: AnsiString): AnsiString;
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;
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.Classes.Add(AParser.Token);
AParser.NextToken;
if AParser.Token=',' then
AParser.NextToken
else if AParser.Token<>';' then begin
ErrorExpect(AParser,';');
cl.Free;
Exit;
end;
end;
Result:=cl;
end;
function ParseInstVars(AParser: TTextParser; itf: TObjCInterface): Boolean;
var
vars : TList;
v : TVarFuncEntity;
s : AnsiString;
begin
Result:=True;
if AParser.Token<>'{' then Exit;
Result:=False;
AParser.NextToken;
vars:=itf.ProtVars;
while AParser.Token<>'}' do begin
if isObjCKeyword(AParser.Token) then begin
s:=GetObjCKeyword(APArser.Token);
if s='protected' then vars:=itf.ProtVars
else if s='private' then vars:=itf.PrivVars
else if s='public' then vars:=itf.PubVars
else if s='package' then vars:=itf.PackVars
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;
vars.Add(v);
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.Name:=nm;
itf.isCategory:=AParser.Token='(';
if itf.isCategory then begin
AParser.NextToken;
if not ConsumeIdentifier(AParser, itf.SuperClass) and ConsumeToken(AParser, ')') then
Exit;
end else begin
// super-class
if AParser.Token=':' then begin
AParser.NextToken;
if not ConsumeIdentifier(AParser, itf.SuperClass) then Exit;
//writeln('SuperClass = ', itf.SuperClass);
end;
// protocols
if AParser.Token='<' then begin
AParser.NextToken;
while AParser.Token<>'>' do begin
if not ConsumeIdentifier(AParser, nm) then Exit;
//writeln('Protos = ', nm);
itf.Protocols.Add(nm);
if AParser.Token=',' then AParser.NextToken
else if AParser.Token<>'>' then begin
ErrorExpect(AParser, '>');
Exit;
end;
end;
AParser.NextToken;
end;
//writeln('parsing vars1 ', AParser.Token);
ParseInstVars(AParser, itf);
//writeln('parsing vars2 ', AParser.Token);
end;
//writeln('parsing methods1 ', AParser.Token);
if not ParseMethods(AParser, itf.Methods, '@end') then Exit;
//writeln('parsing methods2 ', AParser.Token);
if AParser.Token='@end' then AParser.NextToken;
Result:=itf;
finally
if not Assigned(Result) then itf.Free;
end;
end;
function ParserProtocol(AParser: TTextParser): TEntity;
begin
Result:=nil;
end;
var
PrevParseNextEntity : function (AParser: TTextParser): TEntity = nil;
function ParseNextObjCEntity(AParser: TTextParser): TEntity;
var
t : AnsiString;
begin
if 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:=ParserProtocol(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);
Classes := TStringList.Create;
end;
destructor TObjCClasses.Destroy;
begin
Classes.Free;
inherited Destroy;
end;
{ TObjCInterface }
constructor TObjCInterface.Create(AOffset:Integer);
begin
ProtVars := TList.Create;
PrivVars := TList.Create;
PubVars := TList.Create;
PackVars := TList.Create;
Methods := TList.Create;
Protocols := TStringList.Create;
inherited Create(AOffset);
end;
destructor TObjCInterface.Destroy;
var
i : Integer;
begin
for i:=0 to ProtVars.Count-1 do TObject(ProtVars[i]).Free;
for i:=0 to PrivVars.Count-1 do TObject(PrivVars[i]).Free;
for i:=0 to PubVars.Count-1 do TObject(PubVars[i]).Free;
for i:=0 to PackVars.Count-1 do TObject(PubVars[i]).Free;
PrivVars.Free;
PubVars.Free;
ProtVars.Free;
PackVars.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;
//writeln('in method: ', AParser.Token);
m:=TObjCMethod.Create(AParser.TokenPos);
try
AParser.NextToken;
//writeln('in method2: ', AParser.Token);
if AParser.Token='(' then begin
AParser.NextToken;
if not ParseName(AParser, m.RetType, m.RetName) then Exit;
if not ConsumeToken(AParser, ')') then Exit;
end;
//writeln('in method3: ', AParser.Token);
if not ConsumeIdentifier(AParser, nm) then Exit;
//writeln('in method3: ', AParser.Token);
if (AParser.Token=':') then begin
m.Name.Add(nm+':');
AParser.NextToken;
//writeln('in method4: ', AParser.Token);
while AParser.Token<>';' do begin
prm:=ConsumeToken(AParser, '(') and
ParseName(APArser, atype, atname) and
ConsumeToken(AParser, ')') and
ConsumeIdentifier(AParser, aname);
if not prm then Exit;
m.AddArg(atype, atname, aname);
if AParser.TokenType=tt_Ident then ConsumeIdentifier(AParser, nm) else nm:='';
if AParser.Token<>';' then begin
if not ConsumeToken(AParser,':') then Exit;
m.Name.Add(nm+':');
end;
end;
AParser.NextToken;
end else begin
m.Name.Add(nm);
if not ConsumeToken(AParser, ';') then Exit;
end;
//writeln('in method5: ', AParser.Token);
Result:=m;
finally
if not Assigned(Result) then m.Free;
end;
end;
function ParseMethods(AParser: TTextParser; MethodsList: TList; const EndToken: AnsiString = '@end'): Boolean;
var
m : TObjCMethod;
begin
Result:=False;
if not Assigned(MethodsList) or not Assigned(AParser) then Exit;
while (AParser.Token<>EndToken) and (AParser.Token<>'') and (AParser.Token[1] in ['+','-']) do begin
//writeln('AParser.Token = ', AParser.Token);
m:=ParseMethod(AParser);
//writeln('m = ', Integer(m));
if not Assigned(m) then Exit;
MethodsList.Add(m);
end;
Result:=True;
end;
{ TObjCMethod }
constructor TObjCMethod.Create(AOffset:Integer);
begin
inherited Create(AOffset);
Name := TStringList.Create;
RetType := TVarFuncEntity.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;
initialization
PrevParseNextEntity:=ParseNextEntity;
ParseNextEntity:=ParseNextObjCEntity;
end.