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); } };