chelper: *fix enum type converting, if used as function return type* improve expression converting* improve enumeration value assignment* implement writting unions* fixes type name and variable name selection (to avoid pascal names conflicts)* fix function converting with single void* parameter

* c-parsing fixes* IDE lock for multiple ctrl+b presses



git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1272 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
skalogryz
2010-08-09 20:29:26 +00:00
parent 2f5829eff6
commit ab0048814a
4 changed files with 196 additions and 65 deletions

View File

@ -80,6 +80,7 @@ var
p : TPoint; p : TPoint;
cfg : TConvertSettings; cfg : TConvertSettings;
begin begin
if ParamCount=0 then Exit;
inps := TStringList.Create; inps := TStringList.Create;
outs := TStringList.Create; outs := TStringList.Create;

View File

@ -266,10 +266,16 @@ type
{ TExpression } { TExpression }
TExpPart = record
Token : AnsiString;
TokenType : TTokenType;
end;
TExpression = class(TEntity) TExpression = class(TEntity)
function DoParse(AParser: TTextParser): Boolean; override; function DoParse(AParser: TTextParser): Boolean; override;
public public
Text : AnsiString; Tokens : array of TExpPart;
Count : Integer;
procedure PushToken(const AToken: AnsiString; ATokenType: TTokenType);
end; end;
procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString); procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString);
@ -1531,21 +1537,31 @@ end;
function isEndOfExpr(const t: AnsiString; CommaIsEnd: Boolean): Boolean; function isEndOfExpr(const t: AnsiString; CommaIsEnd: Boolean): Boolean;
begin begin
Result:=(t=']') or (t=';') or (t=')') or (CommaIsEnd and (t=',')); Result:=(t=']') or (t=';') or (t=')') or (CommaIsEnd and (t=',')) or (t='}');
end; end;
function ParseCExpr(Parser: TTextParser; CommaIsEnd: Boolean=False): TExpression; function ParseCExpr(Parser: TTextParser; CommaIsEnd: Boolean=False): TExpression;
var var
x : TExpression; x : TExpression;
lvl : Integer;
begin begin
if isEndOfExpr(Parser.Token, CommaIsEnd) then if isEndOfExpr(Parser.Token, CommaIsEnd) then
Result:=nil Result:=nil
else begin else begin
lvl:=0;
x := TExpression.Create(Parser.Index); x := TExpression.Create(Parser.Index);
while not isEndOfExpr(Parser.Token, CommaIsEnd) do begin
x.Text:=x.Text+Parser.Token; repeat
Parser.NextToken; if (Parser.Token='(') or (Parser.Token='[') then
end; inc(lvl)
else begin
if (lvl=0) and isEndOfExpr(Parser.Token, CommaIsEnd) then
Break
else if (Parser.Token=')') or (Parser.Token=']') then
dec(lvl)
end;
x.PushToken(Parser.Token, Parser.TokenType);
until not Parser.NextToken;
Result:=x; Result:=x;
end; end;
end; end;
@ -1557,6 +1573,18 @@ begin
Result:=False; Result:=False;
end; end;
procedure TExpression.PushToken(const AToken:AnsiString; ATokenType: TTokenType);
begin
if Count=length(Tokens) then begin
if Count=0 then SetLength(Tokens, 2)
else SetLength(Tokens, Count*2);
end;
Tokens[Count].Token:=AToken;
Tokens[Count].TokenType:=ATokenType;
inc(Count);
end;
procedure ParseFuncParams(Parser: TTextParser; FuncName: TNamePart); procedure ParseFuncParams(Parser: TTextParser; FuncName: TNamePart);
var var
prmtype : TEntity; prmtype : TEntity;
@ -1777,13 +1805,8 @@ begin
AParser.NextToken; AParser.NextToken;
end; end;
if AParser.Token<>'{' then begin if AParser.Token='{' then begin
ErrorExpect(AParser, '{'); AParser.NextToken;
Exit;
end;
AParser.NextToken;
try
repeat repeat
v:=TVarFuncEntity.Create(AParser.TokenPos); v:=TVarFuncEntity.Create(AParser.TokenPos);
if not ParseNames(AParser, v.RetType, v.Names) then begin if not ParseNames(AParser, v.RetType, v.Names) then begin
@ -1800,10 +1823,8 @@ begin
until (AParser.Token='}'); until (AParser.Token='}');
ConsumeToken(AParser, '}'); ConsumeToken(AParser, '}');
Result:=st;
finally
if not Assigned(Result) then st.Free;
end; end;
Result:=st;
end; end;
function ParseUnion(AParser:TTextParser):TUnionType; function ParseUnion(AParser:TTextParser):TUnionType;
@ -1907,8 +1928,8 @@ begin
en.AddItem(nm, x, ofs); en.AddItem(nm, x, ofs);
if AParser.Token=',' then AParser.NextToken; if AParser.Token=',' then AParser.NextToken;
end; end;
AParser.NextToken;
end; end;
AParser.NextToken;
Result:=en; Result:=en;
finally finally
if not Assigned(Result) then en.Free; if not Assigned(Result) then en.Free;

