chelper: added parser error reports

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1291 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
skalogryz
2010-08-17 12:29:27 +00:00
parent 86e7cf7017
commit 90fda595b9
7 changed files with 181 additions and 39 deletions

View File

@ -8,6 +8,7 @@
<MainUnitHasTitleStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>

View File

@ -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));

View File

@ -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;

View File

@ -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.Offset<Result.Offset)) then begin
if AParser.Errors.Count>0 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;

View File

@ -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;

View File

@ -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.

View File

@ -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