123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- var cmpChar = require('../../tokenizer').cmpChar;
- var isDigit = require('../../tokenizer').isDigit;
- var TYPE = require('../../tokenizer').TYPE;
- var WHITESPACE = TYPE.WhiteSpace;
- var COMMENT = TYPE.Comment;
- var IDENT = TYPE.Ident;
- var NUMBER = TYPE.Number;
- var DIMENSION = TYPE.Dimension;
- var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
- var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
- var N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
- var DISALLOW_SIGN = true;
- var ALLOW_SIGN = false;
- function checkInteger(offset, disallowSign) {
- var pos = this.scanner.tokenStart + offset;
- var code = this.scanner.source.charCodeAt(pos);
- if (code === PLUSSIGN || code === HYPHENMINUS) {
- if (disallowSign) {
- this.error('Number sign is not allowed');
- }
- pos++;
- }
- for (; pos < this.scanner.tokenEnd; pos++) {
- if (!isDigit(this.scanner.source.charCodeAt(pos))) {
- this.error('Integer is expected', pos);
- }
- }
- }
- function checkTokenIsInteger(disallowSign) {
- return checkInteger.call(this, 0, disallowSign);
- }
- function expectCharCode(offset, code) {
- if (!cmpChar(this.scanner.source, this.scanner.tokenStart + offset, code)) {
- var msg = '';
- switch (code) {
- case N:
- msg = 'N is expected';
- break;
- case HYPHENMINUS:
- msg = 'HyphenMinus is expected';
- break;
- }
- this.error(msg, this.scanner.tokenStart + offset);
- }
- }
- // ... <signed-integer>
- // ... ['+' | '-'] <signless-integer>
- function consumeB() {
- var offset = 0;
- var sign = 0;
- var type = this.scanner.tokenType;
- while (type === WHITESPACE || type === COMMENT) {
- type = this.scanner.lookupType(++offset);
- }
- if (type !== NUMBER) {
- if (this.scanner.isDelim(PLUSSIGN, offset) ||
- this.scanner.isDelim(HYPHENMINUS, offset)) {
- sign = this.scanner.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
- do {
- type = this.scanner.lookupType(++offset);
- } while (type === WHITESPACE || type === COMMENT);
- if (type !== NUMBER) {
- this.scanner.skip(offset);
- checkTokenIsInteger.call(this, DISALLOW_SIGN);
- }
- } else {
- return null;
- }
- }
- if (offset > 0) {
- this.scanner.skip(offset);
- }
- if (sign === 0) {
- type = this.scanner.source.charCodeAt(this.scanner.tokenStart);
- if (type !== PLUSSIGN && type !== HYPHENMINUS) {
- this.error('Number sign is expected');
- }
- }
- checkTokenIsInteger.call(this, sign !== 0);
- return sign === HYPHENMINUS ? '-' + this.consume(NUMBER) : this.consume(NUMBER);
- }
- // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
- module.exports = {
- name: 'AnPlusB',
- structure: {
- a: [String, null],
- b: [String, null]
- },
- parse: function() {
- /* eslint-disable brace-style*/
- var start = this.scanner.tokenStart;
- var a = null;
- var b = null;
- // <integer>
- if (this.scanner.tokenType === NUMBER) {
- checkTokenIsInteger.call(this, ALLOW_SIGN);
- b = this.consume(NUMBER);
- }
- // -n
- // -n <signed-integer>
- // -n ['+' | '-'] <signless-integer>
- // -n- <signless-integer>
- // <dashndashdigit-ident>
- else if (this.scanner.tokenType === IDENT && cmpChar(this.scanner.source, this.scanner.tokenStart, HYPHENMINUS)) {
- a = '-1';
- expectCharCode.call(this, 1, N);
- switch (this.scanner.getTokenLength()) {
- // -n
- // -n <signed-integer>
- // -n ['+' | '-'] <signless-integer>
- case 2:
- this.scanner.next();
- b = consumeB.call(this);
- break;
- // -n- <signless-integer>
- case 3:
- expectCharCode.call(this, 2, HYPHENMINUS);
- this.scanner.next();
- this.scanner.skipSC();
- checkTokenIsInteger.call(this, DISALLOW_SIGN);
- b = '-' + this.consume(NUMBER);
- break;
- // <dashndashdigit-ident>
- default:
- expectCharCode.call(this, 2, HYPHENMINUS);
- checkInteger.call(this, 3, DISALLOW_SIGN);
- this.scanner.next();
- b = this.scanner.substrToCursor(start + 2);
- }
- }
- // '+'? n
- // '+'? n <signed-integer>
- // '+'? n ['+' | '-'] <signless-integer>
- // '+'? n- <signless-integer>
- // '+'? <ndashdigit-ident>
- else if (this.scanner.tokenType === IDENT || (this.scanner.isDelim(PLUSSIGN) && this.scanner.lookupType(1) === IDENT)) {
- var sign = 0;
- a = '1';
- // just ignore a plus
- if (this.scanner.isDelim(PLUSSIGN)) {
- sign = 1;
- this.scanner.next();
- }
- expectCharCode.call(this, 0, N);
- switch (this.scanner.getTokenLength()) {
- // '+'? n
- // '+'? n <signed-integer>
- // '+'? n ['+' | '-'] <signless-integer>
- case 1:
- this.scanner.next();
- b = consumeB.call(this);
- break;
- // '+'? n- <signless-integer>
- case 2:
- expectCharCode.call(this, 1, HYPHENMINUS);
- this.scanner.next();
- this.scanner.skipSC();
- checkTokenIsInteger.call(this, DISALLOW_SIGN);
- b = '-' + this.consume(NUMBER);
- break;
- // '+'? <ndashdigit-ident>
- default:
- expectCharCode.call(this, 1, HYPHENMINUS);
- checkInteger.call(this, 2, DISALLOW_SIGN);
- this.scanner.next();
- b = this.scanner.substrToCursor(start + sign + 1);
- }
- }
- // <ndashdigit-dimension>
- // <ndash-dimension> <signless-integer>
- // <n-dimension>
- // <n-dimension> <signed-integer>
- // <n-dimension> ['+' | '-'] <signless-integer>
- else if (this.scanner.tokenType === DIMENSION) {
- var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
- var sign = code === PLUSSIGN || code === HYPHENMINUS;
- for (var i = this.scanner.tokenStart + sign; i < this.scanner.tokenEnd; i++) {
- if (!isDigit(this.scanner.source.charCodeAt(i))) {
- break;
- }
- }
- if (i === this.scanner.tokenStart + sign) {
- this.error('Integer is expected', this.scanner.tokenStart + sign);
- }
- expectCharCode.call(this, i - this.scanner.tokenStart, N);
- a = this.scanner.source.substring(start, i);
- // <n-dimension>
- // <n-dimension> <signed-integer>
- // <n-dimension> ['+' | '-'] <signless-integer>
- if (i + 1 === this.scanner.tokenEnd) {
- this.scanner.next();
- b = consumeB.call(this);
- } else {
- expectCharCode.call(this, i - this.scanner.tokenStart + 1, HYPHENMINUS);
- // <ndash-dimension> <signless-integer>
- if (i + 2 === this.scanner.tokenEnd) {
- this.scanner.next();
- this.scanner.skipSC();
- checkTokenIsInteger.call(this, DISALLOW_SIGN);
- b = '-' + this.consume(NUMBER);
- }
- // <ndashdigit-dimension>
- else {
- checkInteger.call(this, i - this.scanner.tokenStart + 2, DISALLOW_SIGN);
- this.scanner.next();
- b = this.scanner.substrToCursor(i + 1);
- }
- }
- } else {
- this.error();
- }
- if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
- a = a.substr(1);
- }
- if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
- b = b.substr(1);
- }
- return {
- type: 'AnPlusB',
- loc: this.getLocation(start, this.scanner.tokenStart),
- a: a,
- b: b
- };
- },
- generate: function(node) {
- var a = node.a !== null && node.a !== undefined;
- var b = node.b !== null && node.b !== undefined;
- if (a) {
- this.chunk(
- node.a === '+1' ? '+n' : // eslint-disable-line operator-linebreak, indent
- node.a === '1' ? 'n' : // eslint-disable-line operator-linebreak, indent
- node.a === '-1' ? '-n' : // eslint-disable-line operator-linebreak, indent
- node.a + 'n' // eslint-disable-line operator-linebreak, indent
- );
- if (b) {
- b = String(node.b);
- if (b.charAt(0) === '-' || b.charAt(0) === '+') {
- this.chunk(b.charAt(0));
- this.chunk(b.substr(1));
- } else {
- this.chunk('+');
- this.chunk(b);
- }
- }
- } else {
- this.chunk(String(node.b));
- }
- }
- };
|