UnicodeRange.cjs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. 'use strict';
  2. const types = require('../../tokenizer/types.cjs');
  3. const charCodeDefinitions = require('../../tokenizer/char-code-definitions.cjs');
  4. const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
  5. const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
  6. const QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
  7. function eatHexSequence(offset, allowDash) {
  8. let len = 0;
  9. for (let pos = this.tokenStart + offset; pos < this.tokenEnd; pos++) {
  10. const code = this.charCodeAt(pos);
  11. if (code === HYPHENMINUS && allowDash && len !== 0) {
  12. eatHexSequence.call(this, offset + len + 1, false);
  13. return -1;
  14. }
  15. if (!charCodeDefinitions.isHexDigit(code)) {
  16. this.error(
  17. allowDash && len !== 0
  18. ? 'Hyphen minus' + (len < 6 ? ' or hex digit' : '') + ' is expected'
  19. : (len < 6 ? 'Hex digit is expected' : 'Unexpected input'),
  20. pos
  21. );
  22. }
  23. if (++len > 6) {
  24. this.error('Too many hex digits', pos);
  25. } }
  26. this.next();
  27. return len;
  28. }
  29. function eatQuestionMarkSequence(max) {
  30. let count = 0;
  31. while (this.isDelim(QUESTIONMARK)) {
  32. if (++count > max) {
  33. this.error('Too many question marks');
  34. }
  35. this.next();
  36. }
  37. }
  38. function startsWith(code) {
  39. if (this.charCodeAt(this.tokenStart) !== code) {
  40. this.error((code === PLUSSIGN ? 'Plus sign' : 'Hyphen minus') + ' is expected');
  41. }
  42. }
  43. // https://drafts.csswg.org/css-syntax/#urange
  44. // Informally, the <urange> production has three forms:
  45. // U+0001
  46. // Defines a range consisting of a single code point, in this case the code point "1".
  47. // U+0001-00ff
  48. // Defines a range of codepoints between the first and the second value, in this case
  49. // the range between "1" and "ff" (255 in decimal) inclusive.
  50. // U+00??
  51. // Defines a range of codepoints where the "?" characters range over all hex digits,
  52. // in this case defining the same as the value U+0000-00ff.
  53. // In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit).
  54. //
  55. // <urange> =
  56. // u '+' <ident-token> '?'* |
  57. // u <dimension-token> '?'* |
  58. // u <number-token> '?'* |
  59. // u <number-token> <dimension-token> |
  60. // u <number-token> <number-token> |
  61. // u '+' '?'+
  62. function scanUnicodeRange() {
  63. let hexLength = 0;
  64. switch (this.tokenType) {
  65. case types.Number:
  66. // u <number-token> '?'*
  67. // u <number-token> <dimension-token>
  68. // u <number-token> <number-token>
  69. hexLength = eatHexSequence.call(this, 1, true);
  70. if (this.isDelim(QUESTIONMARK)) {
  71. eatQuestionMarkSequence.call(this, 6 - hexLength);
  72. break;
  73. }
  74. if (this.tokenType === types.Dimension ||
  75. this.tokenType === types.Number) {
  76. startsWith.call(this, HYPHENMINUS);
  77. eatHexSequence.call(this, 1, false);
  78. break;
  79. }
  80. break;
  81. case types.Dimension:
  82. // u <dimension-token> '?'*
  83. hexLength = eatHexSequence.call(this, 1, true);
  84. if (hexLength > 0) {
  85. eatQuestionMarkSequence.call(this, 6 - hexLength);
  86. }
  87. break;
  88. default:
  89. // u '+' <ident-token> '?'*
  90. // u '+' '?'+
  91. this.eatDelim(PLUSSIGN);
  92. if (this.tokenType === types.Ident) {
  93. hexLength = eatHexSequence.call(this, 0, true);
  94. if (hexLength > 0) {
  95. eatQuestionMarkSequence.call(this, 6 - hexLength);
  96. }
  97. break;
  98. }
  99. if (this.isDelim(QUESTIONMARK)) {
  100. this.next();
  101. eatQuestionMarkSequence.call(this, 5);
  102. break;
  103. }
  104. this.error('Hex digit or question mark is expected');
  105. }
  106. }
  107. const name = 'UnicodeRange';
  108. const structure = {
  109. value: String
  110. };
  111. function parse() {
  112. const start = this.tokenStart;
  113. // U or u
  114. this.eatIdent('u');
  115. scanUnicodeRange.call(this);
  116. return {
  117. type: 'UnicodeRange',
  118. loc: this.getLocation(start, this.tokenStart),
  119. value: this.substrToCursor(start)
  120. };
  121. }
  122. function generate(node) {
  123. this.tokenize(node.value);
  124. }
  125. exports.generate = generate;
  126. exports.name = name;
  127. exports.parse = parse;
  128. exports.structure = structure;