UnicodeRange.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. var isHex = require('../../tokenizer').isHex;
  2. var TYPE = require('../../tokenizer').TYPE;
  3. var IDENTIFIER = TYPE.Identifier;
  4. var NUMBER = TYPE.Number;
  5. var PLUSSIGN = TYPE.PlusSign;
  6. var HYPHENMINUS = TYPE.HyphenMinus;
  7. var FULLSTOP = TYPE.FullStop;
  8. var QUESTIONMARK = TYPE.QuestionMark;
  9. function scanUnicodeNumber(scanner) {
  10. for (var pos = scanner.tokenStart + 1; pos < scanner.tokenEnd; pos++) {
  11. var code = scanner.source.charCodeAt(pos);
  12. // break on fullstop or hyperminus/plussign after exponent
  13. if (code === FULLSTOP || code === PLUSSIGN) {
  14. // break token, exclude symbol
  15. scanner.tokenStart = pos;
  16. return false;
  17. }
  18. }
  19. return true;
  20. }
  21. // https://drafts.csswg.org/css-syntax-3/#urange
  22. function scanUnicodeRange(scanner) {
  23. var hexStart = scanner.tokenStart + 1; // skip +
  24. var hexLength = 0;
  25. scan: {
  26. if (scanner.tokenType === NUMBER) {
  27. if (scanner.source.charCodeAt(scanner.tokenStart) !== FULLSTOP && scanUnicodeNumber(scanner)) {
  28. scanner.next();
  29. } else if (scanner.source.charCodeAt(scanner.tokenStart) !== HYPHENMINUS) {
  30. break scan;
  31. }
  32. } else {
  33. scanner.next(); // PLUSSIGN
  34. }
  35. if (scanner.tokenType === HYPHENMINUS) {
  36. scanner.next();
  37. }
  38. if (scanner.tokenType === NUMBER) {
  39. scanner.next();
  40. }
  41. if (scanner.tokenType === IDENTIFIER) {
  42. scanner.next();
  43. }
  44. if (scanner.tokenStart === hexStart) {
  45. scanner.error('Unexpected input', hexStart);
  46. }
  47. }
  48. // validate for U+x{1,6} or U+x{1,6}-x{1,6}
  49. // where x is [0-9a-fA-F]
  50. for (var i = hexStart, wasHyphenMinus = false; i < scanner.tokenStart; i++) {
  51. var code = scanner.source.charCodeAt(i);
  52. if (isHex(code) === false && (code !== HYPHENMINUS || wasHyphenMinus)) {
  53. scanner.error('Unexpected input', i);
  54. }
  55. if (code === HYPHENMINUS) {
  56. // hex sequence shouldn't be an empty
  57. if (hexLength === 0) {
  58. scanner.error('Unexpected input', i);
  59. }
  60. wasHyphenMinus = true;
  61. hexLength = 0;
  62. } else {
  63. hexLength++;
  64. // too long hex sequence
  65. if (hexLength > 6) {
  66. scanner.error('Too long hex sequence', i);
  67. }
  68. }
  69. }
  70. // check we have a non-zero sequence
  71. if (hexLength === 0) {
  72. scanner.error('Unexpected input', i - 1);
  73. }
  74. // U+abc???
  75. if (!wasHyphenMinus) {
  76. // consume as many U+003F QUESTION MARK (?) code points as possible
  77. for (; hexLength < 6 && !scanner.eof; scanner.next()) {
  78. if (scanner.tokenType !== QUESTIONMARK) {
  79. break;
  80. }
  81. hexLength++;
  82. }
  83. }
  84. }
  85. module.exports = {
  86. name: 'UnicodeRange',
  87. structure: {
  88. value: String
  89. },
  90. parse: function() {
  91. var start = this.scanner.tokenStart;
  92. this.scanner.next(); // U or u
  93. scanUnicodeRange(this.scanner);
  94. return {
  95. type: 'UnicodeRange',
  96. loc: this.getLocation(start, this.scanner.tokenStart),
  97. value: this.scanner.substrToCursor(start)
  98. };
  99. },
  100. generate: function(node) {
  101. this.chunk(node.value);
  102. }
  103. };