source: trip-planner-front/node_modules/braces/lib/parse.js@ fa375fe

Last change on this file since fa375fe was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

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