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