View File

@ -60,6 +60,23 @@ type
// X - column (starting from 1); // X - column (starting from 1);
function ConvertCode(const t: AnsiString; var endPoint: TPoint; cfg: TConvertSettings = nil): AnsiString; function ConvertCode(const t: AnsiString; var endPoint: TPoint; cfg: TConvertSettings = nil): AnsiString;
// converts C-expression to Pascal expression, replace symbols with pascal equvialents.
// WARN: * the function doesn't handle macroses (treats them as identifiers)
// * it doesn't recognizes typecasting
// * it doesn't recognize the correct order of operations.
function PasExp(x: TExpression): AnsiString;
// returns true, if x is single number expression. V is the value of the number
function isNumberExp(x: TExpression; var v: Int64): Boolean;
// returns array limit base on x expression.
// if expression is a single number (N), then evaluates the N-1 number and returns it as string
// if expression is complex, returns pascal expression exp-1.
// i.e. int a[10] -> a: array [0..9] of Integer;
// int a[10*2] -> a: array [0..10*2-1] of Integer;
// int a[MAXCONST] -> a: array [0..MAXCONST-1] of Integer;
function PasArrayLimit(x: TExpression): AnsiString;
implementation implementation
type type
@ -108,15 +125,16 @@ type
procedure DeclarePasType(TypeEntity: TEntity; const PasTypeName: AnsiString); procedure DeclarePasType(TypeEntity: TEntity; const PasTypeName: AnsiString);
procedure DeclareFuncType(const PasTypeName, RetTypeName: AnsiString; const params: array of TFuncParam); procedure DeclareFuncType(const PasTypeName, RetTypeName: AnsiString; const params: array of TFuncParam);
procedure WriteLnCommentForOffset(AOffset: Integer); procedure WriteLnCommentForOffset(AOffset: Integer; NeedOffset: Boolean=True);
function NextCommentBefore(AOffset: Integer): Integer; function NextCommentBefore(AOffset: Integer): Integer;
procedure WriteLnCommentsBeforeOffset(AOffset: Integer); procedure WriteLnCommentsBeforeOffset(AOffset: Integer);
procedure WriteFuncDecl(const FnName, PasRetType: Ansistring; const params : array of TFuncParam); procedure WriteFuncDecl(const FnName, PasRetType: Ansistring; const params : array of TFuncParam);
procedure WriteFuncOrVar(cent: TVarFuncEntity; StartVar: Boolean); // todo: deprecate! procedure WriteFuncOrVar(cent: TVarFuncEntity; StartVar, WriteComment: Boolean); // todo: deprecate!
procedure WriteTypeDef(tp: TTypeDef); procedure WriteTypeDef(tp: TTypeDef);
procedure WriteEnum(en: TEnumType); procedure WriteEnum(en: TEnumType);
procedure WriteEnumAsConst(en: TEnumType); procedure WriteEnumAsConst(en: TEnumType);
procedure WriteUnion(st: TUnionType);
procedure WriteStruct(st: TStructType); procedure WriteStruct(st: TStructType);
procedure WriteCommentToPas(cent: TComment); procedure WriteCommentToPas(cent: TComment);
procedure WriteExp(x: TExpression); procedure WriteExp(x: TExpression);
@ -289,12 +307,35 @@ begin
FillChar(Result[1], AstCount, '*'); FillChar(Result[1], AstCount, '*');
end; end;
function isNumberExp(x: TExpression; var v: Int64): Boolean;
var
err : Integer;
begin
Result:=Assigned(x) and (x.count=1);
if Result then begin
Val(x.Tokens[0].Token, v, err);
Result:=err=0;
end;
end;
function PasArrayLimit(x: TExpression): AnsiString;
var
i : Int64;
begin
if isNumberExp(x, i) then
Result:=IntToStr(i-1)
else
Result:=PasExp(x) + '-1';
end;
procedure WriteArray(arr: TNamePart; wr: TCodeWriter); procedure WriteArray(arr: TNamePart; wr: TCodeWriter);
var var
i : Integer; i : Integer;
begin begin
wr.W('array '); wr.W('array ');
for i := 0 to length(arr.arrayexp) - 1 do wr.W('[0..' + arr.arrayexp[i].Text + '-1]'); for i := 0 to length(arr.arrayexp) - 1 do wr.W('[0..' + PasArrayLimit(arr.arrayexp[i])+']');
wr.W(' of '); wr.W(' of ');
end; end;
@ -446,7 +487,7 @@ end;
procedure TCodeConvertor.WriteExp(x:TExpression); procedure TCodeConvertor.WriteExp(x:TExpression);
begin begin
wr.W('0 {todo writeexp}'); wr.W(PasExp(x));
end; end;
function CtoPasSymbol(const t: AnsiString): AnsiString; function CtoPasSymbol(const t: AnsiString): AnsiString;
@ -474,7 +515,6 @@ var
begin begin
if cent.SubsText<>'' then begin if cent.SubsText<>'' then begin
SetPasSection(wr, 'const'); SetPasSection(wr, 'const');
//wr.Wln(cfg.GetUniqueName(cent._Name) + ' = ' + Trim(cent.SubsText)+';');
p:=CreateCParser(cent.SubsText, false); p:=CreateCParser(cent.SubsText, false);
s:=''; s:='';
while p.NextToken do begin while p.NextToken do begin
@ -544,14 +584,14 @@ begin
WriteFuncDecl('', RetTypeName, params); WriteFuncDecl('', RetTypeName, params);
end; end;
procedure TCodeConvertor.WriteLnCommentForOffset(AOffset:Integer); procedure TCodeConvertor.WriteLnCommentForOffset(AOffset:Integer; NeedOffset: Boolean);
var var
cmt : TComment; cmt : TComment;
begin begin
cmt:=FindCommentForLine( Breaker.LineNumber(AOffset)); cmt:=FindCommentForLine( Breaker.LineNumber(AOffset));
if Assigned(cmt) then begin if Assigned(cmt) then begin
LastOffset:=cmt.Offset; LastOffset:=cmt.Offset;
wr.W(' '); if NeedOffset then wr.W(' ');
WriteCommentToPas(cmt); WriteCommentToPas(cmt);
end else end else
wr.Wln; wr.Wln;
@ -579,7 +619,7 @@ var
begin begin
i:=NextCommentBefore(AOffset); i:=NextCommentBefore(AOffset);
while i>=0 do begin while i>=0 do begin
WriteLnCommentForOffset(i); WriteLnCommentForOffset(i, False);
i:=NextCommentBefore(AOffset); i:=NextCommentBefore(AOffset);
end; end;
end; end;
@ -680,7 +720,9 @@ begin
if Result then Exit; if Result then Exit;
Result:=length(params)=1; Result:=length(params)=1;
if Result then if Result then
Result:=(params[0].prmtype is TSimpleType) and (TSimpleType(params[0].prmtype).Name='void'); Result:=(params[0].prmtype is TSimpleType) and
(TSimpleType(params[0].prmtype).Name='void') and
(params[0].name=nil);
end; end;
procedure TCodeConvertor.WriteFuncDecl(const FnName, PasRetType: Ansistring; const params : array of TFuncParam); procedure TCodeConvertor.WriteFuncDecl(const FnName, PasRetType: Ansistring; const params : array of TFuncParam);
@ -720,7 +762,7 @@ begin
if cfg.FuncDeclPostfix<>'' then wr.W('; '+cfg.FuncDeclPostfix); if cfg.FuncDeclPostfix<>'' then wr.W('; '+cfg.FuncDeclPostfix);
end; end;
procedure TCodeConvertor.WriteFuncOrVar(cent: TVarFuncEntity; StartVar: Boolean); procedure TCodeConvertor.WriteFuncOrVar(cent: TVarFuncEntity; StartVar, WriteComment: Boolean);
var var
i, j : integer; i, j : integer;
Name : TNamePart; Name : TNamePart;
@ -736,12 +778,14 @@ begin
wr.Wln(' bad declaration synax!'); wr.Wln(' bad declaration synax!');
Exit; Exit;
end; end;
id:=name.Id; id:=cfg.GetUniqueName(name.Id);
n:=name.owner; n:=name.owner;
if not Assigned(n) then begin if not Assigned(n) then begin
PushWriter;
rt:=GetPasTypeName(cent.RetType, Name);
PopWriter;
if StartVar then SetPasSection(wr, 'var'); if StartVar then SetPasSection(wr, 'var');
wr.W(id + ' : ' + GetPasTypeName(cent.RetType, Name)) wr.W(id + ' : ' + rt);
end else if (n.Kind=nk_Func) then begin end else if (n.Kind=nk_Func) then begin
SetPasSection(wr, ''); SetPasSection(wr, '');
rt:=GetPasTypeName(cent.RetType, n.owner); rt:=GetPasTypeName(cent.RetType, n.owner);
@ -752,9 +796,9 @@ begin
wr.W(id + ' : '); wr.W(id + ' : ');
ref:=n; ref:=n;
n:=n.owner; n:=n.owner;
if not Assigned(n) then if not Assigned(n) then begin
wr.W( GetPasTypeName(cent.RetType, ref) ) wr.W( GetPasTypeName(cent.RetType, ref) );
else end else
case n.Kind of case n.Kind of
nk_Array: begin nk_Array: begin
for i:=1 to ref.RefCount do wr.W('^'); for i:=1 to ref.RefCount do wr.W('^');
@ -777,7 +821,7 @@ begin
end; end;
wr.W(';'); wr.W(';');
WriteLnCommentForOffset(cent.Offset) if WriteComment then WriteLnCommentForOffset(cent.Offset)
end; end;
end; end;
@ -787,15 +831,14 @@ begin
Breaker:=TLineBreaker.Create; Breaker:=TLineBreaker.Create;
Breaker.SetText(ParsedText); Breaker.SetText(ParsedText);
if cent is TVarFuncEntity then if cent is TVarFuncEntity then begin
WriteFuncOrVar(cent as TVarFuncEntity, True) WriteFuncOrVar(cent as TVarFuncEntity, True, True)
else if cent is TTypeDef then end else if cent is TTypeDef then
WriteTypeDef(cent as TTypeDef) WriteTypeDef(cent as TTypeDef)
else if cent is TStructType then else if (cent is TStructType) or (cent is TEnumType) or (cent is TUnionType) then begin
DeclarePasType(cent as TStructType, TStructType(cent).Name) DeclarePasType(cent, GetCDeclTypeName(cent));
else if cent is TEnumType then wr.Wln(';');
DeclarePasType(cent as TEnumType, TEnumType(cent).Name) end else if cent is TComment then
else if cent is TComment then
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)
@ -869,7 +912,7 @@ begin
//todo: bit fields support //todo: bit fields support
for i:=0 to length(st.fields)-1 do begin for i:=0 to length(st.fields)-1 do begin
WriteLnCommentsBeforeOffset(st.fields[i].v.Offset); WriteLnCommentsBeforeOffset(st.fields[i].v.Offset);
WriteFuncOrVar(st.fields[i].v, False); WriteFuncOrVar(st.fields[i].v, False, True);
end; end;
wr.DecIdent; wr.DecIdent;
wr.W('end'); wr.W('end');
@ -916,26 +959,60 @@ end;
procedure TCodeConvertor.WriteEnumAsConst(en:TEnumType); procedure TCodeConvertor.WriteEnumAsConst(en:TEnumType);
var var
i : Integer; i : Integer;
v : Int64;
last : AnsiString;
useval : Boolean;
begin begin
if length(en.items)>0 then begin if length(en.items)>0 then begin
PushWriter; PushWriter;
WriteLnCommentsBeforeOffset(en.Offset); WriteLnCommentsBeforeOffset(en.Offset);
SetPasSection(wr, 'const'); SetPasSection(wr, 'const');
v:=0;
last:='';
useval:=True;
for i:=0 to length(en.items)-1 do begin for i:=0 to length(en.items)-1 do begin
WriteLnCommentsBeforeOffset(en.items[i].Offset); WriteLnCommentsBeforeOffset(en.items[i].Offset);
wr.W(en.items[i].Name + ' = '); wr.W(en.items[i].Name + ' = ');
if Assigned(en.items[i].Value) then if Assigned(en.items[i].Value) then begin
WriteExp(en.items[i].Value) WriteExp(en.items[i].Value);
else useval:=isNumberExp(en.items[i].Value, v);
wr.W(IntToStr(i)); end else begin
if useval
then wr.W(IntToStr(v))
else wr.W(last+' + 1');
end;
wr.W(';'); wr.W(';');
WriteLnCommentForOffset(en.items[i].Offset); WriteLnCommentForOffset(en.items[i].Offset);
inc(v);
last:=en.Items[i].Name;
end; end;
PopWriter; PopWriter;
end; end;
wr.W('Integer'); wr.W('Integer');
end; end;
procedure TCodeConvertor.WriteUnion(st:TUnionType);
var
i : Integer;
begin
if cfg.RecordsArePacked then wr.W('packed ');
wr.WLn('record');
wr.Wln('case Integer of');
wr.IncIdent;
for i:=0 to length(st.fields)-1 do begin
WriteLnCommentsBeforeOffset(st.fields[i].v.Offset);
wr.w(IntToStr(i)+':(');
WriteFuncOrVar(st.fields[i].v, False, False);
wr.W(');');
WriteLnCommentForOffset(st.fields[i].v.Offset);
end;
wr.DecIdent;
wr.w('end');
end;
function TCodeConvertor.NextAuxTypeName(const Prefix:AnsiString):AnsiString; function TCodeConvertor.NextAuxTypeName(const Prefix:AnsiString):AnsiString;
begin begin
if Prefix='' then Result:='AuxType'+IntToStr(AuxTypeCounter) if Prefix='' then Result:='AuxType'+IntToStr(AuxTypeCounter)
@ -949,8 +1026,12 @@ begin
wr.W(PasTypeName + ' = '); wr.W(PasTypeName + ' = ');
if TypeEntity is TStructType then if TypeEntity is TStructType then
WriteStruct(TStructType(TypeEntity)) WriteStruct(TStructType(TypeEntity))
else if TypeEntity is TEnumType then else if TypeEntity is TEnumType then begin
WriteEnum(TEnumType(TypeEntity)) WriteEnum(TEnumType(TypeEntity))
end else if TypeEntity is TUnionType then begin
WriteUnion(TUnionType(TypeEntity))
end else if TypeEntity is TSimpleType then
wr.W( cfg.GetTypeName(TSimpleType(TypeEntity).Name))
else begin else begin
{SetPasSection(wr, 'type'); {SetPasSection(wr, 'type');
wr.W(PasTypeName + ' = ');} wr.W(PasTypeName + ' = ');}
@ -1163,8 +1244,10 @@ begin
CtoPasTypes.Values['void'] := ''; CtoPasTypes.Values['void'] := '';
CtoPasTypes.Values['void*'] := 'Pointer'; CtoPasTypes.Values['void*'] := 'Pointer';
CtoPasTypes.Values['void**'] := 'PPointer'; CtoPasTypes.Values['void**'] := 'PPointer';
CtoPasTypes.Values['char'] := 'Char';
CtoPasTypes.Values['char*'] := 'PChar'; CtoPasTypes.Values['char*'] := 'PChar';
CtoPasTypes.Values['char**'] := 'PPChar'; CtoPasTypes.Values['char**'] := 'PPChar';
CtoPasTypes.Values['signed char'] := 'SmallInt';
CtoPasTypes.Values['long'] := 'Longword'; CtoPasTypes.Values['long'] := 'Longword';
CtoPasTypes.Values['long*'] := 'PLongword'; CtoPasTypes.Values['long*'] := 'PLongword';
CtoPasTypes.Values['long long'] := 'Int64'; CtoPasTypes.Values['long long'] := 'Int64';
@ -1173,6 +1256,7 @@ begin
CtoPasTypes.Values['unsigned long long*'] := 'PQWord'; CtoPasTypes.Values['unsigned long long*'] := 'PQWord';
CtoPasTypes.Values['short'] := 'SmallInt'; CtoPasTypes.Values['short'] := 'SmallInt';
CtoPasTypes.Values['short*'] := 'PSmallInt'; CtoPasTypes.Values['short*'] := 'PSmallInt';
CtoPasTypes.Values['unsigned'] := 'LongWord';
CtoPasTypes.Values['unsigned short'] := 'Word'; CtoPasTypes.Values['unsigned short'] := 'Word';
CtoPasTypes.Values['unsigned short*'] := 'PWord'; CtoPasTypes.Values['unsigned short*'] := 'PWord';
CtoPasTypes.Values['unsigned char'] := 'Byte'; CtoPasTypes.Values['unsigned char'] := 'Byte';
@ -1209,5 +1293,20 @@ begin
end; end;
end; end;
function PasExp(x: TExpression): AnsiString;
var
i : Integer;
begin
Result:='';
for i:=0 to x.Count-1 do begin
if x.Tokens[i].TokenType=tt_Symbol then
Result:=Result+CtoPasSymbol(x.Tokens[i].Token)+' '
else
Result:=Result+x.Tokens[i].Token+' ';
end;
Result:=Copy(Result, 1, length(Result)-1);
end;
end. end.

