fcl-css: started skipping invalid selectors

This commit is contained in:
mattias 2022-09-15 10:52:25 +02:00
parent f562a8d9fd
commit 8a6ee0055a
3 changed files with 136 additions and 14 deletions

View File

@ -43,8 +43,10 @@ Type
Function GetCurLine : Integer;
Function GetCurPos : Integer;
protected
Procedure DoError(Msg : TCSSString);
Procedure DoError(Fmt : TCSSString; const Args : Array of const);
Procedure DoWarn(const Msg : TCSSString);
Procedure DoWarn(const Fmt : TCSSString; const Args : Array of const);
Procedure DoError(const Msg : TCSSString);
Procedure DoError(const Fmt : TCSSString; const Args : Array of const);
Procedure Consume(aToken : TCSSToken);
Procedure SkipWhiteSpace;
function ParseComponentValueList(AllowRules: Boolean=True): TCSSElement;
@ -74,6 +76,7 @@ Type
Function ParseUnicodeRange : TCSSElement;
function ParseArray(aPrefix: TCSSElement): TCSSElement;
function ParseURL: TCSSElement;
function ParseInvalidToken: TCSSElement;
Property CurrentSource : TCSSString Read GetCurSource;
Property CurrentLine : Integer Read GetCurLine;
Property CurrentPos : Integer Read GetCurPos;
@ -155,22 +158,22 @@ begin
Result:=(CurrentToken=ctkEOF);
end;
procedure TCSSParser.DoError(Msg: TCSSString);
procedure TCSSParser.DoError(const Msg: TCSSString);
Var
ErrAt : TCSSString;
begin
If Assigned(FScanner) then
If FScanner.CurFilename<>'' then
ErrAt:=Format(SErrFileSource,[FScanner.CurFileName,FScanner.CurRow,FScanner.CurColumn])
ErrAt:=SafeFormat(SErrFileSource,[FScanner.CurFileName,FScanner.CurRow,FScanner.CurColumn])
else
ErrAt:=Format(SErrSource,[FScanner.Currow,FScanner.CurColumn]);
ErrAt:=SafeFormat(SErrSource,[FScanner.Currow,FScanner.CurColumn]);
Raise ECSSParser.Create(ErrAt+Msg)
end;
procedure TCSSParser.DoError(Fmt: TCSSString; const Args: array of const);
procedure TCSSParser.DoError(const Fmt: TCSSString; const Args: array of const);
begin
DoError(Format(Fmt,Args));
DoError(SafeFormat(Fmt,Args));
end;
procedure TCSSParser.Consume(aToken: TCSSToken);
@ -214,6 +217,19 @@ begin
Result:=0;
end;
procedure TCSSParser.DoWarn(const Msg: TCSSString);
begin
if Assigned(Scanner.OnWarn) then
Scanner.OnWarn(Self,Msg)
else
DoError(Msg);
end;
procedure TCSSParser.DoWarn(const Fmt: TCSSString; const Args: array of const);
begin
DoWarn(SafeFormat(Fmt,Args));
end;
constructor TCSSParser.Create(AInput: TStream; ExtraScannerOptions : TCSSScannerOptions = []);
begin
FInput:=AInput;
@ -723,6 +739,12 @@ begin
end;
end;
function TCSSParser.ParseInvalidToken: TCSSElement;
begin
Result:=TCSSElement(CreateElement(TCSSElement));
GetNextToken;
end;
function TCSSParser.ParsePseudo: TCSSElement;
Var
@ -987,11 +1009,16 @@ function TCSSParser.ParseSelector: TCSSElement;
ctkPSEUDO: Result:=ParsePseudo;
ctkPSEUDOFUNCTION: Result:=ParseCall('');
else
DoError(SErrUnexpectedToken ,[
DoWarn(SErrUnexpectedToken ,[
GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
CurrentTokenString,
'selector'
]);
case CurrentToken of
ctkINTEGER: Result:=ParseInteger;
ctkFLOAT: Result:=ParseFloat;
else Result:=ParseInvalidToken;
end;
end;
end;
@ -1011,11 +1038,11 @@ begin
Scanner.ReturnWhiteSpace:=true;
try
repeat
//writeln('TCSSParser.ParseSelector LIST START ',CurrentToken);
//writeln('TCSSParser.ParseSelector LIST START ',CurrentToken,' ',CurrentTokenString);
// read list
List:=nil;
El:=ParseSub;
//writeln('TCSSParser.ParseSelector LIST NEXT ',CurrentToken);
//writeln('TCSSParser.ParseSelector LIST NEXT ',CurrentToken,' ',CurrentTokenString);
while CurrentToken in [ctkSTAR,ctkIDENTIFIER,ctkCLASSNAME,ctkLBRACKET,ctkPSEUDO,ctkPSEUDOFUNCTION] do
begin
if List=nil then
@ -1035,7 +1062,7 @@ begin
Result:=El;
El:=nil;
//writeln('TCSSParser.ParseSelector LIST END ',CurrentToken);
//writeln('TCSSParser.ParseSelector LIST END ',CurrentToken,' ',CurrentTokenString);
SkipWhiteSpace;
case CurrentToken of

View File

@ -124,10 +124,12 @@ Type
TCSSScannerOption = (csoExtendedIdentifiers,csoReturnComments,csoReturnWhiteSpace);
TCSSScannerOptions = set of TCSSScannerOption;
TCSSScannerWarnEvent = procedure(Sender: TObject; Msg: string) of object;
TCSSScanner = class
private
FDisablePseudo: Boolean;
FOnWarn: TCSSScannerWarnEvent;
FOptions: TCSSScannerOptions;
FSourceFile: TLineReader;
FSourceFilename: TCSSString;
@ -176,9 +178,11 @@ Type
property CurColumn: Integer read GetCurColumn;
property CurToken: TCSSToken read FCurToken;
property CurTokenString: TCSSString read FCurTokenString;
Property DisablePseudo : Boolean Read FDisablePseudo Write FDisablePseudo;
property DisablePseudo : Boolean Read FDisablePseudo Write FDisablePseudo;
property OnWarn: TCSSScannerWarnEvent read FOnWarn write FOnWarn;
end;
function SafeFormat(const Fmt: string; const Args: array of const): string;
implementation
@ -190,6 +194,82 @@ Const
WhiteSpace = [' ',#9];
WhiteSpaceEx = WhiteSpace+[#0];
type
TMessageArgs = array of string;
procedure CreateMsgArgs(var MsgArgs: TMessageArgs; const Args: array of const);
var
i: Integer;
{$ifdef pas2js}
v: jsvalue;
{$endif}
begin
SetLength(MsgArgs, High(Args)-Low(Args)+1);
for i:=Low(Args) to High(Args) do
{$ifdef pas2js}
begin
v:=Args[i];
if isBoolean(v) then
MsgArgs[i] := BoolToStr(Boolean(v))
else if isString(v) then
MsgArgs[i] := String(v)
else if isNumber(v) then
begin
if IsInteger(v) then
MsgArgs[i] := str(NativeInt(v))
else
MsgArgs[i] := str(double(v));
end
else
MsgArgs[i]:='';
end;
{$else}
case Args[i].VType of
vtInteger: MsgArgs[i] := IntToStr(Args[i].VInteger);
vtBoolean: MsgArgs[i] := BoolToStr(Args[i].VBoolean);
vtChar: MsgArgs[i] := Args[i].VChar;
{$ifndef FPUNONE}
vtExtended: ; // Args[i].VExtended^;
{$ENDIF}
vtString: MsgArgs[i] := Args[i].VString^;
vtPointer: ; // Args[i].VPointer;
vtPChar: MsgArgs[i] := Args[i].VPChar;
vtObject: ; // Args[i].VObject;
vtClass: ; // Args[i].VClass;
vtWideChar: MsgArgs[i] := AnsiString(Args[i].VWideChar);
vtPWideChar: MsgArgs[i] := Args[i].VPWideChar;
vtAnsiString: MsgArgs[i] := AnsiString(Args[i].VAnsiString);
vtCurrency: ; // Args[i].VCurrency^);
vtVariant: ; // Args[i].VVariant^);
vtInterface: ; // Args[i].VInterface^);
vtWidestring: MsgArgs[i] := AnsiString(WideString(Args[i].VWideString));
vtInt64: MsgArgs[i] := IntToStr(Args[i].VInt64^);
vtQWord: MsgArgs[i] := IntToStr(Args[i].VQWord^);
vtUnicodeString:MsgArgs[i] := AnsiString(UnicodeString(Args[i].VUnicodeString));
end;
{$endif}
end;
function SafeFormat(const Fmt: string; const Args: array of const): string;
var
MsgArgs: TMessageArgs;
i: Integer;
begin
try
Result:=Format(Fmt,Args);
except
Result:='';
MsgArgs:=nil;
CreateMsgArgs(MsgArgs,Args);
for i:=0 to length(MsgArgs)-1 do
begin
if i>0 then
Result:=Result+',';
Result:=Result+MsgArgs[i];
end;
Result:='{'+Fmt+'}['+Result+']';
end;
end;
constructor TFileLineReader.Create(const AFilename: TCSSString);
begin

View File

@ -19,7 +19,8 @@ unit tcCSSParser;
interface
uses
Classes, SysUtils, fpcunit, testregistry, fpcssparser, fpcsstree;
Classes, SysUtils, fpcunit, testregistry, fpcssparser, fpcsstree,
fpCSSScanner;
type
@ -28,11 +29,13 @@ type
TTestBaseCSSParser = class(TTestCase)
Private
FParseResult: TCSSElement;
FSkipInvalid: boolean;
FSource : TStringStream;
FParser : TCSSParser;
FToFree: TCSSElement;
procedure Clear;
function GetRule: TCSSRuleElement;
procedure OnScannerWarn(Sender: TObject; Msg: string);
protected
procedure SetUp; override;
procedure TearDown; override;
@ -57,6 +60,7 @@ type
Property ParseResult : TCSSElement read FParseResult;
Property FirstRule : TCSSRuleElement Read GetRule;
Property ToFree : TCSSElement Read FToFree Write FToFree;
Property SkipInvalid: boolean read FSkipInvalid write FSkipInvalid;
end;
{ TTestCSSParser }
@ -190,7 +194,6 @@ begin
end;
end;
procedure TTestCSSFilesParser.Testabsolute;
begin
RunFileTest;
@ -747,11 +750,13 @@ end;
procedure TTestCSSParser.TestOneDeclarationNoColon;
begin
SkipInvalid:=true;
ParseRule('@a b { 0% { d: e; } }');
end;
procedure TTestCSSParser.TestTwoDeclarationNoColon;
begin
SkipInvalid:=true;
ParseRule('@a b { 0% { d: e; } 100% { f : g; } }');
end;
@ -808,6 +813,14 @@ begin
Result:=TCSSRuleElement(CheckClass('First element is rule',TCSSRuleElement,L.Children[0]));
end;
procedure TTestBaseCSSParser.OnScannerWarn(Sender: TObject; Msg: string);
var
aScanner: TCSSScanner;
begin
aScanner:=FParser.Scanner;
writeln('TTestBaseCSSParser.OnScannerWarn ',aScanner.CurFilename+'('+IntToStr(aScanner.CurRow)+','+IntToStr(aScanner.CurColumn)+') ',Msg);
end;
procedure TTestBaseCSSParser.SetUp;
begin
inherited SetUp;
@ -836,6 +849,8 @@ begin
Clear;
FSource:=TStringStream.Create(ASource);
FParser:=TCSSParser.Create(FSource);
if SkipInvalid then
FParser.Scanner.OnWarn:=@OnScannerWarn;
end;
procedure TTestBaseCSSParser.Parse;