diff --git a/components/chelper/cconvert.lpi b/components/chelper/cconvert.lpi index 6ad4e6435..3c7362ecc 100644 --- a/components/chelper/cconvert.lpi +++ b/components/chelper/cconvert.lpi @@ -8,6 +8,7 @@ + diff --git a/components/chelper/cconvert.lpr b/components/chelper/cconvert.lpr index 0b354b908..c594fb429 100644 --- a/components/chelper/cconvert.lpr +++ b/components/chelper/cconvert.lpr @@ -22,7 +22,7 @@ program cconvert; uses SysUtils,Classes, - ctopasconvert,cparsertypes,cparserutils,cconvconfig, objcparsing; + ctopasconvert,cparsertypes, cparserutils,cconvconfig, objcparsing; var ConfigFile : AnsiString = ''; @@ -81,6 +81,7 @@ var i : Integer; p : TPoint; cfg : TConvertSettings; + err : TErrorInfo; begin if ParamCount=0 then Exit; inps := TStringList.Create; @@ -91,15 +92,15 @@ begin InitSettings(cfg); inps.LoadFromFile(ParamStr(ParamCount)); - outs.Text:=ConvertCode(inps.Text, p, ParseAll, cfg); - if OutputFile<>'' then begin - outs.Insert(0, Format('%d %d', [p.Y,p.X])); + outs.Text:=ConvertCode(inps.Text, p, ParseAll, err, cfg);; + outs.Insert(0, Format('%d %d', [p.Y,p.X])); + if err.isError then outs.Insert(0, Format('error %d %d %s',[err.ErrorPos.Y, err.ErrorPos. X, err.ErrorMsg]) ); + + if OutputFile<>'' then outs.SaveToFile(OutputFile) - end else begin - writeln(p.Y,' ',p.X); + else for i:=0 to outs.Count-1 do writeln(outs[i]); - end; finally if not ConfigFileRO and (ConfigFile<>'') then begin ForceDirectories(ExtractFilePath(ConfigFile)); diff --git a/components/chelper/cparsertypes.pas b/components/chelper/cparsertypes.pas index 742785150..8fb055f2d 100755 --- a/components/chelper/cparsertypes.pas +++ b/components/chelper/cparsertypes.pas @@ -290,7 +290,8 @@ var ParseNextEntity: function (AParser: TTextParser): TEntity = nil; ParseNamePart : function (Parser: TTextParser): TNamePart = nil; -function ParseNextCEntity(AParser: TTextParser): TEntity; +function ParseNextCEntity(AParser: TTextParser): TEntity; // default ParseNextEntity +function ParseCNamePart(Parser: TTextParser): TNamePart; // default ParseNamePart function ParseCExpression(AParser: TTextParser; var ExpS: AnsiString): Boolean; procedure ParseCNumeric(const S: AnsiString; var idx: integer; var NumStr: AnsiSTring); @@ -1500,12 +1501,16 @@ begin Result:=v; end; - if AParser.Token<>';' then ErrorExpect(AParser,';'); + if AParser.Token<>';' then begin + Result.Free; + Result:=nil; + ErrorExpect(AParser,';'); + end; end; procedure ErrorExpect(Parser:TTextParser;const Expect:AnsiString); begin - Parser.SetError('Excepcted: '+ Expect); + Parser.SetError('expected: "'+ Expect + '" but "'+Parser.Token+'" found'); end; function ConsumeToken(Parser:TTextParser;const Token:AnsiString):Boolean; @@ -1718,6 +1723,11 @@ begin end; end; +function isEndOfName(APArser: TTextParser): Boolean; +begin + Result:=(AParser.TokenType=tt_Symbol) and (AParser.Token[1] in [';',')',',']); +end; + function ParseNames(Parser: TTextParser; var NameType: TEntity; Names: TList; AllowMultipleNames: Boolean): Boolean; var Name : TNamePart; @@ -1750,10 +1760,10 @@ begin Result:=True; Exit; end; - done:=(Parser.Token<>',') and (Parser.Token=')'); + done:=isEndOfName(Parser); if not done then begin if Parser.Token <> ',' then begin - ErrorExpect(Parser, ')'); + ErrorExpect(Parser, ';'); Exit; end; Parser.NextToken; diff --git a/components/chelper/ctopasconvert.pas b/components/chelper/ctopasconvert.pas index e5a8fddf7..32d680034 100644 --- a/components/chelper/ctopasconvert.pas +++ b/components/chelper/ctopasconvert.pas @@ -61,10 +61,16 @@ type function GetTypeName(const CTypeName: AnsiString): Ansistring; end; + TErrorInfo = record + isError : Boolean; + ErrorMsg : AnsiString; // error message + ErrorPos : TPoint; // position in ORIGINAL (not-macrosed) text + end; + // endPoint contains // Y - line number (starting from 1), // X - column (starting from 1); -function ConvertCode(const t: AnsiString; var endPoint: TPoint; AllText: Boolean; cfg: TConvertSettings = nil): AnsiString; +function ConvertCode(const t: AnsiString; var endPoint: TPoint; AllText: Boolean; var ParseError: TErrorInfo; 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) @@ -291,7 +297,7 @@ begin PrecompEnd:=-1; end; -function ParseNextEntityOrComment(AParser: TTextParser; cmt: TStopComment): TEntity; +function ParseNextEntityOrComment(AParser: TTextParser; cmt: TStopComment; var ParseError: TErrorInfo): TEntity; var ent : TEntity; entidx : Integer; @@ -313,6 +319,11 @@ begin end; if (not Assigned(Result)) or (Assigned(ent) and (ent.Offset0 then begin + ParseError.ErrorPos.X:=AParser.TokenPos; + ParseError.ErrorMsg:=AParser.Errors[0]; + ParseError.isError:=True; + end; Result:=ent; AParser.Index:=entidx; end; @@ -428,19 +439,20 @@ begin for i:=1 to c do Result:=Result+LineEnding; end; -function ConvertCode(const t: AnsiString; var endPoint: TPoint; AllText: Boolean; cfg: TConvertSettings): AnsiString; +function ConvertCode(const t: AnsiString; var endPoint: TPoint; AllText: Boolean; var ParseError: TErrorInfo; cfg: TConvertSettings): AnsiString; var p : TTextParser; ent : TEntity; - i : integer; - le : integer; cnv : TCodeConvertor; macros : TCMacroHandler; owncfg : Boolean; lastsec : AnsiString; // last code section ofs : Integer; cmt : TStopComment; + i : Integer; + succidx : Integer; begin + FillChar(ParseError, sizeof(ParseError), 0); Result:=''; ent:=nil; owncfg:=not Assigned(cfg); @@ -460,14 +472,20 @@ begin try repeat + cmt.Clear; try ofs := p.Index; p.NextToken; - ent := ParseNextEntityOrComment(p, cmt); + ent := ParseNextEntityOrComment(p, cmt, ParseError); except ent:=nil; end; + if ParseError.isError then + Break + else + succidx:=p.Index + p.MacrosDelta; + if Assigned(ent) then begin cnv := TCodeConvertor.Create(cfg); try @@ -498,16 +516,11 @@ begin until (ent=nil) or not AllText; - i := 1; - le := 0; - endPoint.X := 0; - endPoint.Y := 0; - while i < p.Index do begin - Inc(endPoint.Y); - le := i; - SkipLine(p.Buf, i); - end; - endPoint.X := p.Index - le + 1 + p.MacrosDelta; + + OffsetToLinePos(t, succidx, endPoint); + + if ParseError.isError then + OffsetToLinePos(t, ParseError.ErrorPos.X + p.MacrosDelta, ParseError.ErrorPos); finally p.Free; diff --git a/components/chelper/objcparsing.pas b/components/chelper/objcparsing.pas index ead66fa84..da0e758fb 100644 --- a/components/chelper/objcparsing.pas +++ b/components/chelper/objcparsing.pas @@ -304,7 +304,8 @@ begin if AParser.Token='>' then AParser.NextToken; end; - if ParseMethods(AParser, p.Methods, objcend) then Result:=p; + 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; diff --git a/components/chelper/textparsingutils.pas b/components/chelper/textparsingutils.pas index e2ba8c949..9dd595511 100644 --- a/components/chelper/textparsingutils.pas +++ b/components/chelper/textparsingutils.pas @@ -22,6 +22,9 @@ unit TextParsingUtils; interface +uses + Types; + type TCharSet = set of Char; @@ -50,6 +53,8 @@ function SkipCommentBlock(const s: AnsiString; var index: Integer; const closecm function SkipLine(const s: AnsiString; var index: Integer): AnsiString; +function OffsetToLinePos(const t: AnsiString; Offset: Integer; var P: TPoint): AnsiString; + implementation function ScanWhile(const s: AnsiString; var index: Integer; const ch: TCharSet): AnsiString; @@ -147,5 +152,21 @@ begin inc(index); end; +function OffsetToLinePos(const t: AnsiString; Offset: Integer; var P: TPoint): AnsiString; +var + i, le : Integer; +begin + i := 1; + le := 0; + P.X := 0; + P.Y := 0; + while i < Offset do begin + Inc(P.Y); + le := i; + SkipLine(t, i); + end; + P.X := Offset - le + 1; +end; + end. diff --git a/components/chelper/tosourceeditor.pas b/components/chelper/tosourceeditor.pas index d485706b0..0b51a21a9 100644 --- a/components/chelper/tosourceeditor.pas +++ b/components/chelper/tosourceeditor.pas @@ -24,14 +24,44 @@ interface uses Classes, SysUtils, Dialogs, LCLType, LCLIntf, Forms, - Menus, MenuIntf, SrcEditorIntf, process, LazIDEIntf, + Menus, MenuIntf, SrcEditorIntf, process, LazIDEIntf, IDEMsgIntf, extconvdialog, converteridesettings, cconvconfig; procedure Register; implementation -function DoExtConvert(const t: AnsiString; ParseAll: Boolean; var EndPos: TPoint): AnsiString; +type + TErrorInfo = record + isError : Boolean; + Error : AnsiString; + ErrorPos : TPoint; + end; + + +function GetErrorInfo(const errstr: AnsiString; var error: TerrorInfo): Boolean; +var + i : Integer; + d : AnsiString; + err : Integer; +begin + i:=Pos('error ', errstr); + error.isError:=i>0; + Result:=error.isError; + if not error.isError then Exit; + + d:=Copy(errstr, 7, length(errstr)-6); + i:=Pos(' ', d); + Val( copy(d, 1, i-1), error.ErrorPos.Y, err); + d:=Copy(d, i+1, length(d)); + + i:=Pos(' ', d); + Val( copy(d, 1, i-1), error.ErrorPos.X, err); + + error.Error:=Copy(d, i+1, length(d)); +end; + +function DoExtConvert(const t: AnsiString; ParseAll: Boolean; var EndPos: TPoint; var error: TErrorInfo): AnsiString; var p : TProcess; d : AnsiString; @@ -104,7 +134,10 @@ begin try st.LoadFromFile(outp); if st.Count=0 then Exit; + i:=0; d:=st[0]; + if GetErrorInfo(d, error) then d:=st[1]; + if d='' then Exit; i:=Pos(' ', d); if i>=1 then begin @@ -126,7 +159,7 @@ begin end; end; -function DoConvertCode(const t: AnsiString; ParseAll: Boolean; var EndPoint: TPoint; var txt: AnsiString): Boolean; +function DoConvertCode(const t: AnsiString; ParseAll: Boolean; var EndPoint: TPoint; var txt: AnsiString; var error: TErrorInfo): Boolean; begin Result:=False; if UseExtTool then begin @@ -135,7 +168,7 @@ begin Exit; end; cconvconfig.SaveToFile(ConvFile, ConvSettings); - txt:=DoExtConvert(t, ParseAll, EndPoint); + txt:=DoExtConvert(t, ParseAll, EndPoint, error); Result:=(EndPoint.X>=0) and (EndPoint.Y>=0); if Result then cconvconfig.LoadFromFile(ConvFile, ConvSettings) @@ -156,6 +189,9 @@ var s : AnsiString; p : TPoint; st : TPoint; + err : TErrorInfo; + line : TIDEMessageLine; + parts : TStringList; begin if parsing then Exit; if not Assigned(SourceEditorManagerIntf) or not Assigned(SourceEditorManagerIntf.ActiveEditor) then Exit; @@ -173,13 +209,34 @@ begin for i:=i to editor.Lines.Count-1 do txt:=txt+editor.Lines[i]+#10; - if DoConvertCode(txt, ParseAll, p, s) then + if Assigned(IDEMessagesWindow) then IDEMessagesWindow.Clear; + + if DoConvertCode(txt, ParseAll, p, s, err) then begin - inc(p.Y, st.Y-1); - st.X:=1; - editor.ReplaceText(st, p, s); - if Assigned(CtoPasConfig) then - CtoPasConfig.SettingsToUI; + if p.Y>0 then begin + inc(p.Y, st.Y-1); + st.X:=1; + editor.ReplaceText(st, p, s); + if Assigned(CtoPasConfig) then + CtoPasConfig.SettingsToUI; + end; + if err.isError then begin + inc(err.ErrorPos.Y, st.Y-1); + if Assigned(IDEMessagesWindow) then begin + s:=Format('%s(%d,%d) Chelper: %s', [ExtractFileName(editor.FileName), err.ErrorPos.Y,err.ErrorPos.X, err.Error]); + parts:=TStringList.Create; + try + parts.Values['Type']:='Chelper'; + parts.Values['Filename']:=editor.FileName; + parts.Values['Line']:=IntToStr(err.ErrorPos.Y); + parts.Values['Column']:=IntToStr(err.ErrorPos.X); + IDEMessagesWindow.AddMsg(s, ExtractFileDir(editor.FileName), -1, parts); + finally + parts.Free; + end; + end; + editor.CursorTextXY:=err.ErrorPos; + end; end; finally parsing:=False; @@ -215,6 +272,43 @@ begin RegisterIDEMenuCommand(itmSecondaryTools, 'CtoPas', 'C to Pascal Options', nil, @OnCtoPasOptionsClick); end; + +type + + { TChelperJumper } + + TChelperJumper = class(TIDEMsgQuickFixItem) + constructor Create; + procedure Execute(const Msg: TIDEMessageLine; Step: TIMQuickFixStep); override; + function IsApplicable(Line: TIDEMessageLine): boolean; override; + end; + +constructor TChelperJumper.Create; +begin + inherited Create; + Name:='Chelper code jumper'; + Caption:='Chelper code jumper'; + Steps:=[imqfoJump] +end; + +function TChelperJumper.IsApplicable(Line: TIDEMessageLine): boolean; +begin + Result:=Assigned(Line) and Assigned(Line.Parts) and (Line.Parts.Values['Type']='Chelper'); +end; + +procedure TChelperJumper.Execute(const Msg: TIDEMessageLine; Step: TIMQuickFixStep); +var + fn : AnsiString; + ln : Integer; + cl : Integer; +begin + if Step=imqfoJump then begin + if Msg.Parts.Values['Type']<>'Chelper' then Exit; + Msg.GetSourcePosition(fn, ln, cl); + LazarusIDE.DoOpenFileAndJumpToPos(fn, Point(cl, ln), -1, -1, -1, [ofOnlyIfExists,ofRegularFile,ofVirtualFile]); + end; +end; + procedure Register; var pth : AnsiString; @@ -225,6 +319,7 @@ begin LoadFromFile(ConvFile, ConvSettings); ReadIDESettings(ConvFile); if DefineFile='' then DefineFile:=pth+'cconvdefines.h'; + RegisterIDEMsgQuickFix(TChelperJumper.Create); end; initialization