var cmpChar = require('../../tokenizer').cmpChar; var isNumber = require('../../tokenizer').isNumber; var TYPE = require('../../tokenizer').TYPE; var IDENTIFIER = TYPE.Identifier; var NUMBER = TYPE.Number; var PLUSSIGN = TYPE.PlusSign; var HYPHENMINUS = TYPE.HyphenMinus; var N = 110; // 'n'.charCodeAt(0) var DISALLOW_SIGN = true; var ALLOW_SIGN = false; function checkTokenIsInteger(scanner, disallowSign) { var pos = scanner.tokenStart; if (scanner.source.charCodeAt(pos) === PLUSSIGN || scanner.source.charCodeAt(pos) === HYPHENMINUS) { if (disallowSign) { scanner.error(); } pos++; } for (; pos < scanner.tokenEnd; pos++) { if (!isNumber(scanner.source.charCodeAt(pos))) { scanner.error('Unexpected input', pos); } } } // 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() { var start = this.scanner.tokenStart; var end = start; var prefix = ''; var a = null; var b = null; if (this.scanner.tokenType === NUMBER || this.scanner.tokenType === PLUSSIGN) { checkTokenIsInteger(this.scanner, ALLOW_SIGN); prefix = this.scanner.getTokenValue(); this.scanner.next(); end = this.scanner.tokenStart; } if (this.scanner.tokenType === IDENTIFIER) { var bStart = this.scanner.tokenStart; if (cmpChar(this.scanner.source, bStart, HYPHENMINUS)) { if (prefix === '') { prefix = '-'; bStart++; } else { this.scanner.error('Unexpected hyphen minus'); } } if (!cmpChar(this.scanner.source, bStart, N)) { this.scanner.error(); } a = prefix === '' ? '1' : prefix === '+' ? '+1' : prefix === '-' ? '-1' : prefix; var len = this.scanner.tokenEnd - bStart; if (len > 1) { // ..n-.. if (this.scanner.source.charCodeAt(bStart + 1) !== HYPHENMINUS) { this.scanner.error('Unexpected input', bStart + 1); } if (len > 2) { // ..n-{number}.. this.scanner.tokenStart = bStart + 2; } else { // ..n- {number} this.scanner.next(); this.scanner.skipSC(); } checkTokenIsInteger(this.scanner, DISALLOW_SIGN); b = '-' + this.scanner.getTokenValue(); this.scanner.next(); end = this.scanner.tokenStart; } else { prefix = ''; this.scanner.next(); end = this.scanner.tokenStart; this.scanner.skipSC(); if (this.scanner.tokenType === HYPHENMINUS || this.scanner.tokenType === PLUSSIGN) { prefix = this.scanner.getTokenValue(); this.scanner.next(); this.scanner.skipSC(); } if (this.scanner.tokenType === NUMBER) { checkTokenIsInteger(this.scanner, prefix !== ''); if (!isNumber(this.scanner.source.charCodeAt(this.scanner.tokenStart))) { prefix = this.scanner.source.charAt(this.scanner.tokenStart); this.scanner.tokenStart++; } if (prefix === '') { // should be an operator before number this.scanner.error(); } else if (prefix === '+') { // plus is using by default prefix = ''; } b = prefix + this.scanner.getTokenValue(); this.scanner.next(); end = this.scanner.tokenStart; } else { if (prefix) { this.scanner.eat(NUMBER); } } } } else { if (prefix === '' || prefix === '+') { // no number this.scanner.error( 'Number or identifier is expected', this.scanner.tokenStart + ( this.scanner.tokenType === PLUSSIGN || this.scanner.tokenType === HYPHENMINUS ) ); } b = prefix; } return { type: 'AnPlusB', loc: this.getLocation(start, end), 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' : node.a === '1' ? 'n' : node.a === '-1' ? '-n' : node.a + 'n' ); 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)); } } };