source: trip-planner-front/node_modules/jsonc-parser/lib/esm/impl/parser.js@ 76712b2

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

initial commit

  • Property mode set to 100644
File size: 22.9 KB
Line 
1/*---------------------------------------------------------------------------------------------
2 * Copyright (c) Microsoft Corporation. All rights reserved.
3 * Licensed under the MIT License. See License.txt in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5'use strict';
6import { createScanner } from './scanner';
7var ParseOptions;
8(function (ParseOptions) {
9 ParseOptions.DEFAULT = {
10 allowTrailingComma: false
11 };
12})(ParseOptions || (ParseOptions = {}));
13/**
14 * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
15 */
16export function getLocation(text, position) {
17 var segments = []; // strings or numbers
18 var earlyReturnException = new Object();
19 var previousNode = undefined;
20 var previousNodeInst = {
21 value: {},
22 offset: 0,
23 length: 0,
24 type: 'object',
25 parent: undefined
26 };
27 var isAtPropertyKey = false;
28 function setPreviousNode(value, offset, length, type) {
29 previousNodeInst.value = value;
30 previousNodeInst.offset = offset;
31 previousNodeInst.length = length;
32 previousNodeInst.type = type;
33 previousNodeInst.colonOffset = undefined;
34 previousNode = previousNodeInst;
35 }
36 try {
37 visit(text, {
38 onObjectBegin: function (offset, length) {
39 if (position <= offset) {
40 throw earlyReturnException;
41 }
42 previousNode = undefined;
43 isAtPropertyKey = position > offset;
44 segments.push(''); // push a placeholder (will be replaced)
45 },
46 onObjectProperty: function (name, offset, length) {
47 if (position < offset) {
48 throw earlyReturnException;
49 }
50 setPreviousNode(name, offset, length, 'property');
51 segments[segments.length - 1] = name;
52 if (position <= offset + length) {
53 throw earlyReturnException;
54 }
55 },
56 onObjectEnd: function (offset, length) {
57 if (position <= offset) {
58 throw earlyReturnException;
59 }
60 previousNode = undefined;
61 segments.pop();
62 },
63 onArrayBegin: function (offset, length) {
64 if (position <= offset) {
65 throw earlyReturnException;
66 }
67 previousNode = undefined;
68 segments.push(0);
69 },
70 onArrayEnd: function (offset, length) {
71 if (position <= offset) {
72 throw earlyReturnException;
73 }
74 previousNode = undefined;
75 segments.pop();
76 },
77 onLiteralValue: function (value, offset, length) {
78 if (position < offset) {
79 throw earlyReturnException;
80 }
81 setPreviousNode(value, offset, length, getNodeType(value));
82 if (position <= offset + length) {
83 throw earlyReturnException;
84 }
85 },
86 onSeparator: function (sep, offset, length) {
87 if (position <= offset) {
88 throw earlyReturnException;
89 }
90 if (sep === ':' && previousNode && previousNode.type === 'property') {
91 previousNode.colonOffset = offset;
92 isAtPropertyKey = false;
93 previousNode = undefined;
94 }
95 else if (sep === ',') {
96 var last = segments[segments.length - 1];
97 if (typeof last === 'number') {
98 segments[segments.length - 1] = last + 1;
99 }
100 else {
101 isAtPropertyKey = true;
102 segments[segments.length - 1] = '';
103 }
104 previousNode = undefined;
105 }
106 }
107 });
108 }
109 catch (e) {
110 if (e !== earlyReturnException) {
111 throw e;
112 }
113 }
114 return {
115 path: segments,
116 previousNode: previousNode,
117 isAtPropertyKey: isAtPropertyKey,
118 matches: function (pattern) {
119 var k = 0;
120 for (var i = 0; k < pattern.length && i < segments.length; i++) {
121 if (pattern[k] === segments[i] || pattern[k] === '*') {
122 k++;
123 }
124 else if (pattern[k] !== '**') {
125 return false;
126 }
127 }
128 return k === pattern.length;
129 }
130 };
131}
132/**
133 * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
134 * Therefore always check the errors list to find out if the input was valid.
135 */
136export function parse(text, errors, options) {
137 if (errors === void 0) { errors = []; }
138 if (options === void 0) { options = ParseOptions.DEFAULT; }
139 var currentProperty = null;
140 var currentParent = [];
141 var previousParents = [];
142 function onValue(value) {
143 if (Array.isArray(currentParent)) {
144 currentParent.push(value);
145 }
146 else if (currentProperty !== null) {
147 currentParent[currentProperty] = value;
148 }
149 }
150 var visitor = {
151 onObjectBegin: function () {
152 var object = {};
153 onValue(object);
154 previousParents.push(currentParent);
155 currentParent = object;
156 currentProperty = null;
157 },
158 onObjectProperty: function (name) {
159 currentProperty = name;
160 },
161 onObjectEnd: function () {
162 currentParent = previousParents.pop();
163 },
164 onArrayBegin: function () {
165 var array = [];
166 onValue(array);
167 previousParents.push(currentParent);
168 currentParent = array;
169 currentProperty = null;
170 },
171 onArrayEnd: function () {
172 currentParent = previousParents.pop();
173 },
174 onLiteralValue: onValue,
175 onError: function (error, offset, length) {
176 errors.push({ error: error, offset: offset, length: length });
177 }
178 };
179 visit(text, visitor, options);
180 return currentParent[0];
181}
182/**
183 * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
184 */
185export function parseTree(text, errors, options) {
186 if (errors === void 0) { errors = []; }
187 if (options === void 0) { options = ParseOptions.DEFAULT; }
188 var currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
189 function ensurePropertyComplete(endOffset) {
190 if (currentParent.type === 'property') {
191 currentParent.length = endOffset - currentParent.offset;
192 currentParent = currentParent.parent;
193 }
194 }
195 function onValue(valueNode) {
196 currentParent.children.push(valueNode);
197 return valueNode;
198 }
199 var visitor = {
200 onObjectBegin: function (offset) {
201 currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] });
202 },
203 onObjectProperty: function (name, offset, length) {
204 currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] });
205 currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent });
206 },
207 onObjectEnd: function (offset, length) {
208 ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete
209 currentParent.length = offset + length - currentParent.offset;
210 currentParent = currentParent.parent;
211 ensurePropertyComplete(offset + length);
212 },
213 onArrayBegin: function (offset, length) {
214 currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] });
215 },
216 onArrayEnd: function (offset, length) {
217 currentParent.length = offset + length - currentParent.offset;
218 currentParent = currentParent.parent;
219 ensurePropertyComplete(offset + length);
220 },
221 onLiteralValue: function (value, offset, length) {
222 onValue({ type: getNodeType(value), offset: offset, length: length, parent: currentParent, value: value });
223 ensurePropertyComplete(offset + length);
224 },
225 onSeparator: function (sep, offset, length) {
226 if (currentParent.type === 'property') {
227 if (sep === ':') {
228 currentParent.colonOffset = offset;
229 }
230 else if (sep === ',') {
231 ensurePropertyComplete(offset);
232 }
233 }
234 },
235 onError: function (error, offset, length) {
236 errors.push({ error: error, offset: offset, length: length });
237 }
238 };
239 visit(text, visitor, options);
240 var result = currentParent.children[0];
241 if (result) {
242 delete result.parent;
243 }
244 return result;
245}
246/**
247 * Finds the node at the given path in a JSON DOM.
248 */
249export function findNodeAtLocation(root, path) {
250 if (!root) {
251 return undefined;
252 }
253 var node = root;
254 for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
255 var segment = path_1[_i];
256 if (typeof segment === 'string') {
257 if (node.type !== 'object' || !Array.isArray(node.children)) {
258 return undefined;
259 }
260 var found = false;
261 for (var _a = 0, _b = node.children; _a < _b.length; _a++) {
262 var propertyNode = _b[_a];
263 if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {
264 node = propertyNode.children[1];
265 found = true;
266 break;
267 }
268 }
269 if (!found) {
270 return undefined;
271 }
272 }
273 else {
274 var index = segment;
275 if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
276 return undefined;
277 }
278 node = node.children[index];
279 }
280 }
281 return node;
282}
283/**
284 * Gets the JSON path of the given JSON DOM node
285 */
286export function getNodePath(node) {
287 if (!node.parent || !node.parent.children) {
288 return [];
289 }
290 var path = getNodePath(node.parent);
291 if (node.parent.type === 'property') {
292 var key = node.parent.children[0].value;
293 path.push(key);
294 }
295 else if (node.parent.type === 'array') {
296 var index = node.parent.children.indexOf(node);
297 if (index !== -1) {
298 path.push(index);
299 }
300 }
301 return path;
302}
303/**
304 * Evaluates the JavaScript object of the given JSON DOM node
305 */
306export function getNodeValue(node) {
307 switch (node.type) {
308 case 'array':
309 return node.children.map(getNodeValue);
310 case 'object':
311 var obj = Object.create(null);
312 for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
313 var prop = _a[_i];
314 var valueNode = prop.children[1];
315 if (valueNode) {
316 obj[prop.children[0].value] = getNodeValue(valueNode);
317 }
318 }
319 return obj;
320 case 'null':
321 case 'string':
322 case 'number':
323 case 'boolean':
324 return node.value;
325 default:
326 return undefined;
327 }
328}
329export function contains(node, offset, includeRightBound) {
330 if (includeRightBound === void 0) { includeRightBound = false; }
331 return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
332}
333/**
334 * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
335 */
336export function findNodeAtOffset(node, offset, includeRightBound) {
337 if (includeRightBound === void 0) { includeRightBound = false; }
338 if (contains(node, offset, includeRightBound)) {
339 var children = node.children;
340 if (Array.isArray(children)) {
341 for (var i = 0; i < children.length && children[i].offset <= offset; i++) {
342 var item = findNodeAtOffset(children[i], offset, includeRightBound);
343 if (item) {
344 return item;
345 }
346 }
347 }
348 return node;
349 }
350 return undefined;
351}
352/**
353 * Parses the given text and invokes the visitor functions for each object, array and literal reached.
354 */
355export function visit(text, visitor, options) {
356 if (options === void 0) { options = ParseOptions.DEFAULT; }
357 var _scanner = createScanner(text, false);
358 function toNoArgVisit(visitFunction) {
359 return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
360 }
361 function toOneArgVisit(visitFunction) {
362 return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
363 }
364 var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
365 var disallowComments = options && options.disallowComments;
366 var allowTrailingComma = options && options.allowTrailingComma;
367 function scanNext() {
368 while (true) {
369 var token = _scanner.scan();
370 switch (_scanner.getTokenError()) {
371 case 4 /* InvalidUnicode */:
372 handleError(14 /* InvalidUnicode */);
373 break;
374 case 5 /* InvalidEscapeCharacter */:
375 handleError(15 /* InvalidEscapeCharacter */);
376 break;
377 case 3 /* UnexpectedEndOfNumber */:
378 handleError(13 /* UnexpectedEndOfNumber */);
379 break;
380 case 1 /* UnexpectedEndOfComment */:
381 if (!disallowComments) {
382 handleError(11 /* UnexpectedEndOfComment */);
383 }
384 break;
385 case 2 /* UnexpectedEndOfString */:
386 handleError(12 /* UnexpectedEndOfString */);
387 break;
388 case 6 /* InvalidCharacter */:
389 handleError(16 /* InvalidCharacter */);
390 break;
391 }
392 switch (token) {
393 case 12 /* LineCommentTrivia */:
394 case 13 /* BlockCommentTrivia */:
395 if (disallowComments) {
396 handleError(10 /* InvalidCommentToken */);
397 }
398 else {
399 onComment();
400 }
401 break;
402 case 16 /* Unknown */:
403 handleError(1 /* InvalidSymbol */);
404 break;
405 case 15 /* Trivia */:
406 case 14 /* LineBreakTrivia */:
407 break;
408 default:
409 return token;
410 }
411 }
412 }
413 function handleError(error, skipUntilAfter, skipUntil) {
414 if (skipUntilAfter === void 0) { skipUntilAfter = []; }
415 if (skipUntil === void 0) { skipUntil = []; }
416 onError(error);
417 if (skipUntilAfter.length + skipUntil.length > 0) {
418 var token = _scanner.getToken();
419 while (token !== 17 /* EOF */) {
420 if (skipUntilAfter.indexOf(token) !== -1) {
421 scanNext();
422 break;
423 }
424 else if (skipUntil.indexOf(token) !== -1) {
425 break;
426 }
427 token = scanNext();
428 }
429 }
430 }
431 function parseString(isValue) {
432 var value = _scanner.getTokenValue();
433 if (isValue) {
434 onLiteralValue(value);
435 }
436 else {
437 onObjectProperty(value);
438 }
439 scanNext();
440 return true;
441 }
442 function parseLiteral() {
443 switch (_scanner.getToken()) {
444 case 11 /* NumericLiteral */:
445 var tokenValue = _scanner.getTokenValue();
446 var value = Number(tokenValue);
447 if (isNaN(value)) {
448 handleError(2 /* InvalidNumberFormat */);
449 value = 0;
450 }
451 onLiteralValue(value);
452 break;
453 case 7 /* NullKeyword */:
454 onLiteralValue(null);
455 break;
456 case 8 /* TrueKeyword */:
457 onLiteralValue(true);
458 break;
459 case 9 /* FalseKeyword */:
460 onLiteralValue(false);
461 break;
462 default:
463 return false;
464 }
465 scanNext();
466 return true;
467 }
468 function parseProperty() {
469 if (_scanner.getToken() !== 10 /* StringLiteral */) {
470 handleError(3 /* PropertyNameExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
471 return false;
472 }
473 parseString(false);
474 if (_scanner.getToken() === 6 /* ColonToken */) {
475 onSeparator(':');
476 scanNext(); // consume colon
477 if (!parseValue()) {
478 handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
479 }
480 }
481 else {
482 handleError(5 /* ColonExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
483 }
484 return true;
485 }
486 function parseObject() {
487 onObjectBegin();
488 scanNext(); // consume open brace
489 var needsComma = false;
490 while (_scanner.getToken() !== 2 /* CloseBraceToken */ && _scanner.getToken() !== 17 /* EOF */) {
491 if (_scanner.getToken() === 5 /* CommaToken */) {
492 if (!needsComma) {
493 handleError(4 /* ValueExpected */, [], []);
494 }
495 onSeparator(',');
496 scanNext(); // consume comma
497 if (_scanner.getToken() === 2 /* CloseBraceToken */ && allowTrailingComma) {
498 break;
499 }
500 }
501 else if (needsComma) {
502 handleError(6 /* CommaExpected */, [], []);
503 }
504 if (!parseProperty()) {
505 handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
506 }
507 needsComma = true;
508 }
509 onObjectEnd();
510 if (_scanner.getToken() !== 2 /* CloseBraceToken */) {
511 handleError(7 /* CloseBraceExpected */, [2 /* CloseBraceToken */], []);
512 }
513 else {
514 scanNext(); // consume close brace
515 }
516 return true;
517 }
518 function parseArray() {
519 onArrayBegin();
520 scanNext(); // consume open bracket
521 var needsComma = false;
522 while (_scanner.getToken() !== 4 /* CloseBracketToken */ && _scanner.getToken() !== 17 /* EOF */) {
523 if (_scanner.getToken() === 5 /* CommaToken */) {
524 if (!needsComma) {
525 handleError(4 /* ValueExpected */, [], []);
526 }
527 onSeparator(',');
528 scanNext(); // consume comma
529 if (_scanner.getToken() === 4 /* CloseBracketToken */ && allowTrailingComma) {
530 break;
531 }
532 }
533 else if (needsComma) {
534 handleError(6 /* CommaExpected */, [], []);
535 }
536 if (!parseValue()) {
537 handleError(4 /* ValueExpected */, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
538 }
539 needsComma = true;
540 }
541 onArrayEnd();
542 if (_scanner.getToken() !== 4 /* CloseBracketToken */) {
543 handleError(8 /* CloseBracketExpected */, [4 /* CloseBracketToken */], []);
544 }
545 else {
546 scanNext(); // consume close bracket
547 }
548 return true;
549 }
550 function parseValue() {
551 switch (_scanner.getToken()) {
552 case 3 /* OpenBracketToken */:
553 return parseArray();
554 case 1 /* OpenBraceToken */:
555 return parseObject();
556 case 10 /* StringLiteral */:
557 return parseString(true);
558 default:
559 return parseLiteral();
560 }
561 }
562 scanNext();
563 if (_scanner.getToken() === 17 /* EOF */) {
564 if (options.allowEmptyContent) {
565 return true;
566 }
567 handleError(4 /* ValueExpected */, [], []);
568 return false;
569 }
570 if (!parseValue()) {
571 handleError(4 /* ValueExpected */, [], []);
572 return false;
573 }
574 if (_scanner.getToken() !== 17 /* EOF */) {
575 handleError(9 /* EndOfFileExpected */, [], []);
576 }
577 return true;
578}
579/**
580 * Takes JSON with JavaScript-style comments and remove
581 * them. Optionally replaces every none-newline character
582 * of comments with a replaceCharacter
583 */
584export function stripComments(text, replaceCh) {
585 var _scanner = createScanner(text), parts = [], kind, offset = 0, pos;
586 do {
587 pos = _scanner.getPosition();
588 kind = _scanner.scan();
589 switch (kind) {
590 case 12 /* LineCommentTrivia */:
591 case 13 /* BlockCommentTrivia */:
592 case 17 /* EOF */:
593 if (offset !== pos) {
594 parts.push(text.substring(offset, pos));
595 }
596 if (replaceCh !== undefined) {
597 parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
598 }
599 offset = _scanner.getPosition();
600 break;
601 }
602 } while (kind !== 17 /* EOF */);
603 return parts.join('');
604}
605export function getNodeType(value) {
606 switch (typeof value) {
607 case 'boolean': return 'boolean';
608 case 'number': return 'number';
609 case 'string': return 'string';
610 case 'object': {
611 if (!value) {
612 return 'null';
613 }
614 else if (Array.isArray(value)) {
615 return 'array';
616 }
617 return 'object';
618 }
619 default: return 'null';
620 }
621}
Note: See TracBrowser for help on using the repository browser.