| var isHex = require('../../tokenizer').isHex; |
| var TYPE = require('../../tokenizer').TYPE; |
| |
| var IDENTIFIER = TYPE.Identifier; |
| var NUMBER = TYPE.Number; |
| var PLUSSIGN = TYPE.PlusSign; |
| var HYPHENMINUS = TYPE.HyphenMinus; |
| var FULLSTOP = TYPE.FullStop; |
| var QUESTIONMARK = TYPE.QuestionMark; |
| |
| function scanUnicodeNumber(scanner) { |
| for (var pos = scanner.tokenStart + 1; pos < scanner.tokenEnd; pos++) { |
| var code = scanner.source.charCodeAt(pos); |
| |
| // break on fullstop or hyperminus/plussign after exponent |
| if (code === FULLSTOP || code === PLUSSIGN) { |
| // break token, exclude symbol |
| scanner.tokenStart = pos; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // https://drafts.csswg.org/css-syntax-3/#urange |
| function scanUnicodeRange(scanner) { |
| var hexStart = scanner.tokenStart + 1; // skip + |
| var hexLength = 0; |
| |
| scan: { |
| if (scanner.tokenType === NUMBER) { |
| if (scanner.source.charCodeAt(scanner.tokenStart) !== FULLSTOP && scanUnicodeNumber(scanner)) { |
| scanner.next(); |
| } else if (scanner.source.charCodeAt(scanner.tokenStart) !== HYPHENMINUS) { |
| break scan; |
| } |
| } else { |
| scanner.next(); // PLUSSIGN |
| } |
| |
| if (scanner.tokenType === HYPHENMINUS) { |
| scanner.next(); |
| } |
| |
| if (scanner.tokenType === NUMBER) { |
| scanner.next(); |
| } |
| |
| if (scanner.tokenType === IDENTIFIER) { |
| scanner.next(); |
| } |
| |
| if (scanner.tokenStart === hexStart) { |
| scanner.error('Unexpected input', hexStart); |
| } |
| } |
| |
| // validate for U+x{1,6} or U+x{1,6}-x{1,6} |
| // where x is [0-9a-fA-F] |
| for (var i = hexStart, wasHyphenMinus = false; i < scanner.tokenStart; i++) { |
| var code = scanner.source.charCodeAt(i); |
| |
| if (isHex(code) === false && (code !== HYPHENMINUS || wasHyphenMinus)) { |
| scanner.error('Unexpected input', i); |
| } |
| |
| if (code === HYPHENMINUS) { |
| // hex sequence shouldn't be an empty |
| if (hexLength === 0) { |
| scanner.error('Unexpected input', i); |
| } |
| |
| wasHyphenMinus = true; |
| hexLength = 0; |
| } else { |
| hexLength++; |
| |
| // too long hex sequence |
| if (hexLength > 6) { |
| scanner.error('Too long hex sequence', i); |
| } |
| } |
| |
| } |
| |
| // check we have a non-zero sequence |
| if (hexLength === 0) { |
| scanner.error('Unexpected input', i - 1); |
| } |
| |
| // U+abc??? |
| if (!wasHyphenMinus) { |
| // consume as many U+003F QUESTION MARK (?) code points as possible |
| for (; hexLength < 6 && !scanner.eof; scanner.next()) { |
| if (scanner.tokenType !== QUESTIONMARK) { |
| break; |
| } |
| |
| hexLength++; |
| } |
| } |
| } |
| |
| module.exports = { |
| name: 'UnicodeRange', |
| structure: { |
| value: String |
| }, |
| parse: function() { |
| var start = this.scanner.tokenStart; |
| |
| this.scanner.next(); // U or u |
| scanUnicodeRange(this.scanner); |
| |
| return { |
| type: 'UnicodeRange', |
| loc: this.getLocation(start, this.scanner.tokenStart), |
| value: this.scanner.substrToCursor(start) |
| }; |
| }, |
| generate: function(node) { |
| this.chunk(node.value); |
| } |
| }; |