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