2
0

fpcssparser.pp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  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 a CSS parser
  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 fpCSSParser;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. TypInfo, Classes, SysUtils, fpcsstree, fpcssscanner;
  16. Type
  17. ECSSParser = Class(Exception);
  18. { TCSSParser }
  19. TCSSParser = class(TObject)
  20. private
  21. FInput : TStream;
  22. FScanner: TCSSScanner;
  23. FPrevious : TCSSToken;
  24. FCurrent : TCSSToken;
  25. FCurrentTokenString : TCSSString;
  26. FPeekToken : TCSSToken;
  27. FPeekTokenString : TCSSString;
  28. FFreeScanner : Boolean;
  29. FRuleLevel : Integer;
  30. function CreateElement(aClass: TCSSElementClass): TCSSElement;
  31. class function GetAppendElement(aList: TCSSListElement): TCSSElement;
  32. function GetAtEOF: Boolean;
  33. function GetCurSource: TCSSString;
  34. Function GetCurLine : Integer;
  35. Function GetCurPos : Integer;
  36. protected
  37. Procedure DoError(Msg : TCSSString);
  38. Procedure DoError(Fmt : TCSSString; const Args : Array of const);
  39. Procedure Consume(aToken : TCSSToken);
  40. Procedure SkipWhiteSpace;
  41. function ParseComponentValueList(AllowRules: Boolean=True): TCSSElement;
  42. function ParseComponentValue: TCSSElement;
  43. function ParseExpression: TCSSElement;
  44. function ParseRule: TCSSElement;
  45. function ParseAtRule: TCSSElement;
  46. function ParseAtMediaRule: TCSSAtRuleElement;
  47. function ParseMediaCondition: TCSSElement;
  48. function ParseRuleList(aStopOn : TCSStoken = ctkEOF): TCSSElement;
  49. function ParseSelector: TCSSElement;
  50. function ParseAttributeSelector: TCSSElement;
  51. function ParseWQName: TCSSElement;
  52. function ParseDeclaration(aIsAt : Boolean = false): TCSSDeclarationElement;
  53. function ParseCall(aName: TCSSString): TCSSElement;
  54. function ParseUnary: TCSSElement;
  55. function ParseUnit: TCSSUnits;
  56. function ParseIdentifier : TCSSIdentifierElement;
  57. function ParseHashIdentifier : TCSSHashIdentifierElement;
  58. function ParseClassName : TCSSClassNameElement;
  59. function ParseParenthesis: TCSSElement;
  60. function ParsePseudo: TCSSElement;
  61. Function ParseRuleBody(aRule: TCSSRuleElement; aIsAt : Boolean = False) : integer;
  62. function ParseInteger: TCSSElement;
  63. function ParseFloat: TCSSElement;
  64. function ParseString: TCSSElement;
  65. Function ParseUnicodeRange : TCSSElement;
  66. function ParseArray(aPrefix: TCSSElement): TCSSElement;
  67. function ParseURL: TCSSElement;
  68. Property CurrentSource : TCSSString Read GetCurSource;
  69. Property CurrentLine : Integer Read GetCurLine;
  70. Property CurrentPos : Integer Read GetCurPos;
  71. Public
  72. Constructor Create(AInput: TStream; ExtraScannerOptions : TCSSScannerOptions = []);
  73. Constructor Create(AScanner : TCSSScanner); virtual;
  74. Destructor Destroy; override;
  75. Function Parse : TCSSElement;
  76. Property CurrentToken : TCSSToken Read FCurrent;
  77. Property CurrentTokenString : TCSSString Read FCurrentTokenString;
  78. Function GetNextToken : TCSSToken;
  79. Function PeekNextToken : TCSSToken;
  80. Property Scanner : TCSSScanner Read FScanner;
  81. Property atEOF : Boolean Read GetAtEOF;
  82. end;
  83. Function TokenToBinaryOperation(aToken : TCSSToken) : TCSSBinaryOperation;
  84. Function TokenToUnaryOperation(aToken : TCSSToken) : TCSSUnaryOperation;
  85. implementation
  86. Resourcestring
  87. SBinaryInvalidToken = 'Invalid token for binary operation: %s';
  88. SUnaryInvalidToken = 'Invalid token for unary operation: %s';
  89. SErrFileSource = 'Error: file "%s" line %d, pos %d: ';
  90. SErrSource = 'Error: line %d, pos %d: ';
  91. SErrUnexpectedToken = 'Unexpected token: Got %s (as string: "%s"), expected: %s ';
  92. SErrInvalidFloat = 'Invalid float: %s';
  93. SErrUnexpectedEndOfFile = 'Unexpected EOF while scanning function args: %s';
  94. Function TokenToBinaryOperation(aToken : TCSSToken) : TCSSBinaryOperation;
  95. begin
  96. Case aToken of
  97. ctkEquals : Result:=boEquals;
  98. ctkPlus : Result:=boPlus;
  99. ctkMinus: Result:=boMinus;
  100. ctkAnd : result:=boAnd;
  101. ctkGE : Result:=boGE;
  102. ctkGT : Result:=boGT;
  103. ctkLE : Result:=boLE;
  104. ctkLT : Result:=boLT;
  105. ctkDIV : Result:=boDIV;
  106. ctkStar : Result:=boStar;
  107. ctkSTAREQUAL : Result:=boStarEqual;
  108. ctkTilde : Result:=boTilde;
  109. ctkTILDEEQUAL : Result:=boTildeEqual;
  110. ctkSquared : Result:=boSquared;
  111. ctkSQUAREDEQUAL : Result:=boSquaredEqual;
  112. ctkPIPE : Result:=boPipe;
  113. ctkPIPEEQUAL : Result:=boPipeEqual;
  114. ctkDOLLAR : Result:=boDollar;
  115. ctkDOLLAREQUAL : Result:=boDollarEqual;
  116. ctkColon : Result:=boCOLON;
  117. ctkDoubleColon : Result:=boDoubleColon;
  118. else
  119. Raise ECSSParser.CreateFmt(SBinaryInvalidToken,[GetEnumName(TypeInfo(aToken),Ord(aToken))]);
  120. // Result:=boEquals;
  121. end;
  122. end;
  123. Function TokenToUnaryOperation(aToken : TCSSToken) : TCSSUnaryOperation;
  124. begin
  125. Case aToken of
  126. ctkDOUBLECOLON: Result:=uoDoubleColon;
  127. ctkMinus: Result:=uoMinus;
  128. ctkPlus: Result:=uoPlus;
  129. ctkDiv: Result:=uoDiv;
  130. else
  131. Raise ECSSParser.CreateFmt(SUnaryInvalidToken,[GetEnumName(TypeInfo(aToken),Ord(aToken))]);
  132. end;
  133. end;
  134. { TCSSParser }
  135. function TCSSParser.GetAtEOF: Boolean;
  136. begin
  137. Result:=(CurrentToken=ctkEOF);
  138. end;
  139. procedure TCSSParser.DoError(Msg: TCSSString);
  140. Var
  141. ErrAt : TCSSString;
  142. begin
  143. If Assigned(FScanner) then
  144. If FScanner.CurFilename<>'' then
  145. ErrAt:=Format(SErrFileSource,[FScanner.CurFileName,FScanner.CurRow,FScanner.CurColumn])
  146. else
  147. ErrAt:=Format(SErrSource,[FScanner.Currow,FScanner.CurColumn]);
  148. Raise ECSSParser.Create(ErrAt+Msg)
  149. end;
  150. procedure TCSSParser.DoError(Fmt: TCSSString; const Args: array of const);
  151. begin
  152. DoError(Format(Fmt,Args));
  153. end;
  154. procedure TCSSParser.Consume(aToken: TCSSToken);
  155. begin
  156. if CurrentToken<>aToken then
  157. DoError(SErrUnexpectedToken ,[
  158. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  159. CurrentTokenString,
  160. GetEnumName(TypeInfo(TCSSToken),Ord(aToken))
  161. ]);
  162. GetNextToken;
  163. end;
  164. procedure TCSSParser.SkipWhiteSpace;
  165. begin
  166. while CurrentToken=ctkWHITESPACE do
  167. GetNextToken;
  168. end;
  169. function TCSSParser.GetCurSource: TCSSString;
  170. begin
  171. If Assigned(FScanner) then
  172. Result:=FScanner.CurFileName
  173. else
  174. Result:='';
  175. end;
  176. function TCSSParser.GetCurLine: Integer;
  177. begin
  178. if Assigned(FScanner) then
  179. Result:=FScanner.CurRow
  180. else
  181. Result:=0;
  182. end;
  183. function TCSSParser.GetCurPos: Integer;
  184. begin
  185. if Assigned(FScanner) then
  186. Result:=FScanner.CurColumn
  187. else
  188. Result:=0;
  189. end;
  190. constructor TCSSParser.Create(AInput: TStream; ExtraScannerOptions : TCSSScannerOptions = []);
  191. begin
  192. FInput:=AInput;
  193. Create(TCSSScanner.Create(FInput));
  194. FScanner.Options:=FScanner.Options+ExtraScannerOptions;
  195. FFreeScanner:=True;
  196. end;
  197. constructor TCSSParser.Create(AScanner: TCSSScanner);
  198. begin
  199. FCurrent:=ctkUNKNOWN;
  200. FPeekToken:=ctkUNKNOWN;
  201. FPeekTokenString:='';
  202. FScanner:=aScanner;
  203. end;
  204. destructor TCSSParser.Destroy;
  205. begin
  206. if FFreeScanner then
  207. FreeAndNil(FScanner);
  208. inherited Destroy;
  209. end;
  210. class function TCSSParser.GetAppendElement(aList: TCSSListElement): TCSSElement;
  211. begin
  212. Case aList.ChildCount of
  213. 0 : Result:=Nil;
  214. 1 : Result:=aList.ExtractElement(0);
  215. else
  216. Result:=aList;
  217. end;
  218. if Result<>aList then
  219. aList.Free;
  220. end;
  221. function TCSSParser.ParseAtRule: TCSSElement;
  222. // read unknown at-rule
  223. Var
  224. aRule : TCSSRuleElement;
  225. aSel : TCSSElement;
  226. Term : TCSSTokens;
  227. aLast : TCSSToken;
  228. aList : TCSSListElement;
  229. {$ifdef VerboseCSSParser}
  230. aAt : TCSSString;
  231. {$endif}
  232. begin
  233. Inc(FRuleLevel);
  234. {$ifdef VerboseCSSParser}
  235. aAt:=Format(' Level %d at (%d:%d)',[FRuleLevel,CurrentLine,CurrentPos]);
  236. Writeln('Parse @ rule');
  237. {$endif}
  238. Term:=[ctkLBRACE,ctkEOF,ctkSEMICOLON];
  239. aRule:=TCSSAtRuleElement(CreateElement(TCSSAtRuleElement));
  240. TCSSAtRuleElement(aRule).AtKeyWord:=CurrentTokenString;
  241. GetNextToken;
  242. aList:=nil;
  243. try
  244. aList:=TCSSListElement(CreateElement(TCSSListElement));
  245. While Not (CurrentToken in Term) do
  246. begin
  247. aSel:=ParseComponentValue;
  248. aList.AddChild(aSel);
  249. if CurrentToken=ctkCOMMA then
  250. begin
  251. Consume(ctkCOMMA);
  252. aRule.AddSelector(GetAppendElement(aList));
  253. aList:=TCSSListElement(CreateElement(TCSSListElement));
  254. end;
  255. end;
  256. aRule.AddSelector(GetAppendElement(aList));
  257. aList:=nil;
  258. aLast:=CurrentToken;
  259. if (aLast<>ctkSEMICOLON) then
  260. begin
  261. Consume(ctkLBRACE);
  262. aRule.AddChild(ParseRuleList(ctkRBRACE));
  263. Consume(ctkRBRACE);
  264. end;
  265. Result:=aRule;
  266. aRule:=nil;
  267. {$ifdef VerboseCSSParser} Writeln('Done Parse @ rule ',aAt); {$endif}
  268. Inc(FRuleLevel);
  269. finally
  270. aRule.Free;
  271. end;
  272. end;
  273. function TCSSParser.ParseAtMediaRule: TCSSAtRuleElement;
  274. Var
  275. aRule : TCSSAtRuleElement;
  276. Term : TCSSTokens;
  277. aLast , aToken: TCSSToken;
  278. aList : TCSSListElement;
  279. {$ifdef VerboseCSSParser}
  280. aAt : TCSSString;
  281. {$endif}
  282. begin
  283. Inc(FRuleLevel);
  284. {$ifdef VerboseCSSParser}
  285. aAt:=Format(' Level %d at (%d:%d)',[FRuleLevel,CurrentLine,CurrentPos]);
  286. Writeln('Parse @media rule');
  287. {$endif}
  288. Term:=[ctkLBRACE,ctkEOF,ctkSEMICOLON];
  289. aRule:=TCSSAtRuleElement(CreateElement(TCSSAtRuleElement));
  290. TCSSAtRuleElement(aRule).AtKeyWord:=CurrentTokenString;
  291. GetNextToken;
  292. aList:=nil;
  293. try
  294. aList:=TCSSListElement(CreateElement(TCSSListElement));
  295. While Not (CurrentToken in Term) do
  296. begin
  297. aToken:=CurrentToken;
  298. writeln('TCSSParser.ParseAtMediaRule Token=',CurrentToken);
  299. case aToken of
  300. ctkIDENTIFIER:
  301. aList.AddChild(ParseIdentifier);
  302. ctkLPARENTHESIS:
  303. aList.AddChild(ParseMediaCondition);
  304. else
  305. Consume(ctkIDENTIFIER);
  306. end;
  307. if CurrentToken=ctkCOMMA then
  308. begin
  309. Consume(ctkCOMMA);
  310. aRule.AddSelector(GetAppendElement(aList));
  311. aList:=TCSSListElement(CreateElement(TCSSListElement));
  312. end;
  313. end;
  314. aRule.AddSelector(GetAppendElement(aList));
  315. aList:=nil;
  316. aLast:=CurrentToken;
  317. if (aLast<>ctkSEMICOLON) then
  318. begin
  319. Consume(ctkLBRACE);
  320. aRule.AddChild(ParseRuleList(ctkRBRACE));
  321. Consume(ctkRBRACE);
  322. end;
  323. Result:=aRule;
  324. aRule:=nil;
  325. {$ifdef VerboseCSSParser} Writeln('Done Parse @ rule ',aAt); {$endif}
  326. Inc(FRuleLevel);
  327. finally
  328. aRule.Free;
  329. end;
  330. end;
  331. function TCSSParser.ParseMediaCondition: TCSSElement;
  332. // for example:
  333. // (color)
  334. // (color: #fff)
  335. // (30em <= width)
  336. // (30em >= width > 20em)
  337. // (not(MediaCondition))
  338. var
  339. El: TCSSElement;
  340. Bin: TCSSBinaryElement;
  341. List: TCSSListElement;
  342. aToken: TCSSToken;
  343. begin
  344. Consume(ctkLPARENTHESIS);
  345. {$IFDEF VerboseCSSParser}
  346. writeln('TCSSParser.ParseMediaCondition START ',CurrentToken);
  347. {$ENDIF}
  348. El:=nil;
  349. Bin:=nil;
  350. List:=nil;
  351. try
  352. case CurrentToken of
  353. ctkIDENTIFIER:
  354. begin
  355. El:=ParseIdentifier;
  356. if TCSSIdentifierElement(El).Value='not' then
  357. begin
  358. // (not(mediacondition))
  359. List:=TCSSListElement(CreateElement(TCSSListElement));
  360. List.AddChild(El);
  361. El:=nil;
  362. List.AddChild(ParseMediaCondition());
  363. Result:=List;
  364. List:=nil;
  365. exit;
  366. end
  367. else if CurrentToken=ctkCOLON then
  368. begin
  369. // (mediaproperty: value)
  370. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  371. Bin.Left:=El;
  372. El:=nil;
  373. Consume(ctkCOLON);
  374. Bin.Right:=ParseComponentValue;
  375. Consume(ctkRPARENTHESIS);
  376. Result:=Bin;
  377. Bin:=nil;
  378. exit;
  379. end;
  380. end;
  381. ctkSTRING:
  382. El:=ParseString;
  383. ctkINTEGER:
  384. El:=ParseInteger;
  385. ctkFLOAT:
  386. El:=ParseFloat;
  387. else
  388. Consume(ctkIDENTIFIER);
  389. end;
  390. // read binaryoperator operand til bracket close
  391. repeat
  392. aToken:=CurrentToken;
  393. {$IFDEF VerboseCSSResolver}
  394. writeln('TCSSParser.ParseMediaCondition NEXT ',CurrentToken);
  395. {$ENDIF}
  396. case aToken of
  397. ctkRPARENTHESIS:
  398. begin
  399. Result:=El;
  400. GetNextToken;
  401. break;
  402. end;
  403. ctkEQUALS,
  404. ctkGE,ctkGT,ctkLE,ctkLT:
  405. begin
  406. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  407. Bin.Left:=El;
  408. Bin.Operation:=TokenToBinaryOperation(aToken);
  409. GetNextToken;
  410. end;
  411. else
  412. Consume(ctkRPARENTHESIS);
  413. end;
  414. case CurrentToken of
  415. ctkIDENTIFIER:
  416. Bin.Right:=ParseIdentifier;
  417. ctkSTRING:
  418. Bin.Right:=ParseString;
  419. ctkINTEGER:
  420. Bin.Right:=ParseInteger;
  421. ctkFLOAT:
  422. Bin.Right:=ParseFloat;
  423. else
  424. Consume(ctkIDENTIFIER);
  425. end;
  426. El:=Bin;
  427. Bin:=nil;
  428. until false;
  429. finally
  430. List.Free;
  431. Bin.Free;
  432. El.Free;
  433. end;
  434. {$IFDEF VerboseCSSParser}
  435. writeln('TCSSParser.ParseMediaCondition END');
  436. {$ENDIF}
  437. end;
  438. function TCSSParser.ParseExpression: TCSSElement;
  439. Function AllowRules(const aName : TCSSString) : Boolean;
  440. begin
  441. Result:=sameText(aName,'@print');
  442. end;
  443. Const
  444. RuleTokens =
  445. [ctkIDENTIFIER,ctkCLASSNAME,ctkHASH,ctkINTEGER,
  446. ctkPSEUDO,ctkPSEUDOFUNCTION,
  447. ctkCOLON,ctkDOUBLECOLON,ctkSTAR,ctkTILDE,ctkLBRACKET];
  448. begin
  449. if CurrentToken in RuleTokens then
  450. Result:=ParseRule
  451. else if CurrentToken=ctkATKEYWORD then
  452. begin
  453. if SameText(CurrentTokenString,'@media') then
  454. Result:=ParseAtMediaRule
  455. else
  456. Result:=ParseAtRule;
  457. end
  458. else
  459. Result:=ParseComponentValueList;
  460. end;
  461. function TCSSParser.ParseRuleList(aStopOn : TCSStoken = ctkEOF): TCSSElement;
  462. Var
  463. aList : TCSSCompoundElement;
  464. aEl : TCSSElement;
  465. Terms : TCSSTokens;
  466. begin
  467. Terms:=[ctkEOF,aStopOn];
  468. aList:=TCSSCompoundElement(CreateElement(TCSSCompoundElement));
  469. Try
  470. While not (CurrentToken in Terms) do
  471. begin
  472. aEl:=ParseExpression;
  473. aList.AddChild(aEl);
  474. if CurrentToken=ctkSEMICOLON then
  475. Consume(ctkSEMICOLON);
  476. end;
  477. Result:=aList;
  478. aList:=nil;
  479. finally
  480. aList.Free;
  481. end;
  482. end;
  483. function TCSSParser.Parse: TCSSElement;
  484. begin
  485. GetNextToken;
  486. if CurrentToken=ctkLBRACE then
  487. Result:=ParseRule
  488. else
  489. Result:=ParseRuleList;
  490. end;
  491. function TCSSParser.GetNextToken: TCSSToken;
  492. begin
  493. FPrevious:=FCurrent;
  494. If (FPeekToken<>ctkUNKNOWN) then
  495. begin
  496. FCurrent:=FPeekToken;
  497. FCurrentTokenString:=FPeekTokenString;
  498. FPeekToken:=ctkUNKNOWN;
  499. FPeekTokenString:='';
  500. end
  501. else
  502. begin
  503. FCurrent:=FScanner.FetchToken;
  504. FCurrentTokenString:=FScanner.CurTokenString;
  505. end;
  506. Result:=FCurrent;
  507. {$ifdef VerboseCSSParser}
  508. Writeln('GetNextToken returns ',
  509. GetEnumName(TypeInfo(TCSSToken),Ord(FCurrent)),
  510. '(String: "',FCurrentTokenString,'")',
  511. ' at (',FScanner.CurRow,',',FScanner.CurColumn,'): ',
  512. FSCanner.CurLine);
  513. {$endif VerboseCSSParser}
  514. end;
  515. function TCSSParser.PeekNextToken: TCSSToken;
  516. begin
  517. If (FPeekToken=ctkUNKNOWN) then
  518. begin
  519. FPeekToken:=FScanner.FetchToken;
  520. FPeekTokenString:=FScanner.CurTokenString;
  521. end;
  522. {$ifdef VerboseCSSParser}Writeln('PeekNextToken : ',GetEnumName(TypeInfo(TCSSToken),Ord(FPeekToken)), ' As TCSSString: ',FPeekTokenString);{$endif VerboseCSSParser}
  523. Result:=FPeekToken;
  524. end;
  525. function TCSSParser.ParseUnit : TCSSUnits;
  526. begin
  527. Result:=cuNone;
  528. if (CurrentToken in [ctkIDENTIFIER,ctkPERCENTAGE]) then
  529. begin
  530. Case currentTokenString of
  531. '%' : Result:=cuPERCENT;
  532. 'px' : Result:=cuPX;
  533. 'rem' : Result:=cuREM;
  534. 'em' : Result:=cuEM;
  535. 'fr' : Result:=cuFR;
  536. 'vw' : Result:=cuVW;
  537. 'vh' : Result:=cuVH;
  538. 'pt' : Result:=cuPT;
  539. 'deg' : Result:=cuDEG;
  540. else
  541. // Ignore. For instance margin: 0 auto
  542. end;
  543. if Result<>cuNone then
  544. Consume(CurrentToken);
  545. end;
  546. end;
  547. function TCSSParser.CreateElement(aClass : TCSSElementClass): TCSSElement;
  548. begin
  549. Result:=aClass.Create(CurrentSource,CurrentLine,CurrentPos);
  550. end;
  551. function TCSSParser.ParseIdentifier: TCSSIdentifierElement;
  552. Var
  553. aValue : TCSSString;
  554. begin
  555. aValue:=CurrentTokenString;
  556. Result:=TCSSIdentifierElement(CreateElement(TCSSIdentifierElement));
  557. Result.Value:=aValue;
  558. GetNextToken;
  559. end;
  560. function TCSSParser.ParseHashIdentifier: TCSSHashIdentifierElement;
  561. Var
  562. aValue : TCSSString;
  563. begin
  564. aValue:=CurrentTokenString;
  565. system.delete(aValue,1,1);
  566. Result:=TCSSHashIdentifierElement(CreateElement(TCSSHashIdentifierElement));
  567. Result.Value:=aValue;
  568. GetNextToken;
  569. end;
  570. function TCSSParser.ParseClassName: TCSSClassNameElement;
  571. Var
  572. aValue : TCSSString;
  573. begin
  574. aValue:=CurrentTokenString;
  575. system.delete(aValue,1,1);
  576. Result:=TCSSClassNameElement(CreateElement(TCSSClassNameElement));
  577. Result.Value:=aValue;
  578. GetNextToken;
  579. end;
  580. function TCSSParser.ParseInteger: TCSSElement;
  581. Var
  582. aValue : Integer;
  583. aInt : TCSSIntegerElement;
  584. begin
  585. aValue:=StrToInt(CurrentTokenString);
  586. aInt:=TCSSIntegerElement(CreateElement(TCSSIntegerElement));
  587. try
  588. aInt.Value:=aValue;
  589. Consume(ctkINTEGER);
  590. aInt.Units:=ParseUnit;
  591. Result:=aInt;
  592. aInt:=nil;
  593. finally
  594. aInt.Free;
  595. end;
  596. end;
  597. function TCSSParser.ParseFloat: TCSSElement;
  598. Var
  599. aCode : Integer;
  600. aValue : Double;
  601. aFloat : TCSSFloatElement;
  602. begin
  603. Val(CurrentTokenString,aValue,aCode);
  604. if aCode<>0 then
  605. DoError(SErrInvalidFloat,[CurrentTokenString]);
  606. aFloat:=TCSSFloatElement(CreateElement(TCSSFloatElement));
  607. try
  608. Consume(ctkFloat);
  609. aFloat.Value:=aValue;
  610. aFloat.Units:=ParseUnit;
  611. Result:=aFloat;
  612. aFloat:=nil;
  613. finally
  614. aFloat.Free;
  615. end;
  616. end;
  617. function TCSSParser.ParseParenthesis: TCSSElement;
  618. var
  619. aList: TCSSElement;
  620. begin
  621. Consume(ctkLPARENTHESIS);
  622. aList:=ParseComponentValueList;
  623. try
  624. Consume(ctkRPARENTHESIS);
  625. Result:=aList;
  626. aList:=nil;
  627. finally
  628. aList.Free;
  629. end;
  630. end;
  631. function TCSSParser.ParseURL: TCSSElement;
  632. Var
  633. aURL : TCSSURLElement;
  634. begin
  635. aURL:=TCSSURLElement(CreateElement(TCSSURLElement));
  636. try
  637. aURL.Value:=CurrentTokenString;
  638. if CurrentToken=ctkURL then
  639. Consume(ctkURL)
  640. else
  641. Consume(ctkBADURL);
  642. Result:=aURL;
  643. aURL:=nil;
  644. finally
  645. aURL.Free;
  646. end;
  647. end;
  648. function TCSSParser.ParsePseudo: TCSSElement;
  649. Var
  650. aPseudo : TCSSPseudoClassElement;
  651. aValue : TCSSString;
  652. begin
  653. aValue:=CurrentTokenString;
  654. aPseudo:=TCSSPseudoClassElement(CreateElement(TCSSPseudoClassElement));
  655. try
  656. Consume(ctkPseudo);
  657. aPseudo.Value:=aValue;
  658. Result:=aPseudo;
  659. aPseudo:=nil;
  660. finally
  661. aPseudo.Free;
  662. end;
  663. end;
  664. function TCSSParser.ParseRuleBody(aRule: TCSSRuleElement; aIsAt: Boolean = false): integer;
  665. Var
  666. aDecl : TCSSElement;
  667. begin
  668. aDecl:=nil;
  669. if not (CurrentToken in [ctkRBRACE,ctkSEMICOLON]) then
  670. begin
  671. aDecl:=ParseDeclaration(aIsAt);
  672. aRule.AddChild(aDecl);
  673. end;
  674. While Not (CurrentToken in [ctkEOF,ctkRBRACE]) do
  675. begin
  676. While CurrentToken=ctkSEMICOLON do
  677. Consume(ctkSEMICOLON);
  678. if Not (CurrentToken in [ctkEOF,ctkRBRACE]) then
  679. begin
  680. if CurrentToken=ctkATKEYWORD then
  681. aDecl:=ParseAtRule
  682. else
  683. aDecl:=ParseDeclaration(aIsAt);
  684. aRule.AddChild(aDecl);
  685. end;
  686. end;
  687. Result:=aRule.ChildCount;
  688. end;
  689. function TCSSParser.ParseRule: TCSSElement;
  690. Var
  691. aRule : TCSSRuleElement;
  692. aSel : TCSSElement;
  693. Term : TCSSTokens;
  694. aLast : TCSSToken;
  695. aList: TCSSListElement;
  696. {$IFDEF VerboseCSSParser}
  697. aAt : TCSSString;
  698. {$ENDIF}
  699. begin
  700. Inc(FRuleLevel);
  701. {$IFDEF VerboseCSSParser}
  702. aAt:=Format(' Level %d at (%d:%d)',[FRuleLevel,CurrentLine,CurrentPos]);
  703. Writeln('Parse rule.: ',aAt);
  704. {$ENDIF}
  705. case CurrentToken of
  706. ctkEOF: exit(nil);
  707. ctkSEMICOLON:
  708. begin
  709. Result:=TCSSRuleElement(CreateElement(TCSSRuleElement));
  710. exit;
  711. end;
  712. end;
  713. Term:=[ctkLBRACE,ctkEOF,ctkSEMICOLON];
  714. aRule:=TCSSRuleElement(CreateElement(TCSSRuleElement));
  715. aList:=nil;
  716. try
  717. aList:=TCSSListElement(CreateElement(TCSSListElement));
  718. While Not (CurrentToken in Term) do
  719. begin
  720. aSel:=ParseSelector;
  721. aRule.AddSelector(aSel);
  722. if CurrentToken=ctkCOMMA then
  723. begin
  724. Consume(ctkCOMMA);
  725. aRule.AddSelector(GetAppendElement(aList));
  726. aList:=TCSSListElement(CreateElement(TCSSListElement));
  727. end;
  728. end;
  729. // Note: no selectors is allowed
  730. aRule.AddSelector(GetAppendElement(aList));
  731. aList:=nil;
  732. aLast:=CurrentToken;
  733. if (aLast<>ctkSEMICOLON) then
  734. begin
  735. Consume(ctkLBrace);
  736. ParseRuleBody(aRule);
  737. Consume(ctkRBRACE);
  738. end;
  739. Result:=aRule;
  740. aRule:=nil;
  741. {$IFDEF VerboseCSSParser}
  742. Writeln('Rule started at ',aAt,' done');
  743. {$endif}
  744. Dec(FRuleLevel);
  745. finally
  746. aRule.Free;
  747. aList.Free;
  748. end;
  749. end;
  750. function TCSSParser.ParseUnary: TCSSElement;
  751. var
  752. Un : TCSSUnaryElement;
  753. Op : TCSSUnaryOperation;
  754. begin
  755. Result:=nil;
  756. if not (CurrentToken in [ctkDOUBLECOLON, ctkMinus, ctkPlus, ctkDiv]) then
  757. Raise ECSSParser.CreateFmt(SUnaryInvalidToken,[CurrentTokenString]);
  758. Un:=TCSSUnaryElement(CreateElement(TCSSUnaryElement));
  759. try
  760. op:=TokenToUnaryOperation(CurrentToken);
  761. Un.Operation:=op;
  762. Consume(CurrentToken);
  763. Un.Right:=ParseComponentValue;
  764. Result:=Un;
  765. Un:=nil;
  766. finally
  767. Un.Free;
  768. end;
  769. end;
  770. function TCSSParser.ParseComponentValueList(AllowRules : Boolean = True): TCSSElement;
  771. Const
  772. TermSeps = [ctkEquals,ctkPlus,ctkMinus,ctkAnd,ctkLT,ctkDIV,
  773. ctkStar,ctkTilde,ctkColon, ctkDoubleColon,
  774. ctkSquared,ctkGT, ctkPIPE, ctkDOLLAR];
  775. ListTerms = [ctkEOF,ctkLBRACE,ctkATKEYWORD,ctkComma];
  776. function DoBinary(var aLeft : TCSSElement) : TCSSElement;
  777. var
  778. Bin : TCSSBinaryElement;
  779. begin
  780. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  781. try
  782. Bin.Left:=ALeft;
  783. aLeft:=Nil;
  784. Bin.Operation:=TokenToBinaryOperation(CurrentToken);
  785. Consume(CurrentToken);
  786. Bin.Right:=ParseComponentValue;
  787. if Bin.Right=nil then
  788. DoError(SErrUnexpectedToken ,[
  789. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  790. CurrentTokenString,
  791. 'value'
  792. ]);
  793. Result:=Bin;
  794. Bin:=nil;
  795. finally
  796. Bin.Free;
  797. end;
  798. end;
  799. Var
  800. List : TCSSListElement;
  801. aFactor : TCSSelement;
  802. begin
  803. aFactor:=Nil;
  804. List:=TCSSListElement(CreateElement(TCSSListElement));
  805. try
  806. if AllowRules and (CurrentToken in [ctkLBRACE,ctkATKEYWORD]) then
  807. begin
  808. if CurrentToken=ctkATKEYWORD then
  809. aFactor:=ParseAtRule
  810. else
  811. aFactor:=ParseRule;
  812. end
  813. else
  814. aFactor:=ParseComponentValue;
  815. if aFactor=nil then
  816. DoError(SErrUnexpectedToken ,[
  817. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  818. CurrentTokenString,
  819. 'value'
  820. ]);
  821. While Assigned(aFactor) do
  822. begin
  823. While CurrentToken in TermSeps do
  824. aFactor:=DoBinary(aFactor);
  825. List.AddChild(aFactor);
  826. aFactor:=Nil;
  827. if not (CurrentToken in ListTerms) then
  828. aFactor:=ParseComponentValue;
  829. end;
  830. Result:=GetAppendElement(List);
  831. List:=nil;
  832. finally
  833. List.Free;
  834. aFactor.Free;
  835. end;
  836. end;
  837. function TCSSParser.ParseComponentValue: TCSSElement;
  838. Const
  839. FinalTokens =
  840. [ctkLPARENTHESIS,ctkURL,ctkColon,ctkLBRACE, ctkLBRACKET,
  841. ctkDOUBLECOLON,ctkMinus,ctkPlus,ctkDiv,ctkSTAR,ctkTILDE];
  842. var
  843. aToken : TCSSToken;
  844. begin
  845. aToken:=CurrentToken;
  846. Case aToken of
  847. ctkLPARENTHESIS: Result:=ParseParenthesis;
  848. ctkURL: Result:=ParseURL;
  849. ctkPSEUDO: Result:=ParsePseudo;
  850. ctkLBRACE: Result:=ParseRule;
  851. ctkLBRACKET: Result:=ParseArray(Nil);
  852. ctkMinus,
  853. ctkPlus,
  854. ctkDiv: Result:=ParseUnary;
  855. ctkUnicodeRange: Result:=ParseUnicodeRange;
  856. ctkSTRING,
  857. ctkHASH : Result:=ParseString;
  858. ctkINTEGER: Result:=ParseInteger;
  859. ctkFloat : Result:=ParseFloat;
  860. ctkPSEUDOFUNCTION,
  861. ctkFUNCTION : Result:=ParseCall('');
  862. ctkSTAR,
  863. ctkTILDE,
  864. ctkIDENTIFIER: Result:=ParseIdentifier;
  865. ctkCLASSNAME : Result:=ParseClassName;
  866. else
  867. Result:=nil;
  868. // Consume(aToken);// continue
  869. end;
  870. if aToken in FinalTokens then
  871. exit;
  872. if (CurrentToken=ctkLBRACKET) then
  873. Result:=ParseArray(Result);
  874. end;
  875. function TCSSParser.ParseSelector: TCSSElement;
  876. function ParseSub: TCSSElement;
  877. begin
  878. Result:=nil;
  879. Case CurrentToken of
  880. ctkSTAR,
  881. ctkIDENTIFIER : Result:=ParseIdentifier;
  882. ctkHASH : Result:=ParseHashIdentifier;
  883. ctkCLASSNAME : Result:=ParseClassName;
  884. ctkLBRACKET: Result:=ParseAttributeSelector;
  885. ctkPSEUDO: Result:=ParsePseudo;
  886. ctkPSEUDOFUNCTION: Result:=ParseCall('');
  887. else
  888. DoError(SErrUnexpectedToken ,[
  889. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  890. CurrentTokenString,
  891. 'selector'
  892. ]);
  893. end;
  894. end;
  895. var
  896. ok, OldReturnWhiteSpace: Boolean;
  897. Bin: TCSSBinaryElement;
  898. El: TCSSElement;
  899. List: TCSSListElement;
  900. begin
  901. Result:=nil;
  902. El:=nil;
  903. Bin:=nil;
  904. List:=nil;
  905. ok:=false;
  906. //writeln('TCSSParser.ParseSelector START ',CurrentToken);
  907. OldReturnWhiteSpace:=Scanner.ReturnWhiteSpace;
  908. Scanner.ReturnWhiteSpace:=true;
  909. try
  910. repeat
  911. //writeln('TCSSParser.ParseSelector LIST START ',CurrentToken);
  912. // read list
  913. List:=nil;
  914. El:=ParseSub;
  915. //writeln('TCSSParser.ParseSelector LIST NEXT ',CurrentToken);
  916. while CurrentToken in [ctkSTAR,ctkIDENTIFIER,ctkCLASSNAME,ctkLBRACKET,ctkPSEUDO,ctkPSEUDOFUNCTION] do
  917. begin
  918. if List=nil then
  919. begin
  920. List:=TCSSListElement(CreateElement(TCSSListElement));
  921. List.AddChild(El);
  922. El:=List;
  923. end;
  924. List.AddChild(ParseSub);
  925. end;
  926. List:=nil;
  927. // use element
  928. if Bin<>nil then
  929. Bin.Right:=El
  930. else
  931. Result:=El;
  932. El:=nil;
  933. //writeln('TCSSParser.ParseSelector LIST END ',CurrentToken);
  934. SkipWhiteSpace;
  935. case CurrentToken of
  936. ctkLBRACE,ctkEOF,ctkSEMICOLON,ctkCOMMA:
  937. break;
  938. ctkGT,ctkPLUS,ctkTILDE,ctkPIPE:
  939. begin
  940. // combinator
  941. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  942. Bin.Left:=Result;
  943. Result:=Bin;
  944. Bin.Operation:=TokenToBinaryOperation(CurrentToken);
  945. GetNextToken;
  946. SkipWhiteSpace;
  947. end;
  948. ctkIDENTIFIER,ctkCLASSNAME,ctkLBRACKET,ctkPSEUDO,ctkPSEUDOFUNCTION:
  949. begin
  950. // decendant combinator
  951. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  952. Bin.Left:=Result;
  953. Result:=Bin;
  954. Bin.Operation:=boWhiteSpace;
  955. end;
  956. else
  957. Consume(ctkLBRACE);
  958. end;
  959. until false;
  960. ok:=true;
  961. finally
  962. Scanner.ReturnWhiteSpace:=OldReturnWhiteSpace;
  963. if not ok then
  964. begin
  965. Result.Free;
  966. El.Free;
  967. List.Free;
  968. Bin.Free;
  969. end;
  970. end;
  971. end;
  972. function TCSSParser.ParseAttributeSelector: TCSSElement;
  973. Var
  974. aEl : TCSSElement;
  975. aArray : TCSSArrayElement;
  976. Bin: TCSSBinaryElement;
  977. StrEl: TCSSStringElement;
  978. aToken: TCSSToken;
  979. begin
  980. Result:=Nil;
  981. aArray:=TCSSArrayElement(CreateElement(TCSSArrayElement));
  982. try
  983. Consume(ctkLBRACKET);
  984. SkipWhiteSpace;
  985. aEl:=ParseWQName;
  986. SkipWhiteSpace;
  987. aToken:=CurrentToken;
  988. case aToken of
  989. ctkEQUALS,ctkTILDEEQUAL,ctkPIPEEQUAL,ctkSQUAREDEQUAL,ctkDOLLAREQUAL,ctkSTAREQUAL:
  990. begin
  991. // parse attr-matcher
  992. Bin:=TCSSBinaryElement(CreateElement(TCSSBinaryElement));
  993. aArray.AddChild(Bin);
  994. Bin.Left:=aEl;
  995. Bin.Operation:=TokenToBinaryOperation(aToken);
  996. GetNextToken;
  997. SkipWhiteSpace;
  998. // parse value
  999. case CurrentToken of
  1000. ctkSTRING:
  1001. begin
  1002. StrEl:=TCSSStringElement(CreateElement(TCSSStringElement));
  1003. StrEl.Value:=CurrentTokenString;
  1004. Bin.Right:=StrEl;
  1005. GetNextToken;
  1006. end;
  1007. ctkIDENTIFIER:
  1008. Bin.Right:=ParseIdentifier;
  1009. else
  1010. DoError(SErrUnexpectedToken ,[
  1011. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  1012. CurrentTokenString,
  1013. 'attribute value'
  1014. ]);
  1015. end;
  1016. end;
  1017. else
  1018. aArray.AddChild(aEl);
  1019. end;
  1020. SkipWhiteSpace;
  1021. while CurrentToken=ctkIDENTIFIER do
  1022. begin
  1023. // attribute modifier
  1024. // with CSS 5 there is only i and s, but for future compatibility read all
  1025. aArray.AddChild(ParseIdentifier);
  1026. SkipWhiteSpace;
  1027. end;
  1028. Consume(ctkRBRACKET);
  1029. Result:=aArray;
  1030. aArray:=nil;
  1031. finally
  1032. aArray.Free;
  1033. end;
  1034. end;
  1035. function TCSSParser.ParseWQName: TCSSElement;
  1036. begin
  1037. if CurrentToken<>ctkIDENTIFIER then
  1038. DoError(SErrUnexpectedToken ,[
  1039. GetEnumName(TypeInfo(TCSSToken),Ord(CurrentToken)),
  1040. CurrentTokenString,
  1041. 'identifier'
  1042. ]);
  1043. Result:=ParseIdentifier;
  1044. // todo: parse optional ns-prefix
  1045. end;
  1046. function TCSSParser.ParseDeclaration(aIsAt: Boolean = false): TCSSDeclarationElement;
  1047. Var
  1048. aDecl : TCSSDeclarationElement;
  1049. aKey,aValue : TCSSElement;
  1050. aPrevDisablePseudo : Boolean;
  1051. aList : TCSSListElement;
  1052. begin
  1053. aList:=nil;
  1054. aDecl:= TCSSDeclarationElement(CreateElement(TCSSDeclarationElement));
  1055. try
  1056. aPrevDisablePseudo:= Scanner.DisablePseudo;
  1057. Scanner.DisablePseudo:=True;
  1058. aKey:=ParseComponentValue;
  1059. aDecl.AddKey(aKey);
  1060. if aIsAt then
  1061. begin
  1062. While (CurrentToken=ctkCOMMA) do
  1063. begin
  1064. while (CurrentToken=ctkCOMMA) do
  1065. Consume(ctkCOMMA);
  1066. aKey:=ParseComponentValue;
  1067. aDecl.AddKey(aKey);
  1068. end;
  1069. end;
  1070. if Not aIsAt then
  1071. begin
  1072. aDecl.Colon:=True;
  1073. Consume(ctkCOLON);
  1074. end
  1075. else
  1076. begin
  1077. aDecl.Colon:=CurrentToken=ctkColon;
  1078. if aDecl.Colon then
  1079. Consume(ctkColon)
  1080. end;
  1081. Scanner.DisablePseudo:=aPrevDisablePseudo;
  1082. aValue:=ParseComponentValue;
  1083. aList:=TCSSListElement(CreateElement(TCSSListElement));
  1084. aList.AddChild(aValue);
  1085. if aDecl.Colon then
  1086. begin
  1087. While not (CurrentToken in [ctkSemicolon,ctkRBRACE,ctkImportant]) do
  1088. begin
  1089. While CurrentToken=ctkCOMMA do
  1090. begin
  1091. Consume(ctkCOMMA);
  1092. aDecl.AddChild(GetAppendElement(aList));
  1093. aList:=TCSSListElement(CreateElement(TCSSListElement));
  1094. end;
  1095. aValue:=ParseComponentValue;
  1096. aList.AddChild(aValue);
  1097. end;
  1098. if CurrentToken=ctkImportant then
  1099. begin
  1100. Consume(ctkImportant);
  1101. aDecl.IsImportant:=True;
  1102. end;
  1103. end;
  1104. aDecl.AddChild(GetAppendElement(aList));
  1105. aList:=nil;
  1106. Result:=aDecl;
  1107. aDecl:=nil;
  1108. finally
  1109. Scanner.DisablePseudo:=False;
  1110. aDecl.Free;
  1111. aList.Free;
  1112. end;
  1113. end;
  1114. function TCSSParser.ParseCall(aName : TCSSString): TCSSElement;
  1115. var
  1116. aCall : TCSSCallElement;
  1117. l : Integer;
  1118. begin
  1119. aCall:=TCSSCallElement(CreateELement(TCSSCallElement));
  1120. try
  1121. if (aName='') then
  1122. aName:=CurrentTokenString;
  1123. L:=Length(aName);
  1124. if (L>0) and (aName[L]='(') then
  1125. aName:=Copy(aName,1,L-1);
  1126. aCall.Name:=aName;
  1127. if CurrentToken=ctkPSEUDOFUNCTION then
  1128. Consume(ctkPSEUDOFUNCTION)
  1129. else
  1130. Consume(ctkFUNCTION);
  1131. While not (CurrentToken in [ctkRPARENTHESIS,ctkEOF]) do
  1132. begin
  1133. aCall.AddArg(ParseComponentValueList);
  1134. if (CurrentToken=ctkCOMMA) then
  1135. Consume(ctkCOMMA);
  1136. end;
  1137. if CurrentToken=ctkEOF then
  1138. DoError(SErrUnexpectedEndOfFile,[aName]);
  1139. Consume(ctkRPARENTHESIS);
  1140. // Call argument list can be empty: mask()
  1141. Result:=aCall;
  1142. aCall:=nil;
  1143. finally
  1144. aCall.Free;
  1145. end;
  1146. end;
  1147. function TCSSParser.ParseString: TCSSElement;
  1148. Var
  1149. aValue : TCSSString;
  1150. aEl : TCSSElement;
  1151. aStr : TCSSStringElement;
  1152. begin
  1153. aValue:=CurrentTokenString;
  1154. aStr:=TCSSStringElement(CreateElement(TCSSStringElement));
  1155. try
  1156. if CurrentToken=ctkSTRING then
  1157. Consume(ctkSTRING)
  1158. else
  1159. Consume(ctkHASH); // e.g. #rrggbb
  1160. aStr.Value:=aValue;
  1161. While (CurrentToken in [ctkIDENTIFIER,ctkSTRING,ctkINTEGER,ctkFLOAT,ctkHASH]) do
  1162. begin
  1163. aEl:=ParseComponentValue;
  1164. aStr.Children.Add(aEl);
  1165. end;
  1166. Result:=aStr;
  1167. aStr:=nil;
  1168. finally
  1169. aStr.Free;
  1170. end;
  1171. end;
  1172. function TCSSParser.ParseUnicodeRange: TCSSElement;
  1173. Var
  1174. aValue : TCSSString;
  1175. aRange : TCSSUnicodeRangeElement;
  1176. begin
  1177. aValue:=CurrentTokenString;
  1178. aRange:=TCSSUnicodeRangeElement(CreateElement(TCSSUnicodeRangeElement));
  1179. try
  1180. Consume(ctkUnicodeRange);
  1181. aRange.Value:=aValue;
  1182. Result:=aRange;
  1183. aRange:=nil;
  1184. finally
  1185. aRange.Free;
  1186. end;
  1187. end;
  1188. function TCSSParser.ParseArray(aPrefix: TCSSElement): TCSSElement;
  1189. Var
  1190. aEl : TCSSElement;
  1191. aArray : TCSSArrayElement;
  1192. begin
  1193. Result:=Nil;
  1194. aArray:=TCSSArrayElement(CreateElement(TCSSArrayElement));
  1195. try
  1196. aArray.Prefix:=aPrefix;
  1197. Consume(ctkLBRACKET);
  1198. While CurrentToken<>ctkRBRACKET do
  1199. begin
  1200. aEl:=ParseComponentValueList;
  1201. aArray.AddChild(aEl);
  1202. end;
  1203. Consume(ctkRBRACKET);
  1204. Result:=aArray;
  1205. aArray:=nil;
  1206. finally
  1207. aArray.Free;
  1208. end;
  1209. end;
  1210. end.