massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-json / node_modules / jsonc-parser / lib / esm / impl / parser.js
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';
6 import { createScanner } from './scanner';
7 var 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  */
16 export 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  */
136 export 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  */
185 export 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  */
249 export 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  */
286 export 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  */
306 export 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 }
329 export 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  */
336 export 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  */
355 export 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  */
584 export 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 }
605 export 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 }