1 | var cmpChar = require('../../tokenizer').cmpChar;
|
---|
2 | var isDigit = require('../../tokenizer').isDigit;
|
---|
3 | var TYPE = require('../../tokenizer').TYPE;
|
---|
4 |
|
---|
5 | var WHITESPACE = TYPE.WhiteSpace;
|
---|
6 | var COMMENT = TYPE.Comment;
|
---|
7 | var IDENT = TYPE.Ident;
|
---|
8 | var NUMBER = TYPE.Number;
|
---|
9 | var DIMENSION = TYPE.Dimension;
|
---|
10 | var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
---|
11 | var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
---|
12 | var N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
|
---|
13 | var DISALLOW_SIGN = true;
|
---|
14 | var ALLOW_SIGN = false;
|
---|
15 |
|
---|
16 | function checkInteger(offset, disallowSign) {
|
---|
17 | var pos = this.scanner.tokenStart + offset;
|
---|
18 | var code = this.scanner.source.charCodeAt(pos);
|
---|
19 |
|
---|
20 | if (code === PLUSSIGN || code === HYPHENMINUS) {
|
---|
21 | if (disallowSign) {
|
---|
22 | this.error('Number sign is not allowed');
|
---|
23 | }
|
---|
24 | pos++;
|
---|
25 | }
|
---|
26 |
|
---|
27 | for (; pos < this.scanner.tokenEnd; pos++) {
|
---|
28 | if (!isDigit(this.scanner.source.charCodeAt(pos))) {
|
---|
29 | this.error('Integer is expected', pos);
|
---|
30 | }
|
---|
31 | }
|
---|
32 | }
|
---|
33 |
|
---|
34 | function checkTokenIsInteger(disallowSign) {
|
---|
35 | return checkInteger.call(this, 0, disallowSign);
|
---|
36 | }
|
---|
37 |
|
---|
38 | function expectCharCode(offset, code) {
|
---|
39 | if (!cmpChar(this.scanner.source, this.scanner.tokenStart + offset, code)) {
|
---|
40 | var msg = '';
|
---|
41 |
|
---|
42 | switch (code) {
|
---|
43 | case N:
|
---|
44 | msg = 'N is expected';
|
---|
45 | break;
|
---|
46 | case HYPHENMINUS:
|
---|
47 | msg = 'HyphenMinus is expected';
|
---|
48 | break;
|
---|
49 | }
|
---|
50 |
|
---|
51 | this.error(msg, this.scanner.tokenStart + offset);
|
---|
52 | }
|
---|
53 | }
|
---|
54 |
|
---|
55 | // ... <signed-integer>
|
---|
56 | // ... ['+' | '-'] <signless-integer>
|
---|
57 | function consumeB() {
|
---|
58 | var offset = 0;
|
---|
59 | var sign = 0;
|
---|
60 | var type = this.scanner.tokenType;
|
---|
61 |
|
---|
62 | while (type === WHITESPACE || type === COMMENT) {
|
---|
63 | type = this.scanner.lookupType(++offset);
|
---|
64 | }
|
---|
65 |
|
---|
66 | if (type !== NUMBER) {
|
---|
67 | if (this.scanner.isDelim(PLUSSIGN, offset) ||
|
---|
68 | this.scanner.isDelim(HYPHENMINUS, offset)) {
|
---|
69 | sign = this.scanner.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
|
---|
70 |
|
---|
71 | do {
|
---|
72 | type = this.scanner.lookupType(++offset);
|
---|
73 | } while (type === WHITESPACE || type === COMMENT);
|
---|
74 |
|
---|
75 | if (type !== NUMBER) {
|
---|
76 | this.scanner.skip(offset);
|
---|
77 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
78 | }
|
---|
79 | } else {
|
---|
80 | return null;
|
---|
81 | }
|
---|
82 | }
|
---|
83 |
|
---|
84 | if (offset > 0) {
|
---|
85 | this.scanner.skip(offset);
|
---|
86 | }
|
---|
87 |
|
---|
88 | if (sign === 0) {
|
---|
89 | type = this.scanner.source.charCodeAt(this.scanner.tokenStart);
|
---|
90 | if (type !== PLUSSIGN && type !== HYPHENMINUS) {
|
---|
91 | this.error('Number sign is expected');
|
---|
92 | }
|
---|
93 | }
|
---|
94 |
|
---|
95 | checkTokenIsInteger.call(this, sign !== 0);
|
---|
96 | return sign === HYPHENMINUS ? '-' + this.consume(NUMBER) : this.consume(NUMBER);
|
---|
97 | }
|
---|
98 |
|
---|
99 | // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
|
---|
100 | module.exports = {
|
---|
101 | name: 'AnPlusB',
|
---|
102 | structure: {
|
---|
103 | a: [String, null],
|
---|
104 | b: [String, null]
|
---|
105 | },
|
---|
106 | parse: function() {
|
---|
107 | /* eslint-disable brace-style*/
|
---|
108 | var start = this.scanner.tokenStart;
|
---|
109 | var a = null;
|
---|
110 | var b = null;
|
---|
111 |
|
---|
112 | // <integer>
|
---|
113 | if (this.scanner.tokenType === NUMBER) {
|
---|
114 | checkTokenIsInteger.call(this, ALLOW_SIGN);
|
---|
115 | b = this.consume(NUMBER);
|
---|
116 | }
|
---|
117 |
|
---|
118 | // -n
|
---|
119 | // -n <signed-integer>
|
---|
120 | // -n ['+' | '-'] <signless-integer>
|
---|
121 | // -n- <signless-integer>
|
---|
122 | // <dashndashdigit-ident>
|
---|
123 | else if (this.scanner.tokenType === IDENT && cmpChar(this.scanner.source, this.scanner.tokenStart, HYPHENMINUS)) {
|
---|
124 | a = '-1';
|
---|
125 |
|
---|
126 | expectCharCode.call(this, 1, N);
|
---|
127 |
|
---|
128 | switch (this.scanner.getTokenLength()) {
|
---|
129 | // -n
|
---|
130 | // -n <signed-integer>
|
---|
131 | // -n ['+' | '-'] <signless-integer>
|
---|
132 | case 2:
|
---|
133 | this.scanner.next();
|
---|
134 | b = consumeB.call(this);
|
---|
135 | break;
|
---|
136 |
|
---|
137 | // -n- <signless-integer>
|
---|
138 | case 3:
|
---|
139 | expectCharCode.call(this, 2, HYPHENMINUS);
|
---|
140 |
|
---|
141 | this.scanner.next();
|
---|
142 | this.scanner.skipSC();
|
---|
143 |
|
---|
144 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
145 |
|
---|
146 | b = '-' + this.consume(NUMBER);
|
---|
147 | break;
|
---|
148 |
|
---|
149 | // <dashndashdigit-ident>
|
---|
150 | default:
|
---|
151 | expectCharCode.call(this, 2, HYPHENMINUS);
|
---|
152 | checkInteger.call(this, 3, DISALLOW_SIGN);
|
---|
153 | this.scanner.next();
|
---|
154 |
|
---|
155 | b = this.scanner.substrToCursor(start + 2);
|
---|
156 | }
|
---|
157 | }
|
---|
158 |
|
---|
159 | // '+'? n
|
---|
160 | // '+'? n <signed-integer>
|
---|
161 | // '+'? n ['+' | '-'] <signless-integer>
|
---|
162 | // '+'? n- <signless-integer>
|
---|
163 | // '+'? <ndashdigit-ident>
|
---|
164 | else if (this.scanner.tokenType === IDENT || (this.scanner.isDelim(PLUSSIGN) && this.scanner.lookupType(1) === IDENT)) {
|
---|
165 | var sign = 0;
|
---|
166 | a = '1';
|
---|
167 |
|
---|
168 | // just ignore a plus
|
---|
169 | if (this.scanner.isDelim(PLUSSIGN)) {
|
---|
170 | sign = 1;
|
---|
171 | this.scanner.next();
|
---|
172 | }
|
---|
173 |
|
---|
174 | expectCharCode.call(this, 0, N);
|
---|
175 |
|
---|
176 | switch (this.scanner.getTokenLength()) {
|
---|
177 | // '+'? n
|
---|
178 | // '+'? n <signed-integer>
|
---|
179 | // '+'? n ['+' | '-'] <signless-integer>
|
---|
180 | case 1:
|
---|
181 | this.scanner.next();
|
---|
182 | b = consumeB.call(this);
|
---|
183 | break;
|
---|
184 |
|
---|
185 | // '+'? n- <signless-integer>
|
---|
186 | case 2:
|
---|
187 | expectCharCode.call(this, 1, HYPHENMINUS);
|
---|
188 |
|
---|
189 | this.scanner.next();
|
---|
190 | this.scanner.skipSC();
|
---|
191 |
|
---|
192 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
193 |
|
---|
194 | b = '-' + this.consume(NUMBER);
|
---|
195 | break;
|
---|
196 |
|
---|
197 | // '+'? <ndashdigit-ident>
|
---|
198 | default:
|
---|
199 | expectCharCode.call(this, 1, HYPHENMINUS);
|
---|
200 | checkInteger.call(this, 2, DISALLOW_SIGN);
|
---|
201 | this.scanner.next();
|
---|
202 |
|
---|
203 | b = this.scanner.substrToCursor(start + sign + 1);
|
---|
204 | }
|
---|
205 | }
|
---|
206 |
|
---|
207 | // <ndashdigit-dimension>
|
---|
208 | // <ndash-dimension> <signless-integer>
|
---|
209 | // <n-dimension>
|
---|
210 | // <n-dimension> <signed-integer>
|
---|
211 | // <n-dimension> ['+' | '-'] <signless-integer>
|
---|
212 | else if (this.scanner.tokenType === DIMENSION) {
|
---|
213 | var code = this.scanner.source.charCodeAt(this.scanner.tokenStart);
|
---|
214 | var sign = code === PLUSSIGN || code === HYPHENMINUS;
|
---|
215 |
|
---|
216 | for (var i = this.scanner.tokenStart + sign; i < this.scanner.tokenEnd; i++) {
|
---|
217 | if (!isDigit(this.scanner.source.charCodeAt(i))) {
|
---|
218 | break;
|
---|
219 | }
|
---|
220 | }
|
---|
221 |
|
---|
222 | if (i === this.scanner.tokenStart + sign) {
|
---|
223 | this.error('Integer is expected', this.scanner.tokenStart + sign);
|
---|
224 | }
|
---|
225 |
|
---|
226 | expectCharCode.call(this, i - this.scanner.tokenStart, N);
|
---|
227 | a = this.scanner.source.substring(start, i);
|
---|
228 |
|
---|
229 | // <n-dimension>
|
---|
230 | // <n-dimension> <signed-integer>
|
---|
231 | // <n-dimension> ['+' | '-'] <signless-integer>
|
---|
232 | if (i + 1 === this.scanner.tokenEnd) {
|
---|
233 | this.scanner.next();
|
---|
234 | b = consumeB.call(this);
|
---|
235 | } else {
|
---|
236 | expectCharCode.call(this, i - this.scanner.tokenStart + 1, HYPHENMINUS);
|
---|
237 |
|
---|
238 | // <ndash-dimension> <signless-integer>
|
---|
239 | if (i + 2 === this.scanner.tokenEnd) {
|
---|
240 | this.scanner.next();
|
---|
241 | this.scanner.skipSC();
|
---|
242 | checkTokenIsInteger.call(this, DISALLOW_SIGN);
|
---|
243 | b = '-' + this.consume(NUMBER);
|
---|
244 | }
|
---|
245 | // <ndashdigit-dimension>
|
---|
246 | else {
|
---|
247 | checkInteger.call(this, i - this.scanner.tokenStart + 2, DISALLOW_SIGN);
|
---|
248 | this.scanner.next();
|
---|
249 | b = this.scanner.substrToCursor(i + 1);
|
---|
250 | }
|
---|
251 | }
|
---|
252 | } else {
|
---|
253 | this.error();
|
---|
254 | }
|
---|
255 |
|
---|
256 | if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
|
---|
257 | a = a.substr(1);
|
---|
258 | }
|
---|
259 |
|
---|
260 | if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
|
---|
261 | b = b.substr(1);
|
---|
262 | }
|
---|
263 |
|
---|
264 | return {
|
---|
265 | type: 'AnPlusB',
|
---|
266 | loc: this.getLocation(start, this.scanner.tokenStart),
|
---|
267 | a: a,
|
---|
268 | b: b
|
---|
269 | };
|
---|
270 | },
|
---|
271 | generate: function(node) {
|
---|
272 | var a = node.a !== null && node.a !== undefined;
|
---|
273 | var b = node.b !== null && node.b !== undefined;
|
---|
274 |
|
---|
275 | if (a) {
|
---|
276 | this.chunk(
|
---|
277 | node.a === '+1' ? '+n' : // eslint-disable-line operator-linebreak, indent
|
---|
278 | node.a === '1' ? 'n' : // eslint-disable-line operator-linebreak, indent
|
---|
279 | node.a === '-1' ? '-n' : // eslint-disable-line operator-linebreak, indent
|
---|
280 | node.a + 'n' // eslint-disable-line operator-linebreak, indent
|
---|
281 | );
|
---|
282 |
|
---|
283 | if (b) {
|
---|
284 | b = String(node.b);
|
---|
285 | if (b.charAt(0) === '-' || b.charAt(0) === '+') {
|
---|
286 | this.chunk(b.charAt(0));
|
---|
287 | this.chunk(b.substr(1));
|
---|
288 | } else {
|
---|
289 | this.chunk('+');
|
---|
290 | this.chunk(b);
|
---|
291 | }
|
---|
292 | }
|
---|
293 | } else {
|
---|
294 | this.chunk(String(node.b));
|
---|
295 | }
|
---|
296 | }
|
---|
297 | };
|
---|