You've already forked lazarus-ccr
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:
@ -8,6 +8,7 @@
|
||||
<MainUnitHasTitleStatement Value="False"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<UseAppBundle Value="False"/>
|
||||
<ResourceType Value="res"/>
|
||||
</General>
|
||||
|
@ -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.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));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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,14 +209,35 @@ 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
|
||||
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;
|
||||
end;
|
||||
@ -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
|
||||
|
Reference in New Issue
Block a user