Lexer.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Text;
  5. using ExCSS.Model;
  6. using ExCSS.Model.TextBlocks;
  7. // ReSharper disable once CheckNamespace
  8. #pragma warning disable
  9. namespace ExCSS
  10. {
  11. sealed class Lexer
  12. {
  13. private readonly StringBuilder _buffer;
  14. private readonly StylesheetReader _stylesheetReader;
  15. private bool _ignoreWhitespace;
  16. private bool _ignoreComments;
  17. internal Action<ParserError, string> ErrorHandler { get; set; }
  18. internal Lexer(StylesheetReader source)
  19. {
  20. _buffer = new StringBuilder();
  21. _stylesheetReader = source;
  22. ErrorHandler = (err, msg) => { };
  23. }
  24. private Block DataBlock(char current)
  25. {
  26. switch (current)
  27. {
  28. case Specification.LineFeed:
  29. case Specification.CarriageReturn:
  30. case Specification.Tab:
  31. case Specification.Space:
  32. do
  33. {
  34. current = _stylesheetReader.Next;
  35. }
  36. while (current.IsSpaceCharacter());
  37. if (_ignoreWhitespace)
  38. {
  39. return DataBlock(current);
  40. }
  41. _stylesheetReader.Back();
  42. return SpecialCharacter.Whitespace;
  43. case Specification.DoubleQuote:
  44. return DoubleQuoteString(_stylesheetReader.Next);
  45. case Specification.Hash:
  46. return HashStart(_stylesheetReader.Next);
  47. case Specification.DollarSign:
  48. current = _stylesheetReader.Next;
  49. return current == Specification.EqualSign
  50. ? MatchBlock.Suffix
  51. : Block.Delim(_stylesheetReader.Previous);
  52. case Specification.SingleQuote:
  53. return SingleQuoteString(_stylesheetReader.Next);
  54. case Specification.ParenOpen:
  55. return BracketBlock.OpenRound;
  56. case Specification.ParenClose:
  57. return BracketBlock.CloseRound;
  58. case Specification.Asterisk:
  59. current = _stylesheetReader.Next;
  60. return current == Specification.EqualSign
  61. ? MatchBlock.Substring
  62. : Block.Delim(_stylesheetReader.Previous);
  63. case Specification.PlusSign:
  64. {
  65. var nextFirst = _stylesheetReader.Next;
  66. if (nextFirst == Specification.EndOfFile)
  67. {
  68. _stylesheetReader.Back();
  69. }
  70. else
  71. {
  72. var nextSEcond = _stylesheetReader.Next;
  73. _stylesheetReader.Back(2);
  74. if (nextFirst.IsDigit() || (nextFirst == Specification.Period && nextSEcond.IsDigit()))
  75. {
  76. return NumberStart(current);
  77. }
  78. }
  79. return Block.Delim(current);
  80. }
  81. case Specification.Comma:
  82. return SpecialCharacter.Comma;
  83. case Specification.Period:
  84. {
  85. var c = _stylesheetReader.Next;
  86. return c.IsDigit()
  87. ? NumberStart(_stylesheetReader.Previous)
  88. : Block.Delim(_stylesheetReader.Previous);
  89. }
  90. case Specification.MinusSign:
  91. {
  92. var nextFirst = _stylesheetReader.Next;
  93. if (nextFirst == Specification.EndOfFile)
  94. {
  95. _stylesheetReader.Back();
  96. }
  97. else
  98. {
  99. var nextSecond = _stylesheetReader.Next;
  100. _stylesheetReader.Back(2);
  101. if (nextFirst.IsDigit() || (nextFirst == Specification.Period && nextSecond.IsDigit()))
  102. {
  103. return NumberStart(current);
  104. }
  105. if (nextFirst.IsNameStart())
  106. {
  107. return IdentStart(current);
  108. }
  109. if (nextFirst == Specification.ReverseSolidus && !nextSecond.IsLineBreak() && nextSecond != Specification.EndOfFile)
  110. {
  111. return IdentStart(current);
  112. }
  113. if (nextFirst != Specification.MinusSign || nextSecond != Specification.GreaterThan)
  114. {
  115. return Block.Delim(current);
  116. }
  117. _stylesheetReader.Advance(2);
  118. return _ignoreComments
  119. ? DataBlock(_stylesheetReader.Next)
  120. : CommentBlock.Close;
  121. }
  122. return Block.Delim(current);
  123. }
  124. case Specification.Solidus:
  125. current = _stylesheetReader.Next;
  126. return current == Specification.Asterisk
  127. ? Comment(_stylesheetReader.Next)
  128. : Block.Delim(_stylesheetReader.Previous);
  129. case Specification.ReverseSolidus:
  130. current = _stylesheetReader.Next;
  131. if (current.IsLineBreak() || current == Specification.EndOfFile)
  132. {
  133. ErrorHandler(current == Specification.EndOfFile
  134. ? ParserError.EndOfFile
  135. : ParserError.UnexpectedLineBreak,
  136. ErrorMessages.LineBreakEof);
  137. return Block.Delim(_stylesheetReader.Previous);
  138. }
  139. return IdentStart(_stylesheetReader.Previous);
  140. case Specification.Colon:
  141. return SpecialCharacter.Colon;
  142. case Specification.Simicolon:
  143. return SpecialCharacter.Semicolon;
  144. case Specification.LessThan:
  145. current = _stylesheetReader.Next;
  146. if (current == Specification.Em)
  147. {
  148. current = _stylesheetReader.Next;
  149. if (current == Specification.MinusSign)
  150. {
  151. current = _stylesheetReader.Next;
  152. if (current == Specification.MinusSign)
  153. {
  154. return _ignoreComments
  155. ? DataBlock(_stylesheetReader.Next)
  156. : CommentBlock.Open;
  157. }
  158. current = _stylesheetReader.Previous;
  159. }
  160. current = _stylesheetReader.Previous;
  161. }
  162. return Block.Delim(_stylesheetReader.Previous);
  163. case Specification.At:
  164. return AtKeywordStart(_stylesheetReader.Next);
  165. case Specification.SquareBracketOpen:
  166. return BracketBlock.OpenSquare;
  167. case Specification.SquareBracketClose:
  168. return BracketBlock.CloseSquare;
  169. case Specification.Accent:
  170. current = _stylesheetReader.Next;
  171. return current == Specification.EqualSign
  172. ? MatchBlock.Prefix
  173. : Block.Delim(_stylesheetReader.Previous);
  174. case Specification.CurlyBraceOpen:
  175. return BracketBlock.OpenCurly;
  176. case Specification.CurlyBraceClose:
  177. return BracketBlock.CloseCurly;
  178. case '0':
  179. case '1':
  180. case '2':
  181. case '3':
  182. case '4':
  183. case '5':
  184. case '6':
  185. case '7':
  186. case '8':
  187. case '9':
  188. return NumberStart(current);
  189. case 'U':
  190. case 'u':
  191. current = _stylesheetReader.Next;
  192. if (current == Specification.PlusSign)
  193. {
  194. current = _stylesheetReader.Next;
  195. if (current.IsHex() || current == Specification.QuestionMark)
  196. return UnicodeRange(current);
  197. current = _stylesheetReader.Previous;
  198. }
  199. return IdentStart(_stylesheetReader.Previous);
  200. case Specification.Pipe:
  201. current = _stylesheetReader.Next;
  202. if (current == Specification.EqualSign)
  203. {
  204. return MatchBlock.Dash;
  205. }
  206. if (current == Specification.Pipe)
  207. {
  208. return Block.Column;
  209. }
  210. return Block.Delim(_stylesheetReader.Previous);
  211. case Specification.Tilde:
  212. current = _stylesheetReader.Next;
  213. if (current == Specification.EqualSign)
  214. {
  215. return MatchBlock.Include;
  216. }
  217. return Block.Delim(_stylesheetReader.Previous);
  218. case Specification.EndOfFile:
  219. return null;
  220. case Specification.Em:
  221. current = _stylesheetReader.Next;
  222. return current == Specification.EqualSign
  223. ? MatchBlock.Not
  224. : Block.Delim(_stylesheetReader.Previous);
  225. default:
  226. return current.IsNameStart()
  227. ? IdentStart(current)
  228. : Block.Delim(current);
  229. }
  230. }
  231. private Block DoubleQuoteString(char current)
  232. {
  233. while (true)
  234. {
  235. switch (current)
  236. {
  237. case Specification.DoubleQuote:
  238. case Specification.EndOfFile:
  239. return StringBlock.Plain(FlushBuffer());
  240. case Specification.FormFeed:
  241. case Specification.LineFeed:
  242. ErrorHandler(ParserError.UnexpectedLineBreak, ErrorMessages.DoubleQuotedString);
  243. _stylesheetReader.Back();
  244. return StringBlock.Plain(FlushBuffer(), true);
  245. case Specification.ReverseSolidus:
  246. current = _stylesheetReader.Next;
  247. if (current.IsLineBreak())
  248. {
  249. _buffer.AppendLine();
  250. }
  251. else if (current != Specification.EndOfFile)
  252. {
  253. _buffer.Append(ConsumeEscape(current));
  254. }
  255. else
  256. {
  257. ErrorHandler(ParserError.EndOfFile, ErrorMessages.DoubleQuotedStringEof);
  258. _stylesheetReader.Back();
  259. return StringBlock.Plain(FlushBuffer(), true);
  260. }
  261. break;
  262. default:
  263. _buffer.Append(current);
  264. break;
  265. }
  266. current = _stylesheetReader.Next;
  267. }
  268. }
  269. private Block SingleQuoteString(char current)
  270. {
  271. while (true)
  272. {
  273. switch (current)
  274. {
  275. case Specification.SingleQuote:
  276. case Specification.EndOfFile:
  277. return StringBlock.Plain(FlushBuffer());
  278. case Specification.FormFeed:
  279. case Specification.LineFeed:
  280. ErrorHandler(ParserError.UnexpectedLineBreak, ErrorMessages.SingleQuotedString);
  281. _stylesheetReader.Back();
  282. return (StringBlock.Plain(FlushBuffer(), true));
  283. case Specification.ReverseSolidus:
  284. current = _stylesheetReader.Next;
  285. if (current.IsLineBreak())
  286. {
  287. _buffer.AppendLine();
  288. }
  289. else if (current != Specification.EndOfFile)
  290. {
  291. _buffer.Append(ConsumeEscape(current));
  292. }
  293. else
  294. {
  295. ErrorHandler(ParserError.EndOfFile, ErrorMessages.SingleQuotedStringEof);
  296. _stylesheetReader.Back();
  297. return (StringBlock.Plain(FlushBuffer(), true));
  298. }
  299. break;
  300. default:
  301. _buffer.Append(current);
  302. break;
  303. }
  304. current = _stylesheetReader.Next;
  305. }
  306. }
  307. private Block HashStart(char current)
  308. {
  309. if (current.IsNameStart())
  310. {
  311. _buffer.Append(current);
  312. return HashRest(_stylesheetReader.Next);
  313. }
  314. if (IsValidEscape(current))
  315. {
  316. current = _stylesheetReader.Next;
  317. _buffer.Append(ConsumeEscape(current));
  318. return HashRest(_stylesheetReader.Next);
  319. }
  320. if (current != Specification.ReverseSolidus)
  321. {
  322. _stylesheetReader.Back();
  323. return Block.Delim(Specification.Hash);
  324. }
  325. ErrorHandler(ParserError.InvalidCharacter, ErrorMessages.InvalidCharacterAfterHash);
  326. return Block.Delim(Specification.Hash);
  327. }
  328. private Block HashRest(char current)
  329. {
  330. while (true)
  331. {
  332. if (current.IsName())
  333. {
  334. _buffer.Append(current);
  335. }
  336. else if (IsValidEscape(current))
  337. {
  338. current = _stylesheetReader.Next;
  339. _buffer.Append(ConsumeEscape(current));
  340. }
  341. else if (current == Specification.ReverseSolidus)
  342. {
  343. ErrorHandler(ParserError.InvalidCharacter, ErrorMessages.InvalidCharacterAfterHash);
  344. _stylesheetReader.Back();
  345. return SymbolBlock.Hash(FlushBuffer());
  346. }
  347. else
  348. {
  349. _stylesheetReader.Back();
  350. return SymbolBlock.Hash(FlushBuffer());
  351. }
  352. current = _stylesheetReader.Next;
  353. }
  354. }
  355. private Block Comment(char current)
  356. {
  357. while (true)
  358. {
  359. switch (current)
  360. {
  361. case Specification.Asterisk:
  362. current = _stylesheetReader.Next;
  363. if (current == Specification.Solidus)
  364. {
  365. return DataBlock(_stylesheetReader.Next);
  366. }
  367. break;
  368. case Specification.Solidus:
  369. {
  370. if (_stylesheetReader.Previous == Specification.Asterisk)
  371. {
  372. return DataBlock(_stylesheetReader.Next);
  373. }
  374. current = _stylesheetReader.Next;
  375. break;
  376. }
  377. case Specification.EndOfFile:
  378. ErrorHandler(ParserError.EndOfFile, ErrorMessages.ExpectedCommentEnd);
  379. return DataBlock(current);
  380. }
  381. current = _stylesheetReader.Next;
  382. }
  383. }
  384. private Block AtKeywordStart(char current)
  385. {
  386. if (current == Specification.MinusSign)
  387. {
  388. current = _stylesheetReader.Next;
  389. if (current.IsNameStart() || IsValidEscape(current))
  390. {
  391. _buffer.Append(Specification.MinusSign);
  392. return AtKeywordRest(current);
  393. }
  394. _stylesheetReader.Back(2);
  395. return Block.Delim(Specification.At);
  396. }
  397. if (current.IsNameStart())
  398. {
  399. _buffer.Append(current);
  400. return AtKeywordRest(_stylesheetReader.Next);
  401. }
  402. if (IsValidEscape(current))
  403. {
  404. current = _stylesheetReader.Next;
  405. _buffer.Append(ConsumeEscape(current));
  406. return AtKeywordRest(_stylesheetReader.Next);
  407. }
  408. _stylesheetReader.Back();
  409. return Block.Delim(Specification.At);
  410. }
  411. private Block AtKeywordRest(char current)
  412. {
  413. while (true)
  414. {
  415. if (current.IsName())
  416. {
  417. _buffer.Append(current);
  418. }
  419. else if (IsValidEscape(current))
  420. {
  421. current = _stylesheetReader.Next;
  422. _buffer.Append(ConsumeEscape(current));
  423. }
  424. else
  425. {
  426. _stylesheetReader.Back();
  427. return SymbolBlock.At(FlushBuffer());
  428. }
  429. current = _stylesheetReader.Next;
  430. }
  431. }
  432. private Block IdentStart(char current)
  433. {
  434. if (current == Specification.MinusSign)
  435. {
  436. current = _stylesheetReader.Next;
  437. if (current.IsNameStart() || IsValidEscape(current))
  438. {
  439. _buffer.Append(Specification.MinusSign);
  440. return IdentRest(current);
  441. }
  442. _stylesheetReader.Back();
  443. return Block.Delim(Specification.MinusSign);
  444. }
  445. if (current.IsNameStart())
  446. {
  447. _buffer.Append(current);
  448. return IdentRest(_stylesheetReader.Next);
  449. }
  450. if (current != Specification.ReverseSolidus)
  451. {
  452. return DataBlock(current);
  453. }
  454. if (!IsValidEscape(current))
  455. {
  456. return DataBlock(current);
  457. }
  458. current = _stylesheetReader.Next;
  459. _buffer.Append(ConsumeEscape(current));
  460. return IdentRest(_stylesheetReader.Next);
  461. }
  462. private Block IdentRest(char current)
  463. {
  464. while (true)
  465. {
  466. if (current.IsName())
  467. {
  468. _buffer.Append(current);
  469. }
  470. else if (IsValidEscape(current))
  471. {
  472. current = _stylesheetReader.Next;
  473. _buffer.Append(ConsumeEscape(current));
  474. }
  475. else if (current == Specification.ParenOpen)
  476. {
  477. switch (_buffer.ToString().ToLower())
  478. {
  479. case "url":
  480. _buffer.Length = 0;
  481. return UrlStart(_stylesheetReader.Next);//, GrammarSegment.Url);
  482. case "domain":
  483. _buffer.Length = 0;
  484. return UrlStart(_stylesheetReader.Next);//, GrammarSegment.Domain);
  485. case "url-prefix":
  486. _buffer.Length = 0;
  487. return UrlStart(_stylesheetReader.Next);//, GrammarSegment.UrlPrefix);
  488. default:
  489. return SymbolBlock.Function(FlushBuffer());
  490. }
  491. }
  492. else
  493. {
  494. _stylesheetReader.Back();
  495. return SymbolBlock.Ident(FlushBuffer());
  496. }
  497. current = _stylesheetReader.Next;
  498. }
  499. }
  500. private Block NumberStart(char current)
  501. {
  502. while (true)
  503. {
  504. switch (current)
  505. {
  506. case Specification.MinusSign:
  507. case Specification.PlusSign:
  508. _buffer.Append(current);
  509. current = _stylesheetReader.Next;
  510. if (current == Specification.Period)
  511. {
  512. _buffer.Append(current);
  513. _buffer.Append(_stylesheetReader.Next);
  514. return NumberFraction(_stylesheetReader.Next);
  515. }
  516. _buffer.Append(current);
  517. return NumberRest(_stylesheetReader.Next);
  518. case Specification.Period:
  519. _buffer.Append(current);
  520. _buffer.Append(_stylesheetReader.Next);
  521. return NumberFraction(_stylesheetReader.Next);
  522. default:
  523. if (current.IsDigit())
  524. {
  525. _buffer.Append(current);
  526. return NumberRest(_stylesheetReader.Next);
  527. }
  528. break;
  529. }
  530. current = _stylesheetReader.Next;
  531. }
  532. }
  533. private Block NumberRest(char current)
  534. {
  535. while (true)
  536. {
  537. if (current.IsDigit())
  538. {
  539. _buffer.Append(current);
  540. }
  541. else if (current.IsNameStart())
  542. {
  543. var number = FlushBuffer();
  544. _buffer.Append(current);
  545. return Dimension(_stylesheetReader.Next, number);
  546. }
  547. else if (IsValidEscape(current))
  548. {
  549. current = _stylesheetReader.Next;
  550. var number = FlushBuffer();
  551. _buffer.Append(ConsumeEscape(current));
  552. return Dimension(_stylesheetReader.Next, number);
  553. }
  554. else
  555. {
  556. break;
  557. }
  558. current = _stylesheetReader.Next;
  559. }
  560. switch (current)
  561. {
  562. case Specification.Period:
  563. current = _stylesheetReader.Next;
  564. if (current.IsDigit())
  565. {
  566. _buffer.Append(Specification.Period).Append(current);
  567. return NumberFraction(_stylesheetReader.Next);
  568. }
  569. _stylesheetReader.Back();
  570. return Block.Number(FlushBuffer());
  571. case '%':
  572. return UnitBlock.Percentage(FlushBuffer());
  573. case 'e':
  574. case 'E':
  575. return NumberExponential(current);
  576. case Specification.MinusSign:
  577. return NumberDash(current);
  578. default:
  579. _stylesheetReader.Back();
  580. return Block.Number(FlushBuffer());
  581. }
  582. }
  583. private Block NumberFraction(char current)
  584. {
  585. while (true)
  586. {
  587. if (current.IsDigit())
  588. {
  589. _buffer.Append(current);
  590. }
  591. else if ("eE%-".IndexOf(current) >= 0)
  592. {
  593. break;
  594. }
  595. else if (current.IsNameStart())
  596. {
  597. var number = FlushBuffer();
  598. _buffer.Append(current);
  599. return Dimension(_stylesheetReader.Next, number);
  600. }
  601. else if (IsValidEscape(current))
  602. {
  603. current = _stylesheetReader.Next;
  604. var number = FlushBuffer();
  605. _buffer.Append(ConsumeEscape(current));
  606. return Dimension(_stylesheetReader.Next, number);
  607. }
  608. else
  609. {
  610. break;
  611. }
  612. current = _stylesheetReader.Next;
  613. }
  614. switch (current)
  615. {
  616. case 'e':
  617. case 'E':
  618. return NumberExponential(current);
  619. case '%':
  620. return UnitBlock.Percentage(FlushBuffer());
  621. case Specification.MinusSign:
  622. return NumberDash(current);
  623. default:
  624. _stylesheetReader.Back();
  625. return Block.Number(FlushBuffer());
  626. }
  627. }
  628. private Block Dimension(char current, string number)
  629. {
  630. while (true)
  631. {
  632. if (current.IsName())
  633. {
  634. _buffer.Append(current);
  635. }
  636. else if (IsValidEscape(current))
  637. {
  638. current = _stylesheetReader.Next;
  639. _buffer.Append(ConsumeEscape(current));
  640. }
  641. else
  642. {
  643. _stylesheetReader.Back();
  644. return UnitBlock.Dimension(number, FlushBuffer());
  645. }
  646. current = _stylesheetReader.Next;
  647. }
  648. }
  649. private Block SciNotation(char current)
  650. {
  651. while (true)
  652. {
  653. if (current.IsDigit())
  654. {
  655. _buffer.Append(current);
  656. }
  657. else
  658. {
  659. _stylesheetReader.Back();
  660. return Block.Number(FlushBuffer());
  661. }
  662. current = _stylesheetReader.Next;
  663. }
  664. }
  665. private Block UrlStart(char current)
  666. {
  667. while (current.IsSpaceCharacter())
  668. {
  669. current = _stylesheetReader.Next;
  670. }
  671. switch (current)
  672. {
  673. case Specification.EndOfFile:
  674. ErrorHandler(ParserError.EndOfFile, ErrorMessages.InvalidUrlEnd);
  675. return StringBlock.Url(string.Empty, true);
  676. case Specification.DoubleQuote:
  677. return DoubleQuotedUrl(_stylesheetReader.Next);
  678. case Specification.SingleQuote:
  679. return SingleQuoteUrl(_stylesheetReader.Next);
  680. case ')':
  681. return StringBlock.Url(string.Empty);
  682. default:
  683. return UnquotedUrl(current);
  684. }
  685. }
  686. private Block DoubleQuotedUrl(char current)
  687. {
  688. while (true)
  689. {
  690. if (current.IsLineBreak())
  691. {
  692. ErrorHandler(ParserError.UnexpectedLineBreak, ErrorMessages.InvalidUrlEnd);
  693. return BadUrl(_stylesheetReader.Next);
  694. }
  695. if (Specification.EndOfFile == current)
  696. {
  697. return StringBlock.Url(FlushBuffer());
  698. }
  699. if (current == Specification.DoubleQuote)
  700. {
  701. return UrlEnd(_stylesheetReader.Next);
  702. }
  703. if (current == Specification.ReverseSolidus)
  704. {
  705. current = _stylesheetReader.Next;
  706. if (current == Specification.EndOfFile)
  707. {
  708. _stylesheetReader.Back(2);
  709. ErrorHandler(ParserError.EndOfFile, ErrorMessages.InvalidUrlEnd);
  710. return StringBlock.Url(FlushBuffer(), true);
  711. }
  712. if (current.IsLineBreak())
  713. {
  714. _buffer.AppendLine();
  715. }
  716. else
  717. {
  718. _buffer.Append(ConsumeEscape(current));
  719. }
  720. }
  721. else
  722. {
  723. _buffer.Append(current);
  724. }
  725. current = _stylesheetReader.Next;
  726. }
  727. }
  728. private Block SingleQuoteUrl(char current)
  729. {
  730. while (true)
  731. {
  732. if (current.IsLineBreak())
  733. {
  734. ErrorHandler(ParserError.UnexpectedLineBreak, ErrorMessages.SingleQuotedString);
  735. return BadUrl(_stylesheetReader.Next);
  736. }
  737. if (Specification.EndOfFile == current)
  738. {
  739. return StringBlock.Url(FlushBuffer());
  740. }
  741. if (current == Specification.SingleQuote)
  742. {
  743. return UrlEnd(_stylesheetReader.Next);
  744. }
  745. if (current == Specification.ReverseSolidus)
  746. {
  747. current = _stylesheetReader.Next;
  748. if (current == Specification.EndOfFile)
  749. {
  750. _stylesheetReader.Back(2);
  751. ErrorHandler(ParserError.EndOfFile, ErrorMessages.SingleQuotedString);
  752. return StringBlock.Url(FlushBuffer(), true);
  753. }
  754. if (current.IsLineBreak())
  755. {
  756. _buffer.AppendLine();
  757. }
  758. else
  759. {
  760. _buffer.Append(ConsumeEscape(current));
  761. }
  762. }
  763. else
  764. {
  765. _buffer.Append(current);
  766. }
  767. current = _stylesheetReader.Next;
  768. }
  769. }
  770. private Block UnquotedUrl(char current)
  771. {
  772. while (true)
  773. {
  774. if (current.IsSpaceCharacter())
  775. {
  776. return UrlEnd(_stylesheetReader.Next);
  777. }
  778. if (current == Specification.ParenClose || current == Specification.EndOfFile)
  779. {
  780. return StringBlock.Url(FlushBuffer());
  781. }
  782. if (current == Specification.DoubleQuote || current == Specification.SingleQuote ||
  783. current == Specification.ParenOpen || current.IsNonPrintable())
  784. {
  785. ErrorHandler(ParserError.InvalidCharacter, ErrorMessages.InvalidUrlQuote);
  786. return BadUrl(_stylesheetReader.Next);
  787. }
  788. if (current == Specification.ReverseSolidus)
  789. {
  790. if (IsValidEscape(current))
  791. {
  792. current = _stylesheetReader.Next;
  793. _buffer.Append(ConsumeEscape(current));
  794. }
  795. else
  796. {
  797. ErrorHandler(ParserError.InvalidCharacter, ErrorMessages.InvalidUrlCharacter);
  798. return BadUrl(_stylesheetReader.Next);
  799. }
  800. }
  801. else
  802. {
  803. _buffer.Append(current);
  804. }
  805. current = _stylesheetReader.Next;
  806. }
  807. }
  808. private Block UrlEnd(char current)
  809. {
  810. while (true)
  811. {
  812. if (current == Specification.ParenClose)
  813. {
  814. return StringBlock.Url(FlushBuffer());
  815. }
  816. if (!current.IsSpaceCharacter())
  817. {
  818. ErrorHandler(ParserError.InvalidCharacter, ErrorMessages.InvalidUrlCharacter);
  819. return BadUrl(current);
  820. }
  821. current = _stylesheetReader.Next;
  822. }
  823. }
  824. private Block BadUrl(char current)
  825. {
  826. while (true)
  827. {
  828. if (current == Specification.EndOfFile)
  829. {
  830. ErrorHandler(ParserError.EndOfFile, ErrorMessages.InvalidUrlEnd);
  831. return StringBlock.Url(FlushBuffer(), true);
  832. }
  833. if (current == Specification.ParenClose)
  834. {
  835. return StringBlock.Url(FlushBuffer(), true);
  836. }
  837. if (IsValidEscape(current))
  838. {
  839. current = _stylesheetReader.Next;
  840. _buffer.Append(ConsumeEscape(current));
  841. }
  842. current = _stylesheetReader.Next;
  843. }
  844. }
  845. private Block UnicodeRange(char current)
  846. {
  847. for (var i = 0; i < 6; i++)
  848. {
  849. if (!current.IsHex())
  850. {
  851. break;
  852. }
  853. _buffer.Append(current);
  854. current = _stylesheetReader.Next;
  855. }
  856. if (_buffer.Length != 6)
  857. {
  858. for (var i = 0; i < 6 - _buffer.Length; i++)
  859. {
  860. if (current != Specification.QuestionMark)
  861. {
  862. current = _stylesheetReader.Previous;
  863. break;
  864. }
  865. _buffer.Append(current);
  866. current = _stylesheetReader.Next;
  867. }
  868. var range = FlushBuffer();
  869. var start = range.Replace(Specification.QuestionMark, '0');
  870. var end = range.Replace(Specification.QuestionMark, 'F');
  871. return Block.Range(start, end);
  872. }
  873. if (current == Specification.MinusSign)
  874. {
  875. current = _stylesheetReader.Next;
  876. if (current.IsHex())
  877. {
  878. var start = _buffer.ToString();
  879. _buffer.Length = 0;
  880. for (var i = 0; i < 6; i++)
  881. {
  882. if (!current.IsHex())
  883. {
  884. current = _stylesheetReader.Previous;
  885. break;
  886. }
  887. _buffer.Append(current);
  888. current = _stylesheetReader.Next;
  889. }
  890. var end = FlushBuffer();
  891. return Block.Range(start, end);
  892. }
  893. _stylesheetReader.Back(2);
  894. return Block.Range(FlushBuffer(), null);
  895. }
  896. _stylesheetReader.Back();
  897. return Block.Range(FlushBuffer(), null);
  898. }
  899. private string FlushBuffer()
  900. {
  901. var value = _buffer.ToString();
  902. _buffer.Length = 0;
  903. return value;
  904. }
  905. private Block NumberExponential(char current)
  906. {
  907. current = _stylesheetReader.Next;
  908. if (current.IsDigit())
  909. {
  910. _buffer.Append('e').Append(current);
  911. return SciNotation(_stylesheetReader.Next);
  912. }
  913. if (current == Specification.PlusSign || current == Specification.MinusSign)
  914. {
  915. var op = current;
  916. current = _stylesheetReader.Next;
  917. if (current.IsDigit())
  918. {
  919. _buffer.Append('e').Append(op).Append(current);
  920. return SciNotation(_stylesheetReader.Next);
  921. }
  922. _stylesheetReader.Back();
  923. }
  924. current = _stylesheetReader.Previous;
  925. var number = FlushBuffer();
  926. _buffer.Append(current);
  927. return Dimension(_stylesheetReader.Next, number);
  928. }
  929. private Block NumberDash(char current)
  930. {
  931. current = _stylesheetReader.Next;
  932. if (current.IsNameStart())
  933. {
  934. var number = FlushBuffer();
  935. _buffer.Append(Specification.MinusSign).Append(current);
  936. return Dimension(_stylesheetReader.Next, number);
  937. }
  938. if (IsValidEscape(current))
  939. {
  940. current = _stylesheetReader.Next;
  941. var number = FlushBuffer();
  942. _buffer.Append(Specification.MinusSign).Append(ConsumeEscape(current));
  943. return Dimension(_stylesheetReader.Next, number);
  944. }
  945. _stylesheetReader.Back(2);
  946. return Block.Number(FlushBuffer());
  947. }
  948. private string ConsumeEscape(char current)
  949. {
  950. if (!current.IsHex())
  951. {
  952. return current.ToString(CultureInfo.InvariantCulture);
  953. }
  954. var escape = new List<Char>();
  955. for (var i = 0; i < 6; i++)
  956. {
  957. escape.Add(current);
  958. current = _stylesheetReader.Next;
  959. if (!current.IsHex())
  960. {
  961. break;
  962. }
  963. }
  964. current = _stylesheetReader.Previous;
  965. var code = int.Parse(new string(escape.ToArray()), NumberStyles.HexNumber);
  966. return Char.ConvertFromUtf32(code);
  967. }
  968. private bool IsValidEscape(char current)
  969. {
  970. if (current != Specification.ReverseSolidus)
  971. {
  972. return false;
  973. }
  974. current = _stylesheetReader.Next;
  975. _stylesheetReader.Back();
  976. if (current == Specification.EndOfFile)
  977. {
  978. return false;
  979. }
  980. return !current.IsLineBreak();
  981. }
  982. internal bool IgnoreWhitespace
  983. {
  984. get { return _ignoreWhitespace; }
  985. set { _ignoreWhitespace = value; }
  986. }
  987. internal bool IgnoreComments
  988. {
  989. get { return _ignoreComments; }
  990. set { _ignoreComments = value; }
  991. }
  992. internal StylesheetReader Stream
  993. {
  994. get { return _stylesheetReader; }
  995. }
  996. internal IEnumerable<Block> Tokens
  997. {
  998. get
  999. {
  1000. while (true)
  1001. {
  1002. var token = DataBlock(_stylesheetReader.Current);
  1003. if (token == null)
  1004. {
  1005. yield break;
  1006. }
  1007. _stylesheetReader.Advance();
  1008. yield return token;
  1009. }
  1010. }
  1011. }
  1012. }
  1013. }
  1014. #pragma warning restore