From ab0048814a9a2641fa9e158ac68dca19d95c01d1 Mon Sep 17 00:00:00 2001 From: skalogryz Date: Mon, 9 Aug 2010 20:29:26 +0000 Subject: [PATCH] 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 --- components/chelper/cconvert.lpr | 1 + components/chelper/cparsertypes.pas | 57 ++++++--- components/chelper/ctopasconvert.pas | 161 +++++++++++++++++++++----- components/chelper/tosourceeditor.pas | 42 ++++--- 4 files changed, 196 insertions(+), 65 deletions(-) diff --git a/components/chelper/cconvert.lpr b/components/chelper/cconvert.lpr index 19f992125..c0769101f 100644 --- a/components/chelper/cconvert.lpr +++ b/components/chelper/cconvert.lpr @@ -80,6 +80,7 @@ var p : TPoint; cfg : TConvertSettings; begin + if ParamCount=0 then Exit; inps := TStringList.Create; outs := TStringList.Create; diff --git a/components/chelper/cparsertypes.pas b/components/chelper/cparsertypes.pas index 2d018cb75..e675dd4a0 100755 --- a/components/chelper/cparsertypes.pas +++ b/components/chelper/cparsertypes.pas @@ -266,10 +266,16 @@ type { TExpression } + TExpPart = record + Token : AnsiString; + TokenType : TTokenType; + end; TExpression = class(TEntity) function DoParse(AParser: TTextParser): Boolean; override; public - Text : AnsiString; + Tokens : array of TExpPart; + Count : Integer; + procedure PushToken(const AToken: AnsiString; ATokenType: TTokenType); end; procedure ErrorExpect(Parser: TTextParser; const Expect: AnsiString); @@ -1531,21 +1537,31 @@ end; function isEndOfExpr(const t: AnsiString; CommaIsEnd: Boolean): Boolean; begin - Result:=(t=']') or (t=';') or (t=')') or (CommaIsEnd and (t=',')); + Result:=(t=']') or (t=';') or (t=')') or (CommaIsEnd and (t=',')) or (t='}'); end; function ParseCExpr(Parser: TTextParser; CommaIsEnd: Boolean=False): TExpression; var - x : TExpression; + x : TExpression; + lvl : Integer; begin if isEndOfExpr(Parser.Token, CommaIsEnd) then Result:=nil else begin + lvl:=0; x := TExpression.Create(Parser.Index); - while not isEndOfExpr(Parser.Token, CommaIsEnd) do begin - x.Text:=x.Text+Parser.Token; - Parser.NextToken; - end; + + repeat + if (Parser.Token='(') or (Parser.Token='[') then + 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; end; end; @@ -1557,6 +1573,18 @@ begin Result:=False; 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); var prmtype : TEntity; @@ -1777,13 +1805,8 @@ begin AParser.NextToken; end; - if AParser.Token<>'{' then begin - ErrorExpect(AParser, '{'); - Exit; - end; - AParser.NextToken; - - try + if AParser.Token='{' then begin + AParser.NextToken; repeat v:=TVarFuncEntity.Create(AParser.TokenPos); if not ParseNames(AParser, v.RetType, v.Names) then begin @@ -1800,10 +1823,8 @@ begin until (AParser.Token='}'); ConsumeToken(AParser, '}'); - Result:=st; - finally - if not Assigned(Result) then st.Free; end; + Result:=st; end; function ParseUnion(AParser:TTextParser):TUnionType; @@ -1907,8 +1928,8 @@ begin en.AddItem(nm, x, ofs); if AParser.Token=',' then AParser.NextToken; end; + AParser.NextToken; end; - AParser.NextToken; Result:=en; finally if not Assigned(Result) then en.Free; diff --git a/components/chelper/ctopasconvert.pas b/components/chelper/ctopasconvert.pas index 1b1fe6a72..beeb18073 100644 --- a/components/chelper/ctopasconvert.pas +++ b/components/chelper/ctopasconvert.pas @@ -60,6 +60,23 @@ type // X - column (starting from 1); 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 type @@ -108,15 +125,16 @@ type procedure DeclarePasType(TypeEntity: TEntity; const PasTypeName: AnsiString); 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; procedure WriteLnCommentsBeforeOffset(AOffset: Integer); 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 WriteEnum(en: TEnumType); procedure WriteEnumAsConst(en: TEnumType); + procedure WriteUnion(st: TUnionType); procedure WriteStruct(st: TStructType); procedure WriteCommentToPas(cent: TComment); procedure WriteExp(x: TExpression); @@ -289,12 +307,35 @@ begin FillChar(Result[1], AstCount, '*'); 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); var i : Integer; begin 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 '); end; @@ -446,7 +487,7 @@ end; procedure TCodeConvertor.WriteExp(x:TExpression); begin - wr.W('0 {todo writeexp}'); + wr.W(PasExp(x)); end; function CtoPasSymbol(const t: AnsiString): AnsiString; @@ -474,7 +515,6 @@ var begin if cent.SubsText<>'' then begin SetPasSection(wr, 'const'); - //wr.Wln(cfg.GetUniqueName(cent._Name) + ' = ' + Trim(cent.SubsText)+';'); p:=CreateCParser(cent.SubsText, false); s:=''; while p.NextToken do begin @@ -544,14 +584,14 @@ begin WriteFuncDecl('', RetTypeName, params); end; -procedure TCodeConvertor.WriteLnCommentForOffset(AOffset:Integer); +procedure TCodeConvertor.WriteLnCommentForOffset(AOffset:Integer; NeedOffset: Boolean); var cmt : TComment; begin cmt:=FindCommentForLine( Breaker.LineNumber(AOffset)); if Assigned(cmt) then begin LastOffset:=cmt.Offset; - wr.W(' '); + if NeedOffset then wr.W(' '); WriteCommentToPas(cmt); end else wr.Wln; @@ -579,7 +619,7 @@ var begin i:=NextCommentBefore(AOffset); while i>=0 do begin - WriteLnCommentForOffset(i); + WriteLnCommentForOffset(i, False); i:=NextCommentBefore(AOffset); end; end; @@ -680,7 +720,9 @@ begin if Result then Exit; Result:=length(params)=1; 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; 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); end; -procedure TCodeConvertor.WriteFuncOrVar(cent: TVarFuncEntity; StartVar: Boolean); +procedure TCodeConvertor.WriteFuncOrVar(cent: TVarFuncEntity; StartVar, WriteComment: Boolean); var i, j : integer; Name : TNamePart; @@ -736,12 +778,14 @@ begin wr.Wln(' bad declaration synax!'); Exit; end; - id:=name.Id; + id:=cfg.GetUniqueName(name.Id); n:=name.owner; - if not Assigned(n) then begin + PushWriter; + rt:=GetPasTypeName(cent.RetType, Name); + PopWriter; 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 SetPasSection(wr, ''); rt:=GetPasTypeName(cent.RetType, n.owner); @@ -752,9 +796,9 @@ begin wr.W(id + ' : '); ref:=n; n:=n.owner; - if not Assigned(n) then - wr.W( GetPasTypeName(cent.RetType, ref) ) - else + if not Assigned(n) then begin + wr.W( GetPasTypeName(cent.RetType, ref) ); + end else case n.Kind of nk_Array: begin for i:=1 to ref.RefCount do wr.W('^'); @@ -777,7 +821,7 @@ begin end; wr.W(';'); - WriteLnCommentForOffset(cent.Offset) + if WriteComment then WriteLnCommentForOffset(cent.Offset) end; end; @@ -787,15 +831,14 @@ begin Breaker:=TLineBreaker.Create; Breaker.SetText(ParsedText); - if cent is TVarFuncEntity then - WriteFuncOrVar(cent as TVarFuncEntity, True) - else if cent is TTypeDef then + if cent is TVarFuncEntity then begin + WriteFuncOrVar(cent as TVarFuncEntity, True, True) + end else if cent is TTypeDef then WriteTypeDef(cent as TTypeDef) - else if cent is TStructType then - DeclarePasType(cent as TStructType, TStructType(cent).Name) - else if cent is TEnumType then - DeclarePasType(cent as TEnumType, TEnumType(cent).Name) - else if cent is TComment then + else if (cent is TStructType) or (cent is TEnumType) or (cent is TUnionType) then begin + DeclarePasType(cent, GetCDeclTypeName(cent)); + wr.Wln(';'); + end else if cent is TComment then WriteCommentToPas(cent as TComment) else if cent is TCPrepDefine then WritePreprocessor(cent as TCPrepDefine) @@ -869,7 +912,7 @@ begin //todo: bit fields support for i:=0 to length(st.fields)-1 do begin WriteLnCommentsBeforeOffset(st.fields[i].v.Offset); - WriteFuncOrVar(st.fields[i].v, False); + WriteFuncOrVar(st.fields[i].v, False, True); end; wr.DecIdent; wr.W('end'); @@ -916,26 +959,60 @@ end; procedure TCodeConvertor.WriteEnumAsConst(en:TEnumType); var i : Integer; + v : Int64; + last : AnsiString; + useval : Boolean; begin if length(en.items)>0 then begin PushWriter; WriteLnCommentsBeforeOffset(en.Offset); SetPasSection(wr, 'const'); + v:=0; + last:=''; + useval:=True; + for i:=0 to length(en.items)-1 do begin WriteLnCommentsBeforeOffset(en.items[i].Offset); + wr.W(en.items[i].Name + ' = '); - if Assigned(en.items[i].Value) then - WriteExp(en.items[i].Value) - else - wr.W(IntToStr(i)); + if Assigned(en.items[i].Value) then begin + WriteExp(en.items[i].Value); + useval:=isNumberExp(en.items[i].Value, v); + end else begin + if useval + then wr.W(IntToStr(v)) + else wr.W(last+' + 1'); + end; wr.W(';'); WriteLnCommentForOffset(en.items[i].Offset); + inc(v); + last:=en.Items[i].Name; end; + PopWriter; end; wr.W('Integer'); 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; begin if Prefix='' then Result:='AuxType'+IntToStr(AuxTypeCounter) @@ -949,8 +1026,12 @@ begin wr.W(PasTypeName + ' = '); if TypeEntity is TStructType then WriteStruct(TStructType(TypeEntity)) - else if TypeEntity is TEnumType then + else if TypeEntity is TEnumType then begin 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 {SetPasSection(wr, 'type'); wr.W(PasTypeName + ' = ');} @@ -1163,8 +1244,10 @@ begin CtoPasTypes.Values['void'] := ''; CtoPasTypes.Values['void*'] := 'Pointer'; CtoPasTypes.Values['void**'] := 'PPointer'; + CtoPasTypes.Values['char'] := 'Char'; CtoPasTypes.Values['char*'] := 'PChar'; CtoPasTypes.Values['char**'] := 'PPChar'; + CtoPasTypes.Values['signed char'] := 'SmallInt'; CtoPasTypes.Values['long'] := 'Longword'; CtoPasTypes.Values['long*'] := 'PLongword'; CtoPasTypes.Values['long long'] := 'Int64'; @@ -1173,6 +1256,7 @@ begin CtoPasTypes.Values['unsigned long long*'] := 'PQWord'; CtoPasTypes.Values['short'] := 'SmallInt'; CtoPasTypes.Values['short*'] := 'PSmallInt'; + CtoPasTypes.Values['unsigned'] := 'LongWord'; CtoPasTypes.Values['unsigned short'] := 'Word'; CtoPasTypes.Values['unsigned short*'] := 'PWord'; CtoPasTypes.Values['unsigned char'] := 'Byte'; @@ -1209,5 +1293,20 @@ begin 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. diff --git a/components/chelper/tosourceeditor.pas b/components/chelper/tosourceeditor.pas index e685b49ed..0806ff7b1 100644 --- a/components/chelper/tosourceeditor.pas +++ b/components/chelper/tosourceeditor.pas @@ -144,6 +144,9 @@ begin txt:=''; end; +var + parsing : Boolean = False; + procedure TryParse; var editor : TSourceEditorInterface; @@ -153,26 +156,33 @@ var p : TPoint; st : TPoint; begin + if parsing 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; - 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 Assigned(CtoPasConfig) then CtoPasConfig.UIToSettings; - 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; + i:=editor.CursorTextXY.Y; + 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 + 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;