fpcssscanner.pp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. {
  2. This file is part of the Free Pascal Run time library.
  3. Copyright (c) 2022- by Michael Van Canneyt ([email protected])
  4. This file contains CSS scanner and tokenizer
  5. See the File COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fpCSSScanner;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils;
  16. Type
  17. TCSSToken = (
  18. ctkUNKNOWN,
  19. ctkEOF,
  20. ctkWHITESPACE,
  21. ctkCOMMENT,
  22. ctkSEMICOLON,
  23. ctkLPARENTHESIS,
  24. ctkRPARENTHESIS,
  25. ctkLBRACE,
  26. ctkRBRACE,
  27. ctkLBRACKET,
  28. ctkRBRACKET,
  29. ctkCOMMA,
  30. ctkEQUALS,
  31. ctkAND,
  32. ctkTILDE,
  33. ctkPLUS,
  34. ctkCOLON,
  35. ctkDOUBLECOLON,
  36. ctkDOT,
  37. ctkDIV,
  38. ctkGT,
  39. ctkLT,
  40. ctkPERCENTAGE,
  41. ctkMINUS,
  42. ctkSTAR,
  43. ctkINTEGER,
  44. ctkFLOAT,
  45. ctkHASH,
  46. ctkSTRING,
  47. ctkIDENTIFIER,
  48. ctkATKEYWORD,
  49. ctkURL,
  50. ctkBADURL,
  51. ctkIMPORTANT,
  52. ctkCLASSNAME,
  53. ctkFUNCTION,
  54. ctkPSEUDO,
  55. ctkPSEUDOFUNCTION,
  56. ctkSQUARED,
  57. ctkUNICODERANGE
  58. );
  59. TCSSTokens = Set of TCSSToken;
  60. resourcestring
  61. SErrInvalidCharacter = 'Invalid character ''%s''';
  62. SErrOpenString = 'UTF8String exceeds end of line';
  63. SErrIncludeFileNotFound = 'Could not find include file ''%s''';
  64. SInvalidHexadecimalNumber = 'Invalid decimal number';
  65. SErrUnknownCharacter = 'Unknown character: %s';
  66. Type
  67. ECSSScanner = Class(Exception);
  68. TLineReader = class
  69. public
  70. function IsEOF: Boolean; virtual; abstract;
  71. function ReadLine: UTF8String; virtual; abstract;
  72. end;
  73. { TStreamLineReader }
  74. TStreamLineReader = class(TLineReader)
  75. private
  76. FStream : TStream;
  77. Buffer : Array[0..1024] of Byte;
  78. FBufPos,
  79. FBufLen : Integer;
  80. procedure FillBuffer;
  81. public
  82. Constructor Create(AStream : TStream);
  83. function IsEOF: Boolean; override;
  84. function ReadLine: UTF8String; override;
  85. end;
  86. TFileLineReader = class(TLineReader)
  87. private
  88. FTextFile: Text;
  89. FileOpened: Boolean;
  90. public
  91. constructor Create(const AFilename: UTF8String);
  92. destructor Destroy; override;
  93. function IsEOF: Boolean; override;
  94. function ReadLine: UTF8String; override;
  95. end;
  96. { TCSSScanner }
  97. TCSSScannerOption = (csoExtendedIdentifiers,csoReturnComments,csoReturnWhiteSpace);
  98. TCSSScannerOptions = set of TCSSScannerOption;
  99. TCSSScanner = class
  100. private
  101. FDisablePseudo: Boolean;
  102. FOptions: TCSSScannerOptions;
  103. FSourceFile: TLineReader;
  104. FSourceFilename: UTF8String;
  105. FCurRow: Integer;
  106. FCurToken: TCSSToken;
  107. FCurTokenString: UTF8String;
  108. FCurLine: UTF8String;
  109. TokenStr: PChar;
  110. FSourceStream : TStream;
  111. FOwnSourceFile : Boolean;
  112. function DoHash: TCSSToken;
  113. function DoIdentifierLike : TCSSToken;
  114. function DoMultiLineComment: TCSSToken;
  115. function CommentDiv: TCSSToken;
  116. function DoNumericLiteral: TCSSToken;
  117. function DoSingleLineComment: TCSSToken;
  118. function DoStringLiteral: TCSSToken;
  119. function DoWhiteSpace: TCSSToken;
  120. function EatBadURL: TCSSToken;
  121. Function DoUnicodeRange : TCSSTOKEN;
  122. function FetchLine: Boolean;
  123. function GetCurColumn: Integer;
  124. function GetReturnComments: Boolean;
  125. function GetReturnWhiteSpace: Boolean;
  126. function ReadUnicodeEscape: WideChar;
  127. procedure SetReturnComments(AValue: Boolean);
  128. procedure SetReturnWhiteSpace(AValue: Boolean);
  129. class function UnknownCharToStr(C: Char): String;
  130. protected
  131. procedure DoError(const Msg: UTF8String; Args: array of const); overload;
  132. procedure DoError(const Msg: UTF8String); overload;
  133. function DoFetchToken: TCSSToken; virtual;
  134. public
  135. constructor Create(ALineReader: TLineReader);
  136. constructor Create(AStream : TStream);
  137. destructor Destroy; override;
  138. procedure OpenFile(const AFilename: UTF8String);
  139. Function FetchToken: TCSSToken;
  140. Property ReturnComments : Boolean Read GetReturnComments Write SetReturnComments;
  141. Property ReturnWhiteSpace : Boolean Read GetReturnWhiteSpace Write SetReturnWhiteSpace;
  142. Property Options : TCSSScannerOptions Read FOptions Write FOptions;
  143. property SourceFile: TLineReader read FSourceFile;
  144. property CurFilename: UTF8String read FSourceFilename;
  145. property CurLine: UTF8String read FCurLine;
  146. property CurRow: Integer read FCurRow;
  147. property CurColumn: Integer read GetCurColumn;
  148. property CurToken: TCSSToken read FCurToken;
  149. property CurTokenString: UTF8String read FCurTokenString;
  150. Property DisablePseudo : Boolean Read FDisablePseudo Write FDisablePseudo;
  151. end;
  152. implementation
  153. Const
  154. Alpha = ['A'..'Z','a'..'z'];
  155. Num = ['0'..'9'];
  156. AlNum = Alpha+Num;
  157. AlNumIden = Alpha+Num+['-'];
  158. WhiteSpace = [' ',#9];
  159. WhiteSpaceEx = WhiteSpace+[#0];
  160. constructor TFileLineReader.Create(const AFilename: UTF8String);
  161. begin
  162. inherited Create;
  163. Assign(FTextFile, AFilename);
  164. Reset(FTextFile);
  165. FileOpened := true;
  166. end;
  167. destructor TFileLineReader.Destroy;
  168. begin
  169. if FileOpened then
  170. Close(FTextFile);
  171. inherited Destroy;
  172. end;
  173. function TFileLineReader.IsEOF: Boolean;
  174. begin
  175. Result := EOF(FTextFile);
  176. end;
  177. function TFileLineReader.ReadLine: UTF8String;
  178. begin
  179. ReadLn(FTextFile, Result);
  180. end;
  181. constructor TCSSScanner.Create(ALineReader: TLineReader);
  182. begin
  183. inherited Create;
  184. FSourceFile := ALineReader;
  185. end;
  186. constructor TCSSScanner.Create(AStream: TStream);
  187. begin
  188. FSourceStream:=ASTream;
  189. FOwnSourceFile:=True;
  190. Create(TStreamLineReader.Create(AStream));
  191. end;
  192. destructor TCSSScanner.Destroy;
  193. begin
  194. If FOwnSourceFile then
  195. FSourceFile.Free;
  196. inherited Destroy;
  197. end;
  198. procedure TCSSScanner.OpenFile(const AFilename: UTF8String);
  199. begin
  200. FSourceFile := TFileLineReader.Create(AFilename);
  201. FSourceFilename := AFilename;
  202. end;
  203. function TCSSScanner.FetchLine: Boolean;
  204. begin
  205. if FSourceFile.IsEOF then
  206. begin
  207. FCurLine := '';
  208. TokenStr := nil;
  209. Result := false;
  210. end else
  211. begin
  212. FCurLine := FSourceFile.ReadLine;
  213. TokenStr := PChar(CurLine);
  214. Result := true;
  215. Inc(FCurRow);
  216. end;
  217. end;
  218. function TCSSScanner.DoWhiteSpace : TCSSToken;
  219. begin
  220. Result:=ctkWhitespace;
  221. repeat
  222. Inc(TokenStr);
  223. if TokenStr[0] = #0 then
  224. if not FetchLine then
  225. begin
  226. FCurToken := Result;
  227. exit;
  228. end;
  229. until not (TokenStr[0] in [#9, ' ']);
  230. end;
  231. function TCSSScanner.DoSingleLineComment : TCSSToken;
  232. Var
  233. TokenStart : PChar;
  234. Len : Integer;
  235. begin
  236. Inc(TokenStr);
  237. TokenStart := TokenStr;
  238. while TokenStr[0] <> #0 do
  239. Inc(TokenStr);
  240. Len:=TokenStr-TokenStart;
  241. SetLength(FCurTokenString, Len);
  242. if (Len>0) then
  243. Move(TokenStart^,FCurTokenString[1],Len);
  244. Result := ctkComment;
  245. end;
  246. function TCSSScanner.DoMultiLineComment : TCSSToken;
  247. Var
  248. TokenStart : PChar;
  249. Len,OLen : Integer;
  250. PrevToken : Char;
  251. begin
  252. Inc(TokenStr);
  253. TokenStart := TokenStr;
  254. FCurTokenString := '';
  255. OLen:= 0;
  256. PrevToken:=#0;
  257. while Not ((TokenStr[0]='/') and (PrevToken='*')) do
  258. begin
  259. if (TokenStr[0]=#0) then
  260. begin
  261. Len:=TokenStr-TokenStart+1;
  262. SetLength(FCurTokenString,OLen+Len);
  263. if Len>1 then
  264. Move(TokenStart^,FCurTokenString[OLen+1],Len-1);
  265. Inc(OLen,Len);
  266. FCurTokenString[OLen]:=#10;
  267. if not FetchLine then
  268. begin
  269. Result := ctkEOF;
  270. FCurToken := Result;
  271. exit;
  272. end;
  273. TokenStart := TokenStr;
  274. PrevToken:=#0;
  275. end
  276. else
  277. begin
  278. PrevToken:=TokenStr[0];
  279. Inc(TokenStr);
  280. end;
  281. end;
  282. Len:=TokenStr-TokenStart-1; // -1 for *
  283. SetLength(FCurTokenString, Olen+Len);
  284. if (Len>0) then
  285. Move(TokenStart^, FCurTokenString[Olen + 1], Len);
  286. Inc(TokenStr);
  287. Result := ctkComment;
  288. end;
  289. function TCSSScanner.CommentDiv : TCSSToken;
  290. begin
  291. FCurTokenString := '';
  292. Inc(TokenStr);
  293. if (TokenStr[0] = '/') then // Single-line comment
  294. Result:=DoSingleLineComment
  295. else if (TokenStr[0]='*') then
  296. Result:=DoMultiLineComment
  297. else
  298. Result:=ctkDiv;
  299. end;
  300. function TCSSScanner.ReadUnicodeEscape: WideChar;
  301. const
  302. Hex = ['0'..'9','A'..'F','a'..'f' ];
  303. Var
  304. S : UTF8String;
  305. I : Integer;
  306. HaveHex : Boolean;
  307. begin
  308. S:='';
  309. I:=1;
  310. Repeat
  311. S:=S+Upcase(TokenStr[0]);
  312. HaveHex:=TokenStr[1] in Hex;
  313. if HaveHex then
  314. Inc(TokenStr);
  315. Inc(I);
  316. Until (I>4) or not HaveHex;
  317. // Takes care of conversion... This needs improvement !!
  318. Result:=WideChar(StrToInt('$'+S));
  319. end;
  320. procedure TCSSScanner.SetReturnComments(AValue: Boolean);
  321. begin
  322. if AValue then
  323. Include(FOptions,csoReturnComments)
  324. else
  325. Exclude(FOptions,csoReturnComments)
  326. end;
  327. procedure TCSSScanner.SetReturnWhiteSpace(AValue: Boolean);
  328. begin
  329. if AValue then
  330. Include(FOptions,csoReturnWhiteSpace)
  331. else
  332. Exclude(FOptions,csoReturnWhiteSpace)
  333. end;
  334. function TCSSScanner.DoStringLiteral: TCSSToken;
  335. Var
  336. Delim : Char;
  337. TokenStart : PChar;
  338. Len,OLen: Integer;
  339. S : UTF8String;
  340. begin
  341. Delim:=TokenStr[0];
  342. Inc(TokenStr);
  343. TokenStart := TokenStr;
  344. OLen := 0;
  345. FCurTokenString := '';
  346. while not (TokenStr[0] in [#0,Delim]) do
  347. begin
  348. if (TokenStr[0]='\') then
  349. begin
  350. // Save length
  351. Len := TokenStr - TokenStart;
  352. Inc(TokenStr);
  353. // Read escaped token
  354. Case TokenStr[0] of
  355. '"' : S:='"';
  356. 'a'..'f',
  357. 'A'..'F',
  358. '0'..'9':
  359. begin
  360. S:=UTF8Encode(ReadUniCodeEscape);
  361. end;
  362. #0 : DoError(SErrOpenString);
  363. else
  364. DoError(SErrInvalidCharacter, [TokenStr[0]]);
  365. end;
  366. SetLength(FCurTokenString, OLen + Len+1+Length(S));
  367. if Len > 0 then
  368. Move(TokenStart^, FCurTokenString[OLen + 1], Len);
  369. Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
  370. Inc(OLen, Len+Length(S));
  371. // Next char
  372. // Inc(TokenStr);
  373. TokenStart := TokenStr+1;
  374. end;
  375. if TokenStr[0] = #0 then
  376. DoError(SErrOpenString);
  377. Inc(TokenStr);
  378. end;
  379. if TokenStr[0] = #0 then
  380. DoError(SErrOpenString);
  381. Len := TokenStr - TokenStart;
  382. SetLength(FCurTokenString, OLen + Len);
  383. if Len > 0 then
  384. Move(TokenStart^, FCurTokenString[OLen+1], Len);
  385. Inc(TokenStr);
  386. Result := ctkSTRING;
  387. end;
  388. function TCSSScanner.DoNumericLiteral :TCSSToken;
  389. Var
  390. TokenStart : PChar;
  391. Len : Integer;
  392. isEscape : Boolean;
  393. begin
  394. Result := ctkINTEGER;
  395. isEscape:=TokenStr[0]='\';
  396. if IsEscape then
  397. Inc(TokenStr);
  398. TokenStart := TokenStr;
  399. while true do
  400. begin
  401. Inc(TokenStr);
  402. case TokenStr[0] of
  403. '.':
  404. if IsEscape then
  405. Break
  406. else
  407. begin
  408. Result := ctkFLOAT;
  409. if TokenStr[1] in ['0'..'9'] then
  410. begin
  411. Inc(TokenStr);
  412. repeat
  413. Inc(TokenStr);
  414. until not (TokenStr[0] in ['0'..'9']);
  415. end;
  416. break;
  417. end;
  418. '0'..'9': ;
  419. else
  420. break;
  421. end;
  422. end;
  423. Len:=TokenStr-TokenStart;
  424. Setlength(FCurTokenString, Len);
  425. if (Len>0) then
  426. Move(TokenStart^,FCurTokenString[1],Len);
  427. if IsEscape then
  428. begin
  429. result:=ctkString;
  430. FCurTokenString:=Char(StrToInt(FCurTokenString));
  431. end;
  432. end;
  433. function TCSSScanner.DoHash :TCSSToken;
  434. Var
  435. TokenStart : PChar;
  436. Len : Integer;
  437. begin
  438. Result := ctkHASH;
  439. TokenStart := TokenStr;
  440. Inc(TokenStr);
  441. while (TokenStr[0]<>'#') and (TokenStr[0] in AlNumIden) do
  442. inc(TokenStr);
  443. Len:=TokenStr-TokenStart;
  444. Setlength(FCurTokenString, Len);
  445. if (Len>0) then
  446. Move(TokenStart^,FCurTokenString[1],Len);
  447. end;
  448. function TCSSScanner.EatBadURL: TCSSToken;
  449. var
  450. TokenStart : PChar;
  451. C : Char;
  452. len,oldlen : integer;
  453. begin
  454. Result:=ctkURL;
  455. While not (TokenStr[0] in [#0,')']) do
  456. begin
  457. TokenStart:=TokenStr;
  458. While not (TokenStr[0] in [#0,')']) do
  459. begin
  460. C:=TokenStr[0];
  461. if (Ord(C)<=Ord(' ')) or (Ord(C)>127) then
  462. Result:=ctkBADURL;
  463. inc(TokenStr);
  464. end;
  465. Len:=TokenStr-TokenStart;
  466. oldLen:=Length(FCurTokenString);
  467. Setlength(FCurTokenString, OldLen+Len);
  468. if (Len>0) then
  469. Move(TokenStart^,FCurTokenString[OldLen+1],Len);
  470. if TokenStr[0]=#0 then
  471. if not FetchLine then
  472. Exit(ctkEOF);
  473. end;
  474. end;
  475. function TCSSScanner.DoUnicodeRange: TCSSTOKEN;
  476. Var
  477. TokenStart:PChar;
  478. Len : Integer;
  479. Tokens : Set of char;
  480. begin
  481. Tokens:= ['A'..'F', 'a'..'f', '0'..'9', '-'];
  482. Result:=ctkUNICODERANGE;
  483. TokenStart := TokenStr;
  484. Inc(TokenStr,2); // U+
  485. repeat
  486. if (TokenStr[0]='-') then
  487. Tokens:=Tokens-['-'];
  488. Inc(TokenStr);
  489. //If (TokenStr[0]='\') and (TokenStr[1]='u') then
  490. until not (TokenStr[0] in Tokens);
  491. Len:=(TokenStr-TokenStart);
  492. SetLength(FCurTokenString,Len);
  493. if Len > 0 then
  494. Move(TokenStart^,FCurTokenString[1],Len);
  495. end;
  496. Class Function TCSSScanner.UnknownCharToStr(C: Char) : String;
  497. begin
  498. if C=#0 then
  499. Result:='EOF'
  500. else if (C in WhiteSpace) then
  501. Result:='#'+IntToStr(Ord(C))
  502. else
  503. Result:='"'+C+'"';
  504. end;
  505. function TCSSScanner.DoIdentifierLike : TCSSToken;
  506. Var
  507. TokenStart:PChar;
  508. Len,oLen : Integer;
  509. IsEscape,IsAt, IsPseudo, IsFunc : Boolean;
  510. begin
  511. Result:=ctkIDENTIFIER;
  512. TokenStart := TokenStr;
  513. IsPseudo:=False;
  514. IsAt:=TokenStr[0]='@';
  515. For Len:=1 to 2 do
  516. if TokenStr[0]=':' then
  517. begin
  518. IsPseudo:=True;
  519. Inc(TokenStr);
  520. end;
  521. Repeat
  522. if not (TokenStr[0]='\') then
  523. repeat
  524. Inc(TokenStr);
  525. //If (TokenStr[0]='\') and (TokenStr[1]='u') then
  526. until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','-','$']);
  527. IsEscape:=TokenStr[0]='\';
  528. if IsEscape then
  529. begin
  530. if ((TokenStr[0] in WhiteSpace) or (TokenStr[0]=#0)) then
  531. DoError(SErrUnknownCharacter ,[UnknownCharToStr(TokenStr[0])])
  532. end
  533. else if not IsAt then
  534. begin
  535. IsFunc:=TokenStr[0]='(';
  536. if IsFunc then
  537. Inc(TokenStr);
  538. end;
  539. Len:=(TokenStr-TokenStart);
  540. oLen:=Length(FCurTokenString);
  541. SetLength(FCurTokenString,Olen+Len);
  542. if Len > 0 then
  543. Move(TokenStart^,FCurTokenString[Olen+1],Len);
  544. if IsEscape then
  545. Inc(TokenStr);
  546. TokenStart := TokenStr;
  547. until Not IsEscape;
  548. // Some specials
  549. if (CurTokenString[1]='.') and not IsFunc then
  550. Result:=ctkCLASSNAME
  551. else if isAt then
  552. Result:=ctkATKEYWORD
  553. else if CurTokenString='!important' then
  554. Result:=ctkIMPORTANT
  555. else if (CurtokenString='url(') then
  556. begin
  557. Result:=ctkURL;
  558. If TokenStr[0] in ['"',''''] then
  559. DoStringLiteral
  560. else
  561. begin
  562. result:=EatBadURL;
  563. end;
  564. If (result<>ctkEOF) and (TokenStr[0] in [')']) then
  565. Inc(TokenStr);
  566. end
  567. else if IsPseudo then
  568. begin
  569. if IsFunc then
  570. Result:=ctkPSEUDOFUNCTION
  571. else
  572. Result:=ctkPSEUDO;
  573. end
  574. else if IsFunc then
  575. Result:=ctkFUNCTION;
  576. end;
  577. function TCSSScanner.FetchToken: TCSSToken;
  578. var
  579. CanStop : Boolean;
  580. begin
  581. Repeat
  582. Result:=DoFetchToken;
  583. CanStop:=(Not (Result in [ctkComment,ctkWhiteSpace]))
  584. or ((ReturnComments and (Result=ctkComment))
  585. or
  586. (ReturnWhiteSpace and (Result=ctkWhiteSpace))
  587. )
  588. Until CanStop;
  589. end;
  590. function TCSSScanner.DoFetchToken: TCSSToken;
  591. Procedure CharToken(aToken : TCSSToken); inline;
  592. begin
  593. FCurTokenString:=TokenStr[0];
  594. Inc(TokenStr);
  595. Result:=aToken;
  596. end;
  597. begin
  598. if TokenStr = nil then
  599. begin
  600. if not FetchLine then
  601. begin
  602. Result := ctkEOF;
  603. FCurToken := Result;
  604. exit;
  605. end;
  606. end;
  607. //CurPos:=TokenStr;
  608. FCurTokenString := '';
  609. case TokenStr[0] of
  610. #0: // Empty line
  611. begin
  612. FetchLine;
  613. Result := ctkWhitespace;
  614. end;
  615. '''','"':
  616. Result:=DoStringLiteral;
  617. '/' :
  618. Result:=CommentDiv;
  619. #9, ' ':
  620. Result := DoWhiteSpace;
  621. '#':
  622. Result:=DoHash;
  623. '\':
  624. begin
  625. if TokenStr[1] in ['0'..'9'] then
  626. Result:=DoNumericLiteral
  627. else
  628. begin
  629. if (TokenStr[1] in WhiteSpace) or (TokenStr[1]=#0) then
  630. DoError(SErrUnknownCharacter ,[UnknownCharToStr(TokenStr[1])])
  631. else
  632. Result:=DoIdentifierLike
  633. end;
  634. end;
  635. '0'..'9':
  636. Result:=DoNumericLiteral;
  637. '&': CharToken(ctkAnd);
  638. '{': CharToken( ctkLBRACE);
  639. '}': CharToken(ctkRBRACE);
  640. '*': if Not (csoExtendedIdentifiers in Options) then
  641. CharToken(ctkSTAR)
  642. else if TokenStr[1] in AlNumIden then
  643. Result:=DoIdentifierLike
  644. else
  645. CharToken(ctkSTAR);
  646. '^': CharToken(ctkSQUARED);
  647. ',': CharToken(ctkCOMMA);
  648. '~': CharToken(ctkTILDE);
  649. ';': CharToken(ctkSEMICOLON);
  650. '@': Result:=DoIdentifierLike;
  651. ':':
  652. begin
  653. if DisablePseudo then
  654. CharToken(ctkCOLON)
  655. else if (TokenStr[1]=':') then
  656. begin
  657. if (TokenStr[2] in AlNumIden) then
  658. Result:=DoIdentifierLike
  659. else
  660. Result:=ctkDoubleCOLON
  661. end
  662. else if (TokenStr[1] in AlNumIden) then
  663. Result:=DoIdentifierLike
  664. else
  665. CharToken(ctkCOLON);
  666. end;
  667. '.':
  668. begin
  669. if (TokenStr[1] in AlNum) then
  670. Result:=Self.DoIdentifierLike
  671. else
  672. CharToken(ctkDOT);
  673. end;
  674. '>': CharToken(ctkGT);
  675. '<': CharToken(ctkLT);
  676. '(': CharToken(ctkLPARENTHESIS);
  677. ')': CharToken(ctkRPARENTHESIS);
  678. '[': CharToken(ctkLBRACKET);
  679. ']': CharToken(ctkRBRACKET);
  680. '=': CharToken(ctkEQUALS);
  681. '-':
  682. begin
  683. if (TokenStr[1] in ['0'..'9']) then
  684. Result:=DoNumericLiteral
  685. else if Not (TokenStr[1] in WhiteSpaceEx) then
  686. Result:=DoIdentifierLike
  687. else
  688. CharToken(ctkMINUS);
  689. end;
  690. '+': CharToken(ctkPLUS);
  691. '%': CharToken(ctkPERCENTAGE);
  692. '_','!',
  693. 'a'..'z',
  694. 'A'..'Z':
  695. begin
  696. if (TokenStr[0] in ['u','U']) and (TokenStr[1]='+') then
  697. Result:=DoUnicodeRange
  698. else
  699. Result:=DoIdentifierLike;
  700. end;
  701. else
  702. If Ord(TokenStr[0])>127 then
  703. Result:=DoIdentifierLike
  704. else
  705. DoError(SErrUnknownCharacter ,['"'+TokenStr[0]+'"']);
  706. end; // Case
  707. end;
  708. procedure TCSSScanner.DoError(const Msg: UTF8String; Args: array of const);
  709. begin
  710. DoError(Format(Msg,Args));
  711. end;
  712. procedure TCSSScanner.DoError(const Msg: UTF8String);
  713. Var
  714. S : UTF8String;
  715. begin
  716. S:=Format('Error at (%d,%d): ',[CurRow,CurColumn])+Msg;
  717. Raise ECSSScanner.Create(S);
  718. end;
  719. function TCSSScanner.GetCurColumn: Integer;
  720. begin
  721. if (TokenStr=Nil) or (Length(CurLine)=0) then
  722. Result:=0
  723. else
  724. Result := TokenStr - PChar(CurLine);
  725. end;
  726. function TCSSScanner.GetReturnComments: Boolean;
  727. begin
  728. Result:=(csoReturnComments in FOptions);
  729. end;
  730. function TCSSScanner.GetReturnWhiteSpace: Boolean;
  731. begin
  732. Result:=(csoReturnWhiteSpace in FOptions);
  733. end;
  734. { TStreamLineReader }
  735. constructor TStreamLineReader.Create(AStream: TStream);
  736. begin
  737. FStream:=AStream;
  738. FBufPos:=0;
  739. FBufLen:=0;
  740. end;
  741. function TStreamLineReader.IsEOF: Boolean;
  742. begin
  743. Result:=(FBufPos>=FBufLen);
  744. If Result then
  745. begin
  746. FillBuffer;
  747. Result:=(FBufLen=0);
  748. end;
  749. end;
  750. procedure TStreamLineReader.FillBuffer;
  751. begin
  752. FBufLen:=FStream.Read(Buffer,SizeOf(Buffer)-1);
  753. Buffer[FBufLen]:=0;
  754. FBufPos:=0;
  755. end;
  756. function TStreamLineReader.ReadLine: UTF8String;
  757. Var
  758. FPos,OLen,Len: Integer;
  759. PRun : PByte;
  760. begin
  761. Result:='';
  762. FPos:=FBufPos;
  763. Repeat
  764. PRun:=@Buffer[FBufPos];
  765. While (FBufPos<FBufLen) and Not (PRun^ in [10,13]) do
  766. begin
  767. Inc(PRun);
  768. Inc(FBufPos);
  769. end;
  770. If (FBufPos=FBufLen) then
  771. begin
  772. Len:=FBufPos-FPos;
  773. If (Len>0) then
  774. begin
  775. Olen:=Length(Result);
  776. SetLength(Result,OLen+Len);
  777. Move(Buffer[FPos],Result[OLen+1],Len);
  778. end;
  779. FillBuffer;
  780. FPos:=FBufPos;
  781. end;
  782. until (FBufPos=FBufLen) or (PRun^ in [10,13]);
  783. Len:=FBufPos-FPos;
  784. If (Len>0) then
  785. begin
  786. Olen:=Length(Result);
  787. SetLength(Result,OLen+Len);
  788. Move(Buffer[FPos],Result[OLen+1],Len)
  789. end;
  790. If (PRun^ in [10,13]) and (FBufPos<FBufLen) then
  791. begin
  792. Inc(FBufPos);
  793. // Check #13#10
  794. If (PRun^=13) then
  795. begin
  796. If (FBufPos=FBufLen) then
  797. FillBuffer;
  798. If (FBufPos<FBufLen) and (Buffer[FBufpos]=10) then
  799. Inc(FBufPos);
  800. end;
  801. end;
  802. end;
  803. end.