source: imaps-frontend/node_modules/braces/lib/parse.js@ 0c6b92a

main
Last change on this file since 0c6b92a was 0c6b92a, checked in by stefan toskovski <stefantoska84@…>, 5 weeks ago

Pred finalna verzija

  • Property mode set to 100644
File size: 6.7 KB
Line 
1'use strict';
2
3const stringify = require('./stringify');
4
5/**
6 * Constants
7 */
8
9const {
10 MAX_LENGTH,
11 CHAR_BACKSLASH, /* \ */
12 CHAR_BACKTICK, /* ` */
13 CHAR_COMMA, /* , */
14 CHAR_DOT, /* . */
15 CHAR_LEFT_PARENTHESES, /* ( */
16 CHAR_RIGHT_PARENTHESES, /* ) */
17 CHAR_LEFT_CURLY_BRACE, /* { */
18 CHAR_RIGHT_CURLY_BRACE, /* } */
19 CHAR_LEFT_SQUARE_BRACKET, /* [ */
20 CHAR_RIGHT_SQUARE_BRACKET, /* ] */
21 CHAR_DOUBLE_QUOTE, /* " */
22 CHAR_SINGLE_QUOTE, /* ' */
23 CHAR_NO_BREAK_SPACE,
24 CHAR_ZERO_WIDTH_NOBREAK_SPACE
25} = require('./constants');
26
27/**
28 * parse
29 */
30
31const parse = (input, options = {}) => {
32 if (typeof input !== 'string') {
33 throw new TypeError('Expected a string');
34 }
35
36 const opts = options || {};
37 const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
38 if (input.length > max) {
39 throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
40 }
41
42 const ast = { type: 'root', input, nodes: [] };
43 const stack = [ast];
44 let block = ast;
45 let prev = ast;
46 let brackets = 0;
47 const length = input.length;
48 let index = 0;
49 let depth = 0;
50 let value;
51
52 /**
53 * Helpers
54 */
55
56 const advance = () => input[index++];
57 const push = node => {
58 if (node.type === 'text' && prev.type === 'dot') {
59 prev.type = 'text';
60 }
61
62 if (prev && prev.type === 'text' && node.type === 'text') {
63 prev.value += node.value;
64 return;
65 }
66
67 block.nodes.push(node);
68 node.parent = block;
69 node.prev = prev;
70 prev = node;
71 return node;
72 };
73
74 push({ type: 'bos' });
75
76 while (index < length) {
77 block = stack[stack.length - 1];
78 value = advance();
79
80 /**
81 * Invalid chars
82 */
83
84 if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) {
85 continue;
86 }
87
88 /**
89 * Escaped chars
90 */
91
92 if (value === CHAR_BACKSLASH) {
93 push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() });
94 continue;
95 }
96
97 /**
98 * Right square bracket (literal): ']'
99 */
100
101 if (value === CHAR_RIGHT_SQUARE_BRACKET) {
102 push({ type: 'text', value: '\\' + value });
103 continue;
104 }
105
106 /**
107 * Left square bracket: '['
108 */
109
110 if (value === CHAR_LEFT_SQUARE_BRACKET) {
111 brackets++;
112
113 let next;
114
115 while (index < length && (next = advance())) {
116 value += next;
117
118 if (next === CHAR_LEFT_SQUARE_BRACKET) {
119 brackets++;
120 continue;
121 }
122
123 if (next === CHAR_BACKSLASH) {
124 value += advance();
125 continue;
126 }
127
128 if (next === CHAR_RIGHT_SQUARE_BRACKET) {
129 brackets--;
130
131 if (brackets === 0) {
132 break;
133 }
134 }
135 }
136
137 push({ type: 'text', value });
138 continue;
139 }
140
141 /**
142 * Parentheses
143 */
144
145 if (value === CHAR_LEFT_PARENTHESES) {
146 block = push({ type: 'paren', nodes: [] });
147 stack.push(block);
148 push({ type: 'text', value });
149 continue;
150 }
151
152 if (value === CHAR_RIGHT_PARENTHESES) {
153 if (block.type !== 'paren') {
154 push({ type: 'text', value });
155 continue;
156 }
157 block = stack.pop();
158 push({ type: 'text', value });
159 block = stack[stack.length - 1];
160 continue;
161 }
162
163 /**
164 * Quotes: '|"|`
165 */
166
167 if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
168 const open = value;
169 let next;
170
171 if (options.keepQuotes !== true) {
172 value = '';
173 }
174
175 while (index < length && (next = advance())) {
176 if (next === CHAR_BACKSLASH) {
177 value += next + advance();
178 continue;
179 }
180
181 if (next === open) {
182 if (options.keepQuotes === true) value += next;
183 break;
184 }
185
186 value += next;
187 }
188
189 push({ type: 'text', value });
190 continue;
191 }
192
193 /**
194 * Left curly brace: '{'
195 */
196
197 if (value === CHAR_LEFT_CURLY_BRACE) {
198 depth++;
199
200 const dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
201 const brace = {
202 type: 'brace',
203 open: true,
204 close: false,
205 dollar,
206 depth,
207 commas: 0,
208 ranges: 0,
209 nodes: []
210 };
211
212 block = push(brace);
213 stack.push(block);
214 push({ type: 'open', value });
215 continue;
216 }
217
218 /**
219 * Right curly brace: '}'
220 */
221
222 if (value === CHAR_RIGHT_CURLY_BRACE) {
223 if (block.type !== 'brace') {
224 push({ type: 'text', value });
225 continue;
226 }
227
228 const type = 'close';
229 block = stack.pop();
230 block.close = true;
231
232 push({ type, value });
233 depth--;
234
235 block = stack[stack.length - 1];
236 continue;
237 }
238
239 /**
240 * Comma: ','
241 */
242
243 if (value === CHAR_COMMA && depth > 0) {
244 if (block.ranges > 0) {
245 block.ranges = 0;
246 const open = block.nodes.shift();
247 block.nodes = [open, { type: 'text', value: stringify(block) }];
248 }
249
250 push({ type: 'comma', value });
251 block.commas++;
252 continue;
253 }
254
255 /**
256 * Dot: '.'
257 */
258
259 if (value === CHAR_DOT && depth > 0 && block.commas === 0) {
260 const siblings = block.nodes;
261
262 if (depth === 0 || siblings.length === 0) {
263 push({ type: 'text', value });
264 continue;
265 }
266
267 if (prev.type === 'dot') {
268 block.range = [];
269 prev.value += value;
270 prev.type = 'range';
271
272 if (block.nodes.length !== 3 && block.nodes.length !== 5) {
273 block.invalid = true;
274 block.ranges = 0;
275 prev.type = 'text';
276 continue;
277 }
278
279 block.ranges++;
280 block.args = [];
281 continue;
282 }
283
284 if (prev.type === 'range') {
285 siblings.pop();
286
287 const before = siblings[siblings.length - 1];
288 before.value += prev.value + value;
289 prev = before;
290 block.ranges--;
291 continue;
292 }
293
294 push({ type: 'dot', value });
295 continue;
296 }
297
298 /**
299 * Text
300 */
301
302 push({ type: 'text', value });
303 }
304
305 // Mark imbalanced braces and brackets as invalid
306 do {
307 block = stack.pop();
308
309 if (block.type !== 'root') {
310 block.nodes.forEach(node => {
311 if (!node.nodes) {
312 if (node.type === 'open') node.isOpen = true;
313 if (node.type === 'close') node.isClose = true;
314 if (!node.nodes) node.type = 'text';
315 node.invalid = true;
316 }
317 });
318
319 // get the location of the block on parent.nodes (block's siblings)
320 const parent = stack[stack.length - 1];
321 const index = parent.nodes.indexOf(block);
322 // replace the (invalid) block with it's nodes
323 parent.nodes.splice(index, 1, ...block.nodes);
324 }
325 } while (stack.length > 0);
326
327 push({ type: 'eos' });
328 return ast;
329};
330
331module.exports = parse;
Note: See TracBrowser for help on using the repository browser.