View File

@ -144,6 +144,9 @@ begin
txt:=''; txt:='';
end; end;
var
parsing : Boolean = False;
procedure TryParse; procedure TryParse;
var var
editor : TSourceEditorInterface; editor : TSourceEditorInterface;
@ -153,26 +156,33 @@ var
p : TPoint; p : TPoint;
st : TPoint; st : TPoint;
begin begin
if parsing then Exit;
if not Assigned(SourceEditorManagerIntf) or not Assigned(SourceEditorManagerIntf.ActiveEditor) then Exit; if not Assigned(SourceEditorManagerIntf) or not Assigned(SourceEditorManagerIntf.ActiveEditor) then Exit;
editor:=SourceEditorManagerIntf.ActiveEditor;
if Assigned(CtoPasConfig) then CtoPasConfig.UIToSettings; parsing:=True;
try
editor:=SourceEditorManagerIntf.ActiveEditor;
i:=editor.CursorTextXY.Y; if Assigned(CtoPasConfig) then CtoPasConfig.UIToSettings;
dec(i);
if i<0 then i:=0;
txt:='';
for i:=i to editor.Lines.Count-1 do
txt:=txt+editor.Lines[i]+#10;
if DoConvertCode(txt, p, s) then i:=editor.CursorTextXY.Y;
begin dec(i);
inc(p.Y, editor.CursorTextXY.Y-1); if i<0 then i:=0;
st:=editor.CursorTextXY; txt:='';
st.X:=1; for i:=i to editor.Lines.Count-1 do
editor.ReplaceText(st, p, s); txt:=txt+editor.Lines[i]+#10;
if Assigned(CtoPasConfig) then
CtoPasConfig.SettingsToUI; if DoConvertCode(txt, p, s) then
begin
inc(p.Y, editor.CursorTextXY.Y-1);
st:=editor.CursorTextXY;
st.X:=1;
editor.ReplaceText(st, p, s);
if Assigned(CtoPasConfig) then
CtoPasConfig.SettingsToUI;
end;
finally
parsing:=False;
end; end;
end; end;