jsonc-effect 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ast.js +255 -0
- package/equality.js +166 -0
- package/errors.js +218 -0
- package/format.js +453 -0
- package/index.d.ts +1933 -1983
- package/index.js +11 -1454
- package/package.json +50 -56
- package/parse.js +548 -0
- package/scanner.js +350 -0
- package/schema-integration.js +178 -0
- package/schemas.js +325 -0
- package/tsdoc-metadata.json +11 -11
- package/visitor.js +355 -0
package/index.js
CHANGED
|
@@ -1,1454 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const property = current.children.find((child)=>"property" === child.type && void 0 !== child.children && child.children[0]?.value === segment);
|
|
13
|
-
current = property?.children?.[1];
|
|
14
|
-
} else {
|
|
15
|
-
if ("array" !== current.type) return Option.none();
|
|
16
|
-
current = current.children[segment];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return current ? Option.some(current) : Option.none();
|
|
20
|
-
}
|
|
21
|
-
function findNodeAtOffsetImpl(root, offset) {
|
|
22
|
-
if (offset < root.offset || offset >= root.offset + root.length) return Option.none();
|
|
23
|
-
if (!root.children) return Option.some(root);
|
|
24
|
-
for (const child of root.children)if (offset >= child.offset && offset < child.offset + child.length) return findNodeAtOffsetImpl(child, offset);
|
|
25
|
-
return Option.some(root);
|
|
26
|
-
}
|
|
27
|
-
function buildPath(node, targetOffset, currentPath) {
|
|
28
|
-
if (targetOffset < node.offset || targetOffset >= node.offset + node.length) return Option.none();
|
|
29
|
-
if (!node.children) return Option.some(currentPath);
|
|
30
|
-
if ("object" === node.type) {
|
|
31
|
-
for (const prop of node.children)if ("property" === prop.type && void 0 !== prop.children && targetOffset >= prop.offset && targetOffset < prop.offset + prop.length) {
|
|
32
|
-
const key = prop.children[0]?.value;
|
|
33
|
-
const valuePath = [
|
|
34
|
-
...currentPath,
|
|
35
|
-
key
|
|
36
|
-
];
|
|
37
|
-
const valueChild = prop.children[1];
|
|
38
|
-
if (valueChild && targetOffset >= valueChild.offset && targetOffset < valueChild.offset + valueChild.length) return buildPath(valueChild, targetOffset, valuePath);
|
|
39
|
-
return Option.some(valuePath);
|
|
40
|
-
}
|
|
41
|
-
} else if ("array" === node.type) for(let i = 0; i < node.children.length; i++){
|
|
42
|
-
const child = node.children[i];
|
|
43
|
-
if (targetOffset >= child.offset && targetOffset < child.offset + child.length) return buildPath(child, targetOffset, [
|
|
44
|
-
...currentPath,
|
|
45
|
-
i
|
|
46
|
-
]);
|
|
47
|
-
}
|
|
48
|
-
return Option.some(currentPath);
|
|
49
|
-
}
|
|
50
|
-
function evaluateNode(node) {
|
|
51
|
-
switch(node.type){
|
|
52
|
-
case "object":
|
|
53
|
-
{
|
|
54
|
-
const obj = {};
|
|
55
|
-
if (node.children) {
|
|
56
|
-
for (const prop of node.children)if ("property" === prop.type && void 0 !== prop.children && 2 === prop.children.length) {
|
|
57
|
-
const key = prop.children[0].value;
|
|
58
|
-
obj[key] = evaluateNode(prop.children[1]);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return obj;
|
|
62
|
-
}
|
|
63
|
-
case "array":
|
|
64
|
-
return (node.children ?? []).map(evaluateNode);
|
|
65
|
-
case "property":
|
|
66
|
-
return node.children?.[1] ? evaluateNode(node.children[1]) : void 0;
|
|
67
|
-
case "string":
|
|
68
|
-
case "number":
|
|
69
|
-
case "boolean":
|
|
70
|
-
case "null":
|
|
71
|
-
return node.value;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const JsoncParseErrorCode = Schema.Literal("InvalidSymbol", "InvalidNumberFormat", "PropertyNameExpected", "ValueExpected", "ColonExpected", "CommaExpected", "CloseBraceExpected", "CloseBracketExpected", "EndOfFileExpected", "InvalidCommentToken", "UnexpectedEndOfComment", "UnexpectedEndOfString", "UnexpectedEndOfNumber", "InvalidUnicode", "InvalidEscapeCharacter", "InvalidCharacter");
|
|
75
|
-
class JsoncParseErrorDetail extends Schema.Class("JsoncParseErrorDetail")({
|
|
76
|
-
code: JsoncParseErrorCode,
|
|
77
|
-
message: Schema.String,
|
|
78
|
-
offset: Schema.Number,
|
|
79
|
-
length: Schema.Number,
|
|
80
|
-
startLine: Schema.Number,
|
|
81
|
-
startCharacter: Schema.Number
|
|
82
|
-
}) {
|
|
83
|
-
}
|
|
84
|
-
const JsoncParseErrorBase = Data.TaggedError("JsoncParseError");
|
|
85
|
-
class JsoncParseError extends JsoncParseErrorBase {
|
|
86
|
-
get message() {
|
|
87
|
-
const count = this.errors.length;
|
|
88
|
-
return `JSONC parse failed with ${count} error${1 !== count ? "s" : ""}: ${this.errors.map((e)=>e.message).join("; ")}`;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const JsoncNodeNotFoundErrorBase = Data.TaggedError("JsoncNodeNotFoundError");
|
|
92
|
-
class JsoncNodeNotFoundError extends JsoncNodeNotFoundErrorBase {
|
|
93
|
-
get message() {
|
|
94
|
-
return `Node not found at path [${this.path.join(", ")}] in ${this.rootNodeType} node`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const JsoncModificationErrorBase = Data.TaggedError("JsoncModificationError");
|
|
98
|
-
class JsoncModificationError extends JsoncModificationErrorBase {
|
|
99
|
-
get message() {
|
|
100
|
-
return `Modification failed at path [${this.path.join(", ")}]: ${this.reason}`;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const isWhitespace = (ch)=>0x20 === ch || 0x09 === ch || 0x0b === ch || 0x0c === ch || 0xa0 === ch || 0xfeff === ch;
|
|
104
|
-
const isLineBreak = (ch)=>0x0a === ch || 0x0d === ch || 0x2028 === ch || 0x2029 === ch;
|
|
105
|
-
const isDigit = (ch)=>ch >= 0x30 && ch <= 0x39;
|
|
106
|
-
const createScanner = (text, ignoreTrivia = false)=>{
|
|
107
|
-
const len = text.length;
|
|
108
|
-
let pos = 0;
|
|
109
|
-
let tokenOffset = 0;
|
|
110
|
-
let token = "Unknown";
|
|
111
|
-
let tokenValue = "";
|
|
112
|
-
let tokenError = "None";
|
|
113
|
-
let lineNumber = 0;
|
|
114
|
-
let lineStartOffset = 0;
|
|
115
|
-
let tokenStartLine = 0;
|
|
116
|
-
let tokenStartCharacter = 0;
|
|
117
|
-
const scanHexDigits = (count)=>{
|
|
118
|
-
let value = 0;
|
|
119
|
-
for(let i = 0; i < count; i++){
|
|
120
|
-
if (pos >= len) return -1;
|
|
121
|
-
const ch = text.charCodeAt(pos);
|
|
122
|
-
if (ch >= 0x30 && ch <= 0x39) value = 16 * value + (ch - 0x30);
|
|
123
|
-
else if (ch >= 0x41 && ch <= 0x46) value = 16 * value + (ch - 0x41 + 10);
|
|
124
|
-
else {
|
|
125
|
-
if (!(ch >= 0x61) || !(ch <= 0x66)) return -1;
|
|
126
|
-
value = 16 * value + (ch - 0x61 + 10);
|
|
127
|
-
}
|
|
128
|
-
pos++;
|
|
129
|
-
}
|
|
130
|
-
return value;
|
|
131
|
-
};
|
|
132
|
-
const scanString = ()=>{
|
|
133
|
-
let result = "";
|
|
134
|
-
pos++;
|
|
135
|
-
let start = pos;
|
|
136
|
-
while(pos < len){
|
|
137
|
-
const ch = text.charCodeAt(pos);
|
|
138
|
-
if (0x22 === ch) {
|
|
139
|
-
result += text.substring(start, pos);
|
|
140
|
-
pos++;
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
if (0x5c === ch) {
|
|
144
|
-
result += text.substring(start, pos);
|
|
145
|
-
pos++;
|
|
146
|
-
if (pos >= len) {
|
|
147
|
-
tokenError = "UnexpectedEndOfString";
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
const escaped = text.charCodeAt(pos);
|
|
151
|
-
pos++;
|
|
152
|
-
switch(escaped){
|
|
153
|
-
case 0x22:
|
|
154
|
-
result += '"';
|
|
155
|
-
break;
|
|
156
|
-
case 0x5c:
|
|
157
|
-
result += "\\";
|
|
158
|
-
break;
|
|
159
|
-
case 0x2f:
|
|
160
|
-
result += "/";
|
|
161
|
-
break;
|
|
162
|
-
case 0x62:
|
|
163
|
-
result += "\b";
|
|
164
|
-
break;
|
|
165
|
-
case 0x66:
|
|
166
|
-
result += "\f";
|
|
167
|
-
break;
|
|
168
|
-
case 0x6e:
|
|
169
|
-
result += "\n";
|
|
170
|
-
break;
|
|
171
|
-
case 0x72:
|
|
172
|
-
result += "\r";
|
|
173
|
-
break;
|
|
174
|
-
case 0x74:
|
|
175
|
-
result += "\t";
|
|
176
|
-
break;
|
|
177
|
-
case 0x75:
|
|
178
|
-
{
|
|
179
|
-
const value = scanHexDigits(4);
|
|
180
|
-
if (value >= 0) result += String.fromCharCode(value);
|
|
181
|
-
else tokenError = "InvalidUnicode";
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
default:
|
|
185
|
-
tokenError = "InvalidEscapeCharacter";
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
start = pos;
|
|
189
|
-
} else if (isLineBreak(ch)) {
|
|
190
|
-
tokenError = "UnexpectedEndOfString";
|
|
191
|
-
return result + text.substring(start, pos);
|
|
192
|
-
} else pos++;
|
|
193
|
-
}
|
|
194
|
-
tokenError = "UnexpectedEndOfString";
|
|
195
|
-
return result + text.substring(start, pos);
|
|
196
|
-
};
|
|
197
|
-
const scanNumber = ()=>{
|
|
198
|
-
const start = pos;
|
|
199
|
-
if (0x2d === text.charCodeAt(pos)) pos++;
|
|
200
|
-
if (0x30 === text.charCodeAt(pos)) pos++;
|
|
201
|
-
else {
|
|
202
|
-
if (!isDigit(text.charCodeAt(pos))) {
|
|
203
|
-
tokenError = "UnexpectedEndOfNumber";
|
|
204
|
-
return text.substring(start, pos);
|
|
205
|
-
}
|
|
206
|
-
pos++;
|
|
207
|
-
while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
|
|
208
|
-
}
|
|
209
|
-
if (pos < len && 0x2e === text.charCodeAt(pos)) {
|
|
210
|
-
pos++;
|
|
211
|
-
if (!isDigit(text.charCodeAt(pos))) {
|
|
212
|
-
tokenError = "UnexpectedEndOfNumber";
|
|
213
|
-
return text.substring(start, pos);
|
|
214
|
-
}
|
|
215
|
-
pos++;
|
|
216
|
-
while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
|
|
217
|
-
}
|
|
218
|
-
if (pos < len && (0x45 === text.charCodeAt(pos) || 0x65 === text.charCodeAt(pos))) {
|
|
219
|
-
pos++;
|
|
220
|
-
if (pos < len && (0x2b === text.charCodeAt(pos) || 0x2d === text.charCodeAt(pos))) pos++;
|
|
221
|
-
if (!isDigit(text.charCodeAt(pos))) {
|
|
222
|
-
tokenError = "UnexpectedEndOfNumber";
|
|
223
|
-
return text.substring(start, pos);
|
|
224
|
-
}
|
|
225
|
-
pos++;
|
|
226
|
-
while(pos < len && isDigit(text.charCodeAt(pos)))pos++;
|
|
227
|
-
}
|
|
228
|
-
return text.substring(start, pos);
|
|
229
|
-
};
|
|
230
|
-
const scan = ()=>{
|
|
231
|
-
tokenValue = "";
|
|
232
|
-
tokenError = "None";
|
|
233
|
-
if (pos >= len) {
|
|
234
|
-
tokenOffset = len;
|
|
235
|
-
tokenStartLine = lineNumber;
|
|
236
|
-
tokenStartCharacter = pos - lineStartOffset;
|
|
237
|
-
token = "EOF";
|
|
238
|
-
return token;
|
|
239
|
-
}
|
|
240
|
-
let ch = text.charCodeAt(pos);
|
|
241
|
-
if (isWhitespace(ch)) {
|
|
242
|
-
tokenOffset = pos;
|
|
243
|
-
tokenStartLine = lineNumber;
|
|
244
|
-
tokenStartCharacter = pos - lineStartOffset;
|
|
245
|
-
do {
|
|
246
|
-
pos++;
|
|
247
|
-
ch = pos < len ? text.charCodeAt(pos) : 0;
|
|
248
|
-
}while (isWhitespace(ch));
|
|
249
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
250
|
-
if (ignoreTrivia) return scan();
|
|
251
|
-
token = "Trivia";
|
|
252
|
-
return token;
|
|
253
|
-
}
|
|
254
|
-
if (isLineBreak(ch)) {
|
|
255
|
-
tokenOffset = pos;
|
|
256
|
-
tokenStartLine = lineNumber;
|
|
257
|
-
tokenStartCharacter = pos - lineStartOffset;
|
|
258
|
-
pos++;
|
|
259
|
-
if (0x0d === ch && pos < len && 0x0a === text.charCodeAt(pos)) pos++;
|
|
260
|
-
lineNumber++;
|
|
261
|
-
lineStartOffset = pos;
|
|
262
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
263
|
-
if (ignoreTrivia) return scan();
|
|
264
|
-
token = "LineBreak";
|
|
265
|
-
return token;
|
|
266
|
-
}
|
|
267
|
-
tokenOffset = pos;
|
|
268
|
-
tokenStartLine = lineNumber;
|
|
269
|
-
tokenStartCharacter = pos - lineStartOffset;
|
|
270
|
-
switch(ch){
|
|
271
|
-
case 0x7b:
|
|
272
|
-
pos++;
|
|
273
|
-
tokenValue = "{";
|
|
274
|
-
token = "OpenBrace";
|
|
275
|
-
return token;
|
|
276
|
-
case 0x7d:
|
|
277
|
-
pos++;
|
|
278
|
-
tokenValue = "}";
|
|
279
|
-
token = "CloseBrace";
|
|
280
|
-
return token;
|
|
281
|
-
case 0x5b:
|
|
282
|
-
pos++;
|
|
283
|
-
tokenValue = "[";
|
|
284
|
-
token = "OpenBracket";
|
|
285
|
-
return token;
|
|
286
|
-
case 0x5d:
|
|
287
|
-
pos++;
|
|
288
|
-
tokenValue = "]";
|
|
289
|
-
token = "CloseBracket";
|
|
290
|
-
return token;
|
|
291
|
-
case 0x3a:
|
|
292
|
-
pos++;
|
|
293
|
-
tokenValue = ":";
|
|
294
|
-
token = "Colon";
|
|
295
|
-
return token;
|
|
296
|
-
case 0x2c:
|
|
297
|
-
pos++;
|
|
298
|
-
tokenValue = ",";
|
|
299
|
-
token = "Comma";
|
|
300
|
-
return token;
|
|
301
|
-
case 0x22:
|
|
302
|
-
tokenValue = scanString();
|
|
303
|
-
token = "String";
|
|
304
|
-
return token;
|
|
305
|
-
case 0x2f:
|
|
306
|
-
{
|
|
307
|
-
const nextCh = pos + 1 < len ? text.charCodeAt(pos + 1) : 0;
|
|
308
|
-
if (0x2f === nextCh) {
|
|
309
|
-
pos += 2;
|
|
310
|
-
while(pos < len && !isLineBreak(text.charCodeAt(pos)))pos++;
|
|
311
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
312
|
-
if (ignoreTrivia) return scan();
|
|
313
|
-
token = "LineComment";
|
|
314
|
-
return token;
|
|
315
|
-
}
|
|
316
|
-
if (0x2a === nextCh) {
|
|
317
|
-
pos += 2;
|
|
318
|
-
const safeLen = len - 1;
|
|
319
|
-
let commentClosed = false;
|
|
320
|
-
while(pos < safeLen){
|
|
321
|
-
const cch = text.charCodeAt(pos);
|
|
322
|
-
if (isLineBreak(cch)) {
|
|
323
|
-
if (0x0d === cch && pos + 1 < len && 0x0a === text.charCodeAt(pos + 1)) pos++;
|
|
324
|
-
pos++;
|
|
325
|
-
lineNumber++;
|
|
326
|
-
lineStartOffset = pos;
|
|
327
|
-
} else if (0x2a === cch && 0x2f === text.charCodeAt(pos + 1)) {
|
|
328
|
-
pos += 2;
|
|
329
|
-
commentClosed = true;
|
|
330
|
-
break;
|
|
331
|
-
} else pos++;
|
|
332
|
-
}
|
|
333
|
-
if (!commentClosed) {
|
|
334
|
-
pos = len;
|
|
335
|
-
tokenError = "UnexpectedEndOfComment";
|
|
336
|
-
}
|
|
337
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
338
|
-
if (ignoreTrivia) return scan();
|
|
339
|
-
token = "BlockComment";
|
|
340
|
-
return token;
|
|
341
|
-
}
|
|
342
|
-
pos++;
|
|
343
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
344
|
-
token = "Unknown";
|
|
345
|
-
tokenError = "InvalidCharacter";
|
|
346
|
-
return token;
|
|
347
|
-
}
|
|
348
|
-
case 0x2d:
|
|
349
|
-
if (pos + 1 < len && isDigit(text.charCodeAt(pos + 1))) {
|
|
350
|
-
tokenValue = scanNumber();
|
|
351
|
-
token = "Number";
|
|
352
|
-
return token;
|
|
353
|
-
}
|
|
354
|
-
pos++;
|
|
355
|
-
tokenValue = "-";
|
|
356
|
-
token = "Unknown";
|
|
357
|
-
tokenError = "InvalidSymbol";
|
|
358
|
-
return token;
|
|
359
|
-
default:
|
|
360
|
-
if (isDigit(ch)) {
|
|
361
|
-
tokenValue = scanNumber();
|
|
362
|
-
token = "Number";
|
|
363
|
-
return token;
|
|
364
|
-
}
|
|
365
|
-
if (ch >= 0x61 && ch <= 0x7a) {
|
|
366
|
-
const start = pos;
|
|
367
|
-
pos++;
|
|
368
|
-
while(pos < len){
|
|
369
|
-
const kch = text.charCodeAt(pos);
|
|
370
|
-
if (kch >= 0x61 && kch <= 0x7a) pos++;
|
|
371
|
-
else break;
|
|
372
|
-
}
|
|
373
|
-
tokenValue = text.substring(start, pos);
|
|
374
|
-
switch(tokenValue){
|
|
375
|
-
case "true":
|
|
376
|
-
token = "True";
|
|
377
|
-
return token;
|
|
378
|
-
case "false":
|
|
379
|
-
token = "False";
|
|
380
|
-
return token;
|
|
381
|
-
case "null":
|
|
382
|
-
token = "Null";
|
|
383
|
-
return token;
|
|
384
|
-
default:
|
|
385
|
-
token = "Unknown";
|
|
386
|
-
tokenError = "InvalidSymbol";
|
|
387
|
-
return token;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
pos++;
|
|
391
|
-
tokenValue = text.substring(tokenOffset, pos);
|
|
392
|
-
token = "Unknown";
|
|
393
|
-
tokenError = "InvalidCharacter";
|
|
394
|
-
return token;
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
return {
|
|
398
|
-
scan,
|
|
399
|
-
getToken: ()=>token,
|
|
400
|
-
getTokenValue: ()=>tokenValue,
|
|
401
|
-
getTokenOffset: ()=>tokenOffset,
|
|
402
|
-
getTokenLength: ()=>pos - tokenOffset,
|
|
403
|
-
getTokenStartLine: ()=>tokenStartLine,
|
|
404
|
-
getTokenStartCharacter: ()=>tokenStartCharacter,
|
|
405
|
-
getTokenError: ()=>tokenError,
|
|
406
|
-
getPosition: ()=>pos,
|
|
407
|
-
setPosition: (newPos)=>{
|
|
408
|
-
pos = newPos;
|
|
409
|
-
tokenValue = "";
|
|
410
|
-
token = "Unknown";
|
|
411
|
-
tokenError = "None";
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
};
|
|
415
|
-
const parse = (text, options)=>Effect.sync(()=>parseInternal(text, options ?? {}, false)).pipe(Effect.flatMap(({ value, errors })=>{
|
|
416
|
-
if (errors.length > 0) return Effect.fail(new JsoncParseError({
|
|
417
|
-
errors,
|
|
418
|
-
text,
|
|
419
|
-
...void 0 !== options ? {
|
|
420
|
-
options
|
|
421
|
-
} : {}
|
|
422
|
-
}));
|
|
423
|
-
return Effect.succeed(value);
|
|
424
|
-
}));
|
|
425
|
-
const parseTree = (text, options)=>Effect.sync(()=>parseInternal(text, options ?? {}, true)).pipe(Effect.flatMap(({ root, errors })=>{
|
|
426
|
-
if (errors.length > 0) return Effect.fail(new JsoncParseError({
|
|
427
|
-
errors,
|
|
428
|
-
text,
|
|
429
|
-
...void 0 !== options ? {
|
|
430
|
-
options
|
|
431
|
-
} : {}
|
|
432
|
-
}));
|
|
433
|
-
return Effect.succeed(root ? Option.some(root) : Option.none());
|
|
434
|
-
}));
|
|
435
|
-
const stripComments = (text, replaceCh)=>Effect.sync(()=>{
|
|
436
|
-
const scanner = createScanner(text);
|
|
437
|
-
const parts = [];
|
|
438
|
-
let lastOffset = 0;
|
|
439
|
-
let kind;
|
|
440
|
-
do {
|
|
441
|
-
kind = scanner.scan();
|
|
442
|
-
const offset = scanner.getTokenOffset();
|
|
443
|
-
const length = scanner.getTokenLength();
|
|
444
|
-
if ("LineComment" === kind || "BlockComment" === kind) {
|
|
445
|
-
if (lastOffset < offset) parts.push(text.substring(lastOffset, offset));
|
|
446
|
-
if (void 0 !== replaceCh) for(let i = 0; i < length; i++){
|
|
447
|
-
const ch = text.charCodeAt(offset + i);
|
|
448
|
-
parts.push(0x0a === ch || 0x0d === ch ? text[offset + i] : replaceCh);
|
|
449
|
-
}
|
|
450
|
-
lastOffset = offset + length;
|
|
451
|
-
}
|
|
452
|
-
}while ("EOF" !== kind);
|
|
453
|
-
if (lastOffset < text.length) parts.push(text.substring(lastOffset));
|
|
454
|
-
return parts.join("");
|
|
455
|
-
});
|
|
456
|
-
function parseInternal(text, options, buildTree) {
|
|
457
|
-
const scanner = createScanner(text, false);
|
|
458
|
-
const errors = [];
|
|
459
|
-
const disallowComments = options.disallowComments ?? false;
|
|
460
|
-
const allowTrailingComma = options.allowTrailingComma ?? true;
|
|
461
|
-
const allowEmptyContent = options.allowEmptyContent ?? false;
|
|
462
|
-
let currentToken = "Unknown";
|
|
463
|
-
function token() {
|
|
464
|
-
return currentToken;
|
|
465
|
-
}
|
|
466
|
-
function scanNext() {
|
|
467
|
-
for(;;){
|
|
468
|
-
currentToken = scanner.scan();
|
|
469
|
-
switch(scanner.getTokenError()){
|
|
470
|
-
case "InvalidUnicode":
|
|
471
|
-
handleError("InvalidUnicode");
|
|
472
|
-
break;
|
|
473
|
-
case "InvalidEscapeCharacter":
|
|
474
|
-
handleError("InvalidEscapeCharacter");
|
|
475
|
-
break;
|
|
476
|
-
case "UnexpectedEndOfNumber":
|
|
477
|
-
handleError("InvalidNumberFormat");
|
|
478
|
-
break;
|
|
479
|
-
case "UnexpectedEndOfComment":
|
|
480
|
-
handleError("UnexpectedEndOfComment");
|
|
481
|
-
break;
|
|
482
|
-
case "UnexpectedEndOfString":
|
|
483
|
-
handleError("UnexpectedEndOfString");
|
|
484
|
-
break;
|
|
485
|
-
case "InvalidCharacter":
|
|
486
|
-
handleError("InvalidCharacter");
|
|
487
|
-
break;
|
|
488
|
-
}
|
|
489
|
-
switch(currentToken){
|
|
490
|
-
case "LineComment":
|
|
491
|
-
case "BlockComment":
|
|
492
|
-
if (disallowComments) handleError("InvalidCommentToken");
|
|
493
|
-
break;
|
|
494
|
-
case "Trivia":
|
|
495
|
-
case "LineBreak":
|
|
496
|
-
break;
|
|
497
|
-
default:
|
|
498
|
-
return currentToken;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
function handleError(code, skipUntilAfter = [], skipUntil = []) {
|
|
503
|
-
errors.push(new JsoncParseErrorDetail({
|
|
504
|
-
code,
|
|
505
|
-
message: formatError(code, scanner.getTokenOffset()),
|
|
506
|
-
offset: scanner.getTokenOffset(),
|
|
507
|
-
length: scanner.getTokenLength(),
|
|
508
|
-
startLine: scanner.getTokenStartLine(),
|
|
509
|
-
startCharacter: scanner.getTokenStartCharacter()
|
|
510
|
-
}));
|
|
511
|
-
if (skipUntilAfter.length > 0 || skipUntil.length > 0) {
|
|
512
|
-
let t = token();
|
|
513
|
-
while("EOF" !== t){
|
|
514
|
-
if (skipUntilAfter.includes(t)) {
|
|
515
|
-
scanNext();
|
|
516
|
-
break;
|
|
517
|
-
}
|
|
518
|
-
if (skipUntil.includes(t)) break;
|
|
519
|
-
t = scanNext();
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
function parseValue() {
|
|
524
|
-
switch(token()){
|
|
525
|
-
case "OpenBracket":
|
|
526
|
-
return parseArray();
|
|
527
|
-
case "OpenBrace":
|
|
528
|
-
return parseObject();
|
|
529
|
-
case "String":
|
|
530
|
-
return parseString();
|
|
531
|
-
case "Number":
|
|
532
|
-
return parseNumber();
|
|
533
|
-
case "True":
|
|
534
|
-
scanNext();
|
|
535
|
-
return true;
|
|
536
|
-
case "False":
|
|
537
|
-
scanNext();
|
|
538
|
-
return false;
|
|
539
|
-
case "Null":
|
|
540
|
-
scanNext();
|
|
541
|
-
return null;
|
|
542
|
-
default:
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
function parseString() {
|
|
547
|
-
const value = scanner.getTokenValue();
|
|
548
|
-
scanNext();
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
function parseNumber() {
|
|
552
|
-
const value = Number.parseFloat(scanner.getTokenValue());
|
|
553
|
-
scanNext();
|
|
554
|
-
return value;
|
|
555
|
-
}
|
|
556
|
-
function parseArray() {
|
|
557
|
-
scanNext();
|
|
558
|
-
const arr = [];
|
|
559
|
-
let needsComma = false;
|
|
560
|
-
while("CloseBracket" !== token() && "EOF" !== token()){
|
|
561
|
-
if ("Comma" === token()) {
|
|
562
|
-
if (!needsComma) handleError("ValueExpected");
|
|
563
|
-
scanNext();
|
|
564
|
-
if ("CloseBracket" === token() && allowTrailingComma) break;
|
|
565
|
-
} else if (needsComma) handleError("CommaExpected");
|
|
566
|
-
const value = parseValue();
|
|
567
|
-
if (void 0 === value) handleError("ValueExpected", [], [
|
|
568
|
-
"CloseBracket",
|
|
569
|
-
"Comma"
|
|
570
|
-
]);
|
|
571
|
-
else arr.push(value);
|
|
572
|
-
needsComma = true;
|
|
573
|
-
}
|
|
574
|
-
if ("CloseBracket" !== token()) handleError("CloseBracketExpected");
|
|
575
|
-
else scanNext();
|
|
576
|
-
return arr;
|
|
577
|
-
}
|
|
578
|
-
function parseObject() {
|
|
579
|
-
scanNext();
|
|
580
|
-
const obj = {};
|
|
581
|
-
let needsComma = false;
|
|
582
|
-
while("CloseBrace" !== token() && "EOF" !== token()){
|
|
583
|
-
if ("Comma" === token()) {
|
|
584
|
-
if (!needsComma) handleError("PropertyNameExpected");
|
|
585
|
-
scanNext();
|
|
586
|
-
if ("CloseBrace" === token() && allowTrailingComma) break;
|
|
587
|
-
} else if (needsComma) handleError("CommaExpected");
|
|
588
|
-
if ("String" !== token()) {
|
|
589
|
-
handleError("PropertyNameExpected", [], [
|
|
590
|
-
"CloseBrace",
|
|
591
|
-
"Comma"
|
|
592
|
-
]);
|
|
593
|
-
continue;
|
|
594
|
-
}
|
|
595
|
-
const key = scanner.getTokenValue();
|
|
596
|
-
scanNext();
|
|
597
|
-
if ("Colon" !== token()) {
|
|
598
|
-
handleError("ColonExpected", [], [
|
|
599
|
-
"CloseBrace",
|
|
600
|
-
"Comma"
|
|
601
|
-
]);
|
|
602
|
-
continue;
|
|
603
|
-
}
|
|
604
|
-
scanNext();
|
|
605
|
-
const value = parseValue();
|
|
606
|
-
if (void 0 === value) handleError("ValueExpected", [], [
|
|
607
|
-
"CloseBrace",
|
|
608
|
-
"Comma"
|
|
609
|
-
]);
|
|
610
|
-
else obj[key] = value;
|
|
611
|
-
needsComma = true;
|
|
612
|
-
}
|
|
613
|
-
if ("CloseBrace" !== token()) handleError("CloseBraceExpected");
|
|
614
|
-
else scanNext();
|
|
615
|
-
return obj;
|
|
616
|
-
}
|
|
617
|
-
function parseValueTree() {
|
|
618
|
-
switch(token()){
|
|
619
|
-
case "OpenBracket":
|
|
620
|
-
return parseArrayTree();
|
|
621
|
-
case "OpenBrace":
|
|
622
|
-
return parseObjectTree();
|
|
623
|
-
case "String":
|
|
624
|
-
{
|
|
625
|
-
const node = {
|
|
626
|
-
type: "string",
|
|
627
|
-
offset: scanner.getTokenOffset(),
|
|
628
|
-
length: 0,
|
|
629
|
-
value: scanner.getTokenValue()
|
|
630
|
-
};
|
|
631
|
-
scanNext();
|
|
632
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
633
|
-
return node;
|
|
634
|
-
}
|
|
635
|
-
case "Number":
|
|
636
|
-
{
|
|
637
|
-
const node = {
|
|
638
|
-
type: "number",
|
|
639
|
-
offset: scanner.getTokenOffset(),
|
|
640
|
-
length: 0,
|
|
641
|
-
value: Number.parseFloat(scanner.getTokenValue())
|
|
642
|
-
};
|
|
643
|
-
scanNext();
|
|
644
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
645
|
-
return node;
|
|
646
|
-
}
|
|
647
|
-
case "True":
|
|
648
|
-
{
|
|
649
|
-
const node = {
|
|
650
|
-
type: "boolean",
|
|
651
|
-
offset: scanner.getTokenOffset(),
|
|
652
|
-
length: 0,
|
|
653
|
-
value: true
|
|
654
|
-
};
|
|
655
|
-
scanNext();
|
|
656
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
657
|
-
return node;
|
|
658
|
-
}
|
|
659
|
-
case "False":
|
|
660
|
-
{
|
|
661
|
-
const node = {
|
|
662
|
-
type: "boolean",
|
|
663
|
-
offset: scanner.getTokenOffset(),
|
|
664
|
-
length: 0,
|
|
665
|
-
value: false
|
|
666
|
-
};
|
|
667
|
-
scanNext();
|
|
668
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
669
|
-
return node;
|
|
670
|
-
}
|
|
671
|
-
case "Null":
|
|
672
|
-
{
|
|
673
|
-
const node = {
|
|
674
|
-
type: "null",
|
|
675
|
-
offset: scanner.getTokenOffset(),
|
|
676
|
-
length: 0,
|
|
677
|
-
value: null
|
|
678
|
-
};
|
|
679
|
-
scanNext();
|
|
680
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
681
|
-
return node;
|
|
682
|
-
}
|
|
683
|
-
default:
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
function parseArrayTree() {
|
|
688
|
-
const node = {
|
|
689
|
-
type: "array",
|
|
690
|
-
offset: scanner.getTokenOffset(),
|
|
691
|
-
length: 0,
|
|
692
|
-
children: []
|
|
693
|
-
};
|
|
694
|
-
scanNext();
|
|
695
|
-
let needsComma = false;
|
|
696
|
-
while("CloseBracket" !== token() && "EOF" !== token()){
|
|
697
|
-
if ("Comma" === token()) {
|
|
698
|
-
if (!needsComma) handleError("ValueExpected");
|
|
699
|
-
scanNext();
|
|
700
|
-
if ("CloseBracket" === token() && allowTrailingComma) break;
|
|
701
|
-
} else if (needsComma) handleError("CommaExpected");
|
|
702
|
-
const child = parseValueTree();
|
|
703
|
-
if (child) node.children.push(child);
|
|
704
|
-
else handleError("ValueExpected", [], [
|
|
705
|
-
"CloseBracket",
|
|
706
|
-
"Comma"
|
|
707
|
-
]);
|
|
708
|
-
needsComma = true;
|
|
709
|
-
}
|
|
710
|
-
if ("CloseBracket" !== token()) handleError("CloseBracketExpected");
|
|
711
|
-
else scanNext();
|
|
712
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
713
|
-
return node;
|
|
714
|
-
}
|
|
715
|
-
function parseObjectTree() {
|
|
716
|
-
const node = {
|
|
717
|
-
type: "object",
|
|
718
|
-
offset: scanner.getTokenOffset(),
|
|
719
|
-
length: 0,
|
|
720
|
-
children: []
|
|
721
|
-
};
|
|
722
|
-
scanNext();
|
|
723
|
-
let needsComma = false;
|
|
724
|
-
while("CloseBrace" !== token() && "EOF" !== token()){
|
|
725
|
-
if ("Comma" === token()) {
|
|
726
|
-
if (!needsComma) handleError("PropertyNameExpected");
|
|
727
|
-
scanNext();
|
|
728
|
-
if ("CloseBrace" === token() && allowTrailingComma) break;
|
|
729
|
-
} else if (needsComma) handleError("CommaExpected");
|
|
730
|
-
if ("String" !== token()) {
|
|
731
|
-
handleError("PropertyNameExpected", [], [
|
|
732
|
-
"CloseBrace",
|
|
733
|
-
"Comma"
|
|
734
|
-
]);
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
const property = {
|
|
738
|
-
type: "property",
|
|
739
|
-
offset: scanner.getTokenOffset(),
|
|
740
|
-
length: 0,
|
|
741
|
-
children: []
|
|
742
|
-
};
|
|
743
|
-
const keyNode = {
|
|
744
|
-
type: "string",
|
|
745
|
-
offset: scanner.getTokenOffset(),
|
|
746
|
-
length: 0,
|
|
747
|
-
value: scanner.getTokenValue()
|
|
748
|
-
};
|
|
749
|
-
scanNext();
|
|
750
|
-
keyNode.length = scanner.getTokenOffset() - keyNode.offset;
|
|
751
|
-
property.children.push(keyNode);
|
|
752
|
-
if ("Colon" !== token()) {
|
|
753
|
-
handleError("ColonExpected", [], [
|
|
754
|
-
"CloseBrace",
|
|
755
|
-
"Comma"
|
|
756
|
-
]);
|
|
757
|
-
property.length = scanner.getTokenOffset() - property.offset;
|
|
758
|
-
node.children.push(property);
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
property.colonOffset = scanner.getTokenOffset();
|
|
762
|
-
scanNext();
|
|
763
|
-
const valueNode = parseValueTree();
|
|
764
|
-
if (valueNode) property.children.push(valueNode);
|
|
765
|
-
else handleError("ValueExpected", [], [
|
|
766
|
-
"CloseBrace",
|
|
767
|
-
"Comma"
|
|
768
|
-
]);
|
|
769
|
-
property.length = scanner.getTokenOffset() - property.offset;
|
|
770
|
-
node.children.push(property);
|
|
771
|
-
needsComma = true;
|
|
772
|
-
}
|
|
773
|
-
if ("CloseBrace" !== token()) handleError("CloseBraceExpected");
|
|
774
|
-
else scanNext();
|
|
775
|
-
node.length = scanner.getTokenOffset() - node.offset;
|
|
776
|
-
return node;
|
|
777
|
-
}
|
|
778
|
-
scanNext();
|
|
779
|
-
if (buildTree) {
|
|
780
|
-
const root = parseValueTree();
|
|
781
|
-
if ("EOF" !== token()) handleError("EndOfFileExpected");
|
|
782
|
-
if (!root && !allowEmptyContent) handleError("ValueExpected");
|
|
783
|
-
return {
|
|
784
|
-
value: void 0,
|
|
785
|
-
root,
|
|
786
|
-
errors
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
const value = parseValue();
|
|
790
|
-
if ("EOF" !== token()) handleError("EndOfFileExpected");
|
|
791
|
-
if (void 0 === value && !allowEmptyContent) handleError("ValueExpected");
|
|
792
|
-
return {
|
|
793
|
-
value,
|
|
794
|
-
root: void 0,
|
|
795
|
-
errors
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
function formatError(code, offset) {
|
|
799
|
-
switch(code){
|
|
800
|
-
case "InvalidSymbol":
|
|
801
|
-
return `Invalid symbol at offset ${offset}`;
|
|
802
|
-
case "InvalidNumberFormat":
|
|
803
|
-
return `Invalid number format at offset ${offset}`;
|
|
804
|
-
case "PropertyNameExpected":
|
|
805
|
-
return `Property name expected at offset ${offset}`;
|
|
806
|
-
case "ValueExpected":
|
|
807
|
-
return `Value expected at offset ${offset}`;
|
|
808
|
-
case "ColonExpected":
|
|
809
|
-
return `Colon expected at offset ${offset}`;
|
|
810
|
-
case "CommaExpected":
|
|
811
|
-
return `Comma expected at offset ${offset}`;
|
|
812
|
-
case "CloseBraceExpected":
|
|
813
|
-
return `Close brace expected at offset ${offset}`;
|
|
814
|
-
case "CloseBracketExpected":
|
|
815
|
-
return `Close bracket expected at offset ${offset}`;
|
|
816
|
-
case "EndOfFileExpected":
|
|
817
|
-
return `End of file expected at offset ${offset}`;
|
|
818
|
-
case "InvalidCommentToken":
|
|
819
|
-
return `Comments not allowed at offset ${offset}`;
|
|
820
|
-
case "UnexpectedEndOfComment":
|
|
821
|
-
return `Unexpected end of comment at offset ${offset}`;
|
|
822
|
-
case "UnexpectedEndOfString":
|
|
823
|
-
return `Unexpected end of string at offset ${offset}`;
|
|
824
|
-
case "UnexpectedEndOfNumber":
|
|
825
|
-
return `Unexpected end of number at offset ${offset}`;
|
|
826
|
-
case "InvalidUnicode":
|
|
827
|
-
return `Invalid unicode escape at offset ${offset}`;
|
|
828
|
-
case "InvalidEscapeCharacter":
|
|
829
|
-
return `Invalid escape character at offset ${offset}`;
|
|
830
|
-
case "InvalidCharacter":
|
|
831
|
-
return `Invalid character at offset ${offset}`;
|
|
832
|
-
default:
|
|
833
|
-
return `Parse error at offset ${offset}`;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
function deepEqual(a, b) {
|
|
837
|
-
if (a === b) return true;
|
|
838
|
-
if (null === a || null === b) return false;
|
|
839
|
-
if (typeof a !== typeof b) return false;
|
|
840
|
-
if (Array.isArray(a)) {
|
|
841
|
-
if (!Array.isArray(b)) return false;
|
|
842
|
-
if (a.length !== b.length) return false;
|
|
843
|
-
for(let i = 0; i < a.length; i++)if (!deepEqual(a[i], b[i])) return false;
|
|
844
|
-
return true;
|
|
845
|
-
}
|
|
846
|
-
if (Array.isArray(b)) return false;
|
|
847
|
-
if ("object" == typeof a && "object" == typeof b) {
|
|
848
|
-
const aObj = a;
|
|
849
|
-
const bObj = b;
|
|
850
|
-
const aKeys = Object.keys(aObj);
|
|
851
|
-
const bKeys = Object.keys(bObj);
|
|
852
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
853
|
-
for (const key of aKeys){
|
|
854
|
-
if (!Object.hasOwn(bObj, key)) return false;
|
|
855
|
-
if (!deepEqual(aObj[key], bObj[key])) return false;
|
|
856
|
-
}
|
|
857
|
-
return true;
|
|
858
|
-
}
|
|
859
|
-
return false;
|
|
860
|
-
}
|
|
861
|
-
const equals = Function.dual(2, (self, that)=>Effect.map(Effect.all([
|
|
862
|
-
parse(self),
|
|
863
|
-
parse(that)
|
|
864
|
-
]), ([a, b])=>deepEqual(a, b)));
|
|
865
|
-
const equalsValue = Function.dual(2, (self, value)=>Effect.map(parse(self), (parsed)=>deepEqual(parsed, value)));
|
|
866
|
-
const format = (text, range, options)=>Effect.sync(()=>formatImpl(text, range, options));
|
|
867
|
-
const applyEdits = Function.dual(2, (text, edits)=>Effect.sync(()=>{
|
|
868
|
-
const sorted = [
|
|
869
|
-
...edits
|
|
870
|
-
].sort((a, b)=>b.offset - a.offset);
|
|
871
|
-
let result = text;
|
|
872
|
-
for (const edit of sorted)result = result.substring(0, edit.offset) + edit.content + result.substring(edit.offset + edit.length);
|
|
873
|
-
return result;
|
|
874
|
-
}));
|
|
875
|
-
const formatAndApply = (text, range, options)=>format(text, range, options).pipe(Effect.flatMap((edits)=>applyEdits(text, edits)));
|
|
876
|
-
const modify = Function.dual((args)=>"string" == typeof args[0] && Array.isArray(args[1]), (text, path, value, options)=>Effect["try"]({
|
|
877
|
-
try: ()=>modifyImpl(text, path, value, options?.formattingOptions),
|
|
878
|
-
catch: (e)=>new JsoncModificationError({
|
|
879
|
-
path,
|
|
880
|
-
reason: String(e)
|
|
881
|
-
})
|
|
882
|
-
}));
|
|
883
|
-
function formatImpl(text, range, options) {
|
|
884
|
-
const opts = {
|
|
885
|
-
tabSize: options?.tabSize ?? 2,
|
|
886
|
-
insertSpaces: options?.insertSpaces ?? true,
|
|
887
|
-
eol: options?.eol ?? "\n",
|
|
888
|
-
insertFinalNewline: options?.insertFinalNewline ?? false,
|
|
889
|
-
keepLines: options?.keepLines ?? false
|
|
890
|
-
};
|
|
891
|
-
const indentUnit = opts.insertSpaces ? " ".repeat(opts.tabSize) : "\t";
|
|
892
|
-
const edits = [];
|
|
893
|
-
const scanner = createScanner(text, false);
|
|
894
|
-
const rangeStart = range?.offset ?? 0;
|
|
895
|
-
const rangeEnd = range ? range.offset + range.length : text.length;
|
|
896
|
-
let depth = 0;
|
|
897
|
-
let prevTokenEnd = -1;
|
|
898
|
-
let prevToken = "Unknown";
|
|
899
|
-
let firstToken = true;
|
|
900
|
-
function makeIndent(d) {
|
|
901
|
-
return indentUnit.repeat(d);
|
|
902
|
-
}
|
|
903
|
-
function addEdit(offset, length, content) {
|
|
904
|
-
if (offset >= rangeStart && offset + length <= rangeEnd) {
|
|
905
|
-
if (text.substring(offset, offset + length) !== content) edits.push({
|
|
906
|
-
offset,
|
|
907
|
-
length,
|
|
908
|
-
content
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
let kind = scanner.scan();
|
|
913
|
-
while("EOF" !== kind){
|
|
914
|
-
const tokenOffset = scanner.getTokenOffset();
|
|
915
|
-
const tokenLength = scanner.getTokenLength();
|
|
916
|
-
if ("Trivia" !== kind && "LineBreak" !== kind) {
|
|
917
|
-
if (!firstToken && prevTokenEnd >= 0) {
|
|
918
|
-
const gap = text.substring(prevTokenEnd, tokenOffset);
|
|
919
|
-
let expectedGap;
|
|
920
|
-
if ("CloseBrace" === kind || "CloseBracket" === kind) {
|
|
921
|
-
depth--;
|
|
922
|
-
expectedGap = opts.eol + makeIndent(depth);
|
|
923
|
-
} else expectedGap = "OpenBrace" === prevToken || "OpenBracket" === prevToken ? opts.eol + makeIndent(depth) : "Comma" === prevToken ? opts.eol + makeIndent(depth) : "Colon" === prevToken ? " " : "LineComment" === kind || "BlockComment" === kind ? gap.includes("\n") ? opts.eol + makeIndent(depth) : " " : "LineComment" === prevToken ? opts.eol + makeIndent(depth) : "BlockComment" === prevToken ? gap.includes("\n") ? opts.eol + makeIndent(depth) : " " : gap;
|
|
924
|
-
if (opts.keepLines && gap.includes("\n")) expectedGap = gap;
|
|
925
|
-
addEdit(prevTokenEnd, tokenOffset - prevTokenEnd, expectedGap);
|
|
926
|
-
}
|
|
927
|
-
if ("OpenBrace" === kind || "OpenBracket" === kind) depth++;
|
|
928
|
-
prevToken = kind;
|
|
929
|
-
prevTokenEnd = tokenOffset + tokenLength;
|
|
930
|
-
firstToken = false;
|
|
931
|
-
}
|
|
932
|
-
kind = scanner.scan();
|
|
933
|
-
}
|
|
934
|
-
if (opts.insertFinalNewline && prevTokenEnd >= 0) {
|
|
935
|
-
const trailing = text.substring(prevTokenEnd);
|
|
936
|
-
if (!trailing.endsWith(opts.eol)) edits.push({
|
|
937
|
-
offset: prevTokenEnd,
|
|
938
|
-
length: trailing.length,
|
|
939
|
-
content: opts.eol
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
return edits;
|
|
943
|
-
}
|
|
944
|
-
function modifyImpl(text, path, value, formattingOptions) {
|
|
945
|
-
const opts = {
|
|
946
|
-
tabSize: formattingOptions?.tabSize ?? 2,
|
|
947
|
-
insertSpaces: formattingOptions?.insertSpaces ?? true,
|
|
948
|
-
eol: formattingOptions?.eol ?? "\n"
|
|
949
|
-
};
|
|
950
|
-
const indentUnit = opts.insertSpaces ? " ".repeat(opts.tabSize) : "\t";
|
|
951
|
-
if (0 === path.length) {
|
|
952
|
-
const content = void 0 === value ? "" : JSON.stringify(value, null, opts.tabSize);
|
|
953
|
-
return [
|
|
954
|
-
{
|
|
955
|
-
offset: 0,
|
|
956
|
-
length: text.length,
|
|
957
|
-
content
|
|
958
|
-
}
|
|
959
|
-
];
|
|
960
|
-
}
|
|
961
|
-
const scanner = createScanner(text, true);
|
|
962
|
-
let currentToken = scanner.scan();
|
|
963
|
-
function skipValue() {
|
|
964
|
-
switch(currentToken){
|
|
965
|
-
case "OpenBrace":
|
|
966
|
-
{
|
|
967
|
-
currentToken = scanner.scan();
|
|
968
|
-
let first = true;
|
|
969
|
-
while("CloseBrace" !== currentToken && "EOF" !== currentToken){
|
|
970
|
-
if (!first && "Comma" === currentToken) currentToken = scanner.scan();
|
|
971
|
-
if ("String" === currentToken) {
|
|
972
|
-
currentToken = scanner.scan();
|
|
973
|
-
if ("Colon" === currentToken) {
|
|
974
|
-
currentToken = scanner.scan();
|
|
975
|
-
skipValue();
|
|
976
|
-
}
|
|
977
|
-
} else currentToken = scanner.scan();
|
|
978
|
-
first = false;
|
|
979
|
-
}
|
|
980
|
-
if ("CloseBrace" === currentToken) currentToken = scanner.scan();
|
|
981
|
-
break;
|
|
982
|
-
}
|
|
983
|
-
case "OpenBracket":
|
|
984
|
-
{
|
|
985
|
-
currentToken = scanner.scan();
|
|
986
|
-
let first = true;
|
|
987
|
-
while("CloseBracket" !== currentToken && "EOF" !== currentToken){
|
|
988
|
-
if (!first && "Comma" === currentToken) currentToken = scanner.scan();
|
|
989
|
-
skipValue();
|
|
990
|
-
first = false;
|
|
991
|
-
}
|
|
992
|
-
if ("CloseBracket" === currentToken) currentToken = scanner.scan();
|
|
993
|
-
break;
|
|
994
|
-
}
|
|
995
|
-
default:
|
|
996
|
-
currentToken = scanner.scan();
|
|
997
|
-
break;
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
let depth = 0;
|
|
1001
|
-
for (const segment of path){
|
|
1002
|
-
depth++;
|
|
1003
|
-
if ("string" == typeof segment) {
|
|
1004
|
-
if ("OpenBrace" !== currentToken) throw new Error(`Expected object at depth ${depth}`);
|
|
1005
|
-
currentToken = scanner.scan();
|
|
1006
|
-
let found = false;
|
|
1007
|
-
let lastValueEnd = scanner.getTokenOffset();
|
|
1008
|
-
let isFirst = true;
|
|
1009
|
-
while("CloseBrace" !== currentToken && "EOF" !== currentToken){
|
|
1010
|
-
if (!isFirst && "Comma" === currentToken) currentToken = scanner.scan();
|
|
1011
|
-
if ("String" === currentToken) {
|
|
1012
|
-
const key = scanner.getTokenValue();
|
|
1013
|
-
currentToken = scanner.scan();
|
|
1014
|
-
if ("Colon" === currentToken) currentToken = scanner.scan();
|
|
1015
|
-
if (key === segment) {
|
|
1016
|
-
found = true;
|
|
1017
|
-
if (depth === path.length) {
|
|
1018
|
-
const valueStart = scanner.getTokenOffset();
|
|
1019
|
-
const prevEnd = valueStart;
|
|
1020
|
-
skipValue();
|
|
1021
|
-
const valueEnd = scanner.getTokenOffset();
|
|
1022
|
-
if (void 0 === value) {
|
|
1023
|
-
let removeStart = valueStart;
|
|
1024
|
-
let removeEnd = valueEnd;
|
|
1025
|
-
const keySearchArea = text.substring(0, valueStart);
|
|
1026
|
-
const keyStart = keySearchArea.lastIndexOf(`"${segment}"`);
|
|
1027
|
-
if (keyStart >= 0) removeStart = keyStart;
|
|
1028
|
-
const beforeKey = text.substring(0, removeStart).trimEnd();
|
|
1029
|
-
const commaPosBefore = beforeKey.lastIndexOf(",");
|
|
1030
|
-
if (commaPosBefore >= 0) removeStart = commaPosBefore;
|
|
1031
|
-
else {
|
|
1032
|
-
const afterValue = text.substring(removeEnd);
|
|
1033
|
-
const trimmedAfter = afterValue.match(/^(\s*,)/);
|
|
1034
|
-
if (trimmedAfter) removeEnd += trimmedAfter[1].length;
|
|
1035
|
-
}
|
|
1036
|
-
return [
|
|
1037
|
-
{
|
|
1038
|
-
offset: removeStart,
|
|
1039
|
-
length: removeEnd - removeStart,
|
|
1040
|
-
content: ""
|
|
1041
|
-
}
|
|
1042
|
-
];
|
|
1043
|
-
}
|
|
1044
|
-
const serialized = JSON.stringify(value, null, opts.tabSize);
|
|
1045
|
-
return [
|
|
1046
|
-
{
|
|
1047
|
-
offset: prevEnd,
|
|
1048
|
-
length: valueEnd - prevEnd,
|
|
1049
|
-
content: serialized
|
|
1050
|
-
}
|
|
1051
|
-
];
|
|
1052
|
-
}
|
|
1053
|
-
break;
|
|
1054
|
-
}
|
|
1055
|
-
skipValue();
|
|
1056
|
-
} else currentToken = scanner.scan();
|
|
1057
|
-
lastValueEnd = scanner.getTokenOffset();
|
|
1058
|
-
isFirst = false;
|
|
1059
|
-
}
|
|
1060
|
-
if (!found && depth === path.length && void 0 !== value) {
|
|
1061
|
-
const indent = indentUnit.repeat(depth);
|
|
1062
|
-
const serialized = JSON.stringify(value, null, opts.tabSize);
|
|
1063
|
-
const insertText = isFirst ? `${opts.eol}${indent}"${segment}": ${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}"${segment}": ${serialized}`;
|
|
1064
|
-
return [
|
|
1065
|
-
{
|
|
1066
|
-
offset: lastValueEnd,
|
|
1067
|
-
length: 0,
|
|
1068
|
-
content: insertText
|
|
1069
|
-
}
|
|
1070
|
-
];
|
|
1071
|
-
}
|
|
1072
|
-
} else {
|
|
1073
|
-
if ("OpenBracket" !== currentToken) throw new Error(`Expected array at depth ${depth}`);
|
|
1074
|
-
currentToken = scanner.scan();
|
|
1075
|
-
let idx = 0;
|
|
1076
|
-
let lastEnd = scanner.getTokenOffset();
|
|
1077
|
-
while("CloseBracket" !== currentToken && "EOF" !== currentToken){
|
|
1078
|
-
if (idx > 0 && "Comma" === currentToken) currentToken = scanner.scan();
|
|
1079
|
-
if (idx === segment) {
|
|
1080
|
-
if (depth === path.length) {
|
|
1081
|
-
const valueStart = scanner.getTokenOffset();
|
|
1082
|
-
skipValue();
|
|
1083
|
-
const valueEnd = scanner.getTokenOffset();
|
|
1084
|
-
if (void 0 === value) {
|
|
1085
|
-
let removeEnd = valueEnd;
|
|
1086
|
-
const after = text.substring(removeEnd).trimStart();
|
|
1087
|
-
if (after.startsWith(",")) removeEnd = text.indexOf(",", removeEnd) + 1;
|
|
1088
|
-
return [
|
|
1089
|
-
{
|
|
1090
|
-
offset: valueStart,
|
|
1091
|
-
length: removeEnd - valueStart,
|
|
1092
|
-
content: ""
|
|
1093
|
-
}
|
|
1094
|
-
];
|
|
1095
|
-
}
|
|
1096
|
-
const serialized = JSON.stringify(value, null, opts.tabSize);
|
|
1097
|
-
return [
|
|
1098
|
-
{
|
|
1099
|
-
offset: valueStart,
|
|
1100
|
-
length: valueEnd - valueStart,
|
|
1101
|
-
content: serialized
|
|
1102
|
-
}
|
|
1103
|
-
];
|
|
1104
|
-
}
|
|
1105
|
-
break;
|
|
1106
|
-
}
|
|
1107
|
-
skipValue();
|
|
1108
|
-
lastEnd = scanner.getTokenOffset();
|
|
1109
|
-
idx++;
|
|
1110
|
-
}
|
|
1111
|
-
if (idx <= segment && depth === path.length && void 0 !== value) {
|
|
1112
|
-
const indent = indentUnit.repeat(depth);
|
|
1113
|
-
const serialized = JSON.stringify(value, null, opts.tabSize);
|
|
1114
|
-
const insertText = 0 === idx ? `${opts.eol}${indent}${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}${serialized}`;
|
|
1115
|
-
return [
|
|
1116
|
-
{
|
|
1117
|
-
offset: lastEnd,
|
|
1118
|
-
length: 0,
|
|
1119
|
-
content: insertText
|
|
1120
|
-
}
|
|
1121
|
-
];
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
return [];
|
|
1126
|
-
}
|
|
1127
|
-
const JsoncFromString = makeJsoncFromString();
|
|
1128
|
-
function makeJsoncFromString(options) {
|
|
1129
|
-
return Schema.transformOrFail(Schema.String, Schema.Unknown, {
|
|
1130
|
-
strict: true,
|
|
1131
|
-
decode: (input, _options, ast)=>{
|
|
1132
|
-
const program = options ? parse(input, options) : parse(input);
|
|
1133
|
-
return Effect.mapError(program, (parseError)=>new ParseResult.Type(ast, input, parseError.message));
|
|
1134
|
-
},
|
|
1135
|
-
encode: (value)=>ParseResult.succeed(JSON.stringify(value, null, 2))
|
|
1136
|
-
}).annotations({
|
|
1137
|
-
title: "JsoncFromString",
|
|
1138
|
-
description: "Parse a JSONC string into an unknown JavaScript value"
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
const makeJsoncSchema = (targetSchema, options)=>Schema.compose(makeJsoncFromString(options), targetSchema);
|
|
1142
|
-
const JsoncSyntaxKind = Schema.Literal("OpenBrace", "CloseBrace", "OpenBracket", "CloseBracket", "Comma", "Colon", "Null", "True", "False", "String", "Number", "LineComment", "BlockComment", "LineBreak", "Trivia", "Unknown", "EOF");
|
|
1143
|
-
const JsoncScanError = Schema.Literal("None", "UnexpectedEndOfComment", "UnexpectedEndOfString", "UnexpectedEndOfNumber", "InvalidUnicode", "InvalidEscapeCharacter", "InvalidCharacter", "InvalidSymbol");
|
|
1144
|
-
class JsoncToken extends Schema.Class("JsoncToken")({
|
|
1145
|
-
kind: JsoncSyntaxKind,
|
|
1146
|
-
value: Schema.String,
|
|
1147
|
-
offset: Schema.Number,
|
|
1148
|
-
length: Schema.Number,
|
|
1149
|
-
startLine: Schema.Number,
|
|
1150
|
-
startCharacter: Schema.Number,
|
|
1151
|
-
error: JsoncScanError
|
|
1152
|
-
}) {
|
|
1153
|
-
}
|
|
1154
|
-
const JsoncNodeType = Schema.Literal("object", "array", "property", "string", "number", "boolean", "null");
|
|
1155
|
-
class JsoncNode extends Schema.Class("JsoncNode")({
|
|
1156
|
-
type: JsoncNodeType,
|
|
1157
|
-
value: Schema.optional(Schema.Unknown),
|
|
1158
|
-
offset: Schema.Number,
|
|
1159
|
-
length: Schema.Number,
|
|
1160
|
-
colonOffset: Schema.optional(Schema.Number),
|
|
1161
|
-
children: Schema.optional(Schema.Array(Schema.suspend(()=>JsoncNode)))
|
|
1162
|
-
}) {
|
|
1163
|
-
}
|
|
1164
|
-
const JsoncSegment = Schema.Union(Schema.String, Schema.Number);
|
|
1165
|
-
Schema.Array(JsoncSegment);
|
|
1166
|
-
class JsoncEdit extends Schema.Class("JsoncEdit")({
|
|
1167
|
-
offset: Schema.Number,
|
|
1168
|
-
length: Schema.Number,
|
|
1169
|
-
content: Schema.String
|
|
1170
|
-
}) {
|
|
1171
|
-
}
|
|
1172
|
-
class JsoncRange extends Schema.Class("JsoncRange")({
|
|
1173
|
-
offset: Schema.Number,
|
|
1174
|
-
length: Schema.Number
|
|
1175
|
-
}) {
|
|
1176
|
-
}
|
|
1177
|
-
class JsoncParseOptions extends Schema.Class("JsoncParseOptions")({
|
|
1178
|
-
disallowComments: Schema.optionalWith(Schema.Boolean, {
|
|
1179
|
-
default: ()=>false
|
|
1180
|
-
}),
|
|
1181
|
-
allowTrailingComma: Schema.optionalWith(Schema.Boolean, {
|
|
1182
|
-
default: ()=>true
|
|
1183
|
-
}),
|
|
1184
|
-
allowEmptyContent: Schema.optionalWith(Schema.Boolean, {
|
|
1185
|
-
default: ()=>false
|
|
1186
|
-
})
|
|
1187
|
-
}) {
|
|
1188
|
-
}
|
|
1189
|
-
class JsoncFormattingOptions extends Schema.Class("JsoncFormattingOptions")({
|
|
1190
|
-
tabSize: Schema.optionalWith(Schema.Number, {
|
|
1191
|
-
default: ()=>2
|
|
1192
|
-
}),
|
|
1193
|
-
insertSpaces: Schema.optionalWith(Schema.Boolean, {
|
|
1194
|
-
default: ()=>true
|
|
1195
|
-
}),
|
|
1196
|
-
eol: Schema.optionalWith(Schema.String, {
|
|
1197
|
-
default: ()=>"\n"
|
|
1198
|
-
}),
|
|
1199
|
-
insertFinalNewline: Schema.optionalWith(Schema.Boolean, {
|
|
1200
|
-
default: ()=>false
|
|
1201
|
-
}),
|
|
1202
|
-
keepLines: Schema.optionalWith(Schema.Boolean, {
|
|
1203
|
-
default: ()=>false
|
|
1204
|
-
})
|
|
1205
|
-
}) {
|
|
1206
|
-
}
|
|
1207
|
-
const visit = (text, options)=>Stream.fromIterable(visitGen(text, options));
|
|
1208
|
-
const visitCollect = (text, predicate, options)=>visit(text, options).pipe(Stream.filter(predicate), Stream.runCollect, Effect.map(Chunk.toReadonlyArray));
|
|
1209
|
-
function* visitGen(text, options) {
|
|
1210
|
-
const scanner = createScanner(text, false);
|
|
1211
|
-
const disallowComments = options?.disallowComments ?? false;
|
|
1212
|
-
const path = [];
|
|
1213
|
-
function* scanNext() {
|
|
1214
|
-
for(;;){
|
|
1215
|
-
const t = scanner.scan();
|
|
1216
|
-
const scanError = scanner.getTokenError();
|
|
1217
|
-
if ("None" !== scanError) {
|
|
1218
|
-
let code;
|
|
1219
|
-
switch(scanError){
|
|
1220
|
-
case "InvalidUnicode":
|
|
1221
|
-
code = "InvalidUnicode";
|
|
1222
|
-
break;
|
|
1223
|
-
case "InvalidEscapeCharacter":
|
|
1224
|
-
code = "InvalidEscapeCharacter";
|
|
1225
|
-
break;
|
|
1226
|
-
case "UnexpectedEndOfNumber":
|
|
1227
|
-
code = "InvalidNumberFormat";
|
|
1228
|
-
break;
|
|
1229
|
-
case "UnexpectedEndOfComment":
|
|
1230
|
-
code = "UnexpectedEndOfComment";
|
|
1231
|
-
break;
|
|
1232
|
-
case "UnexpectedEndOfString":
|
|
1233
|
-
code = "UnexpectedEndOfString";
|
|
1234
|
-
break;
|
|
1235
|
-
case "InvalidCharacter":
|
|
1236
|
-
code = "InvalidCharacter";
|
|
1237
|
-
break;
|
|
1238
|
-
default:
|
|
1239
|
-
code = "InvalidSymbol";
|
|
1240
|
-
}
|
|
1241
|
-
yield {
|
|
1242
|
-
_tag: "Error",
|
|
1243
|
-
code,
|
|
1244
|
-
offset: scanner.getTokenOffset(),
|
|
1245
|
-
length: scanner.getTokenLength()
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
switch(t){
|
|
1249
|
-
case "LineComment":
|
|
1250
|
-
case "BlockComment":
|
|
1251
|
-
if (disallowComments) yield {
|
|
1252
|
-
_tag: "Error",
|
|
1253
|
-
code: "InvalidCommentToken",
|
|
1254
|
-
offset: scanner.getTokenOffset(),
|
|
1255
|
-
length: scanner.getTokenLength()
|
|
1256
|
-
};
|
|
1257
|
-
else yield {
|
|
1258
|
-
_tag: "Comment",
|
|
1259
|
-
offset: scanner.getTokenOffset(),
|
|
1260
|
-
length: scanner.getTokenLength()
|
|
1261
|
-
};
|
|
1262
|
-
break;
|
|
1263
|
-
case "Trivia":
|
|
1264
|
-
case "LineBreak":
|
|
1265
|
-
break;
|
|
1266
|
-
default:
|
|
1267
|
-
return t;
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
function getLiteralValue(kind, tokenValue) {
|
|
1272
|
-
switch(kind){
|
|
1273
|
-
case "String":
|
|
1274
|
-
return tokenValue;
|
|
1275
|
-
case "Number":
|
|
1276
|
-
return Number.parseFloat(tokenValue);
|
|
1277
|
-
case "True":
|
|
1278
|
-
return true;
|
|
1279
|
-
case "False":
|
|
1280
|
-
return false;
|
|
1281
|
-
case "Null":
|
|
1282
|
-
return null;
|
|
1283
|
-
default:
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
function* visitValue() {
|
|
1288
|
-
const t = scanner.getToken();
|
|
1289
|
-
switch(t){
|
|
1290
|
-
case "OpenBrace":
|
|
1291
|
-
return yield* visitObject();
|
|
1292
|
-
case "OpenBracket":
|
|
1293
|
-
return yield* visitArray();
|
|
1294
|
-
case "String":
|
|
1295
|
-
case "Number":
|
|
1296
|
-
case "True":
|
|
1297
|
-
case "False":
|
|
1298
|
-
case "Null":
|
|
1299
|
-
yield {
|
|
1300
|
-
_tag: "LiteralValue",
|
|
1301
|
-
value: getLiteralValue(t, scanner.getTokenValue()),
|
|
1302
|
-
offset: scanner.getTokenOffset(),
|
|
1303
|
-
length: scanner.getTokenLength(),
|
|
1304
|
-
path: [
|
|
1305
|
-
...path
|
|
1306
|
-
]
|
|
1307
|
-
};
|
|
1308
|
-
yield* scanNext();
|
|
1309
|
-
return true;
|
|
1310
|
-
default:
|
|
1311
|
-
yield {
|
|
1312
|
-
_tag: "Error",
|
|
1313
|
-
code: "ValueExpected",
|
|
1314
|
-
offset: scanner.getTokenOffset(),
|
|
1315
|
-
length: scanner.getTokenLength()
|
|
1316
|
-
};
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
function* visitObject() {
|
|
1321
|
-
yield {
|
|
1322
|
-
_tag: "ObjectBegin",
|
|
1323
|
-
offset: scanner.getTokenOffset(),
|
|
1324
|
-
length: scanner.getTokenLength(),
|
|
1325
|
-
path: [
|
|
1326
|
-
...path
|
|
1327
|
-
]
|
|
1328
|
-
};
|
|
1329
|
-
yield* scanNext();
|
|
1330
|
-
let needsComma = false;
|
|
1331
|
-
while("CloseBrace" !== scanner.getToken() && "EOF" !== scanner.getToken()){
|
|
1332
|
-
if ("Comma" === scanner.getToken()) {
|
|
1333
|
-
yield {
|
|
1334
|
-
_tag: "Separator",
|
|
1335
|
-
character: ",",
|
|
1336
|
-
offset: scanner.getTokenOffset(),
|
|
1337
|
-
length: scanner.getTokenLength()
|
|
1338
|
-
};
|
|
1339
|
-
yield* scanNext();
|
|
1340
|
-
if ("CloseBrace" === scanner.getToken()) break;
|
|
1341
|
-
} else if (needsComma) yield {
|
|
1342
|
-
_tag: "Error",
|
|
1343
|
-
code: "CommaExpected",
|
|
1344
|
-
offset: scanner.getTokenOffset(),
|
|
1345
|
-
length: scanner.getTokenLength()
|
|
1346
|
-
};
|
|
1347
|
-
if ("String" !== scanner.getToken()) {
|
|
1348
|
-
yield {
|
|
1349
|
-
_tag: "Error",
|
|
1350
|
-
code: "PropertyNameExpected",
|
|
1351
|
-
offset: scanner.getTokenOffset(),
|
|
1352
|
-
length: scanner.getTokenLength()
|
|
1353
|
-
};
|
|
1354
|
-
yield* scanNext();
|
|
1355
|
-
continue;
|
|
1356
|
-
}
|
|
1357
|
-
const key = scanner.getTokenValue();
|
|
1358
|
-
yield {
|
|
1359
|
-
_tag: "ObjectProperty",
|
|
1360
|
-
property: key,
|
|
1361
|
-
offset: scanner.getTokenOffset(),
|
|
1362
|
-
length: scanner.getTokenLength(),
|
|
1363
|
-
path: [
|
|
1364
|
-
...path
|
|
1365
|
-
]
|
|
1366
|
-
};
|
|
1367
|
-
path.push(key);
|
|
1368
|
-
yield* scanNext();
|
|
1369
|
-
if ("Colon" === scanner.getToken()) {
|
|
1370
|
-
yield {
|
|
1371
|
-
_tag: "Separator",
|
|
1372
|
-
character: ":",
|
|
1373
|
-
offset: scanner.getTokenOffset(),
|
|
1374
|
-
length: scanner.getTokenLength()
|
|
1375
|
-
};
|
|
1376
|
-
yield* scanNext();
|
|
1377
|
-
} else yield {
|
|
1378
|
-
_tag: "Error",
|
|
1379
|
-
code: "ColonExpected",
|
|
1380
|
-
offset: scanner.getTokenOffset(),
|
|
1381
|
-
length: scanner.getTokenLength()
|
|
1382
|
-
};
|
|
1383
|
-
yield* visitValue();
|
|
1384
|
-
path.pop();
|
|
1385
|
-
needsComma = true;
|
|
1386
|
-
}
|
|
1387
|
-
if ("CloseBrace" === scanner.getToken()) {
|
|
1388
|
-
yield {
|
|
1389
|
-
_tag: "ObjectEnd",
|
|
1390
|
-
offset: scanner.getTokenOffset(),
|
|
1391
|
-
length: scanner.getTokenLength()
|
|
1392
|
-
};
|
|
1393
|
-
yield* scanNext();
|
|
1394
|
-
} else yield {
|
|
1395
|
-
_tag: "Error",
|
|
1396
|
-
code: "CloseBraceExpected",
|
|
1397
|
-
offset: scanner.getTokenOffset(),
|
|
1398
|
-
length: scanner.getTokenLength()
|
|
1399
|
-
};
|
|
1400
|
-
return true;
|
|
1401
|
-
}
|
|
1402
|
-
function* visitArray() {
|
|
1403
|
-
yield {
|
|
1404
|
-
_tag: "ArrayBegin",
|
|
1405
|
-
offset: scanner.getTokenOffset(),
|
|
1406
|
-
length: scanner.getTokenLength(),
|
|
1407
|
-
path: [
|
|
1408
|
-
...path
|
|
1409
|
-
]
|
|
1410
|
-
};
|
|
1411
|
-
yield* scanNext();
|
|
1412
|
-
let index = 0;
|
|
1413
|
-
let needsComma = false;
|
|
1414
|
-
while("CloseBracket" !== scanner.getToken() && "EOF" !== scanner.getToken()){
|
|
1415
|
-
if ("Comma" === scanner.getToken()) {
|
|
1416
|
-
yield {
|
|
1417
|
-
_tag: "Separator",
|
|
1418
|
-
character: ",",
|
|
1419
|
-
offset: scanner.getTokenOffset(),
|
|
1420
|
-
length: scanner.getTokenLength()
|
|
1421
|
-
};
|
|
1422
|
-
yield* scanNext();
|
|
1423
|
-
if ("CloseBracket" === scanner.getToken()) break;
|
|
1424
|
-
} else if (needsComma) yield {
|
|
1425
|
-
_tag: "Error",
|
|
1426
|
-
code: "CommaExpected",
|
|
1427
|
-
offset: scanner.getTokenOffset(),
|
|
1428
|
-
length: scanner.getTokenLength()
|
|
1429
|
-
};
|
|
1430
|
-
path.push(index);
|
|
1431
|
-
yield* visitValue();
|
|
1432
|
-
path.pop();
|
|
1433
|
-
index++;
|
|
1434
|
-
needsComma = true;
|
|
1435
|
-
}
|
|
1436
|
-
if ("CloseBracket" === scanner.getToken()) {
|
|
1437
|
-
yield {
|
|
1438
|
-
_tag: "ArrayEnd",
|
|
1439
|
-
offset: scanner.getTokenOffset(),
|
|
1440
|
-
length: scanner.getTokenLength()
|
|
1441
|
-
};
|
|
1442
|
-
yield* scanNext();
|
|
1443
|
-
} else yield {
|
|
1444
|
-
_tag: "Error",
|
|
1445
|
-
code: "CloseBracketExpected",
|
|
1446
|
-
offset: scanner.getTokenOffset(),
|
|
1447
|
-
length: scanner.getTokenLength()
|
|
1448
|
-
};
|
|
1449
|
-
return true;
|
|
1450
|
-
}
|
|
1451
|
-
yield* scanNext();
|
|
1452
|
-
if ("EOF" !== scanner.getToken()) yield* visitValue();
|
|
1453
|
-
}
|
|
1454
|
-
export { JsoncEdit, JsoncFormattingOptions, JsoncFromString, JsoncModificationError, JsoncModificationErrorBase, JsoncNode, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail, JsoncParseOptions, JsoncRange, JsoncToken, applyEdits, createScanner, equals, equalsValue, findNode, findNodeAtOffset, format, formatAndApply, getNodePath, getNodeValue, makeJsoncFromString, makeJsoncSchema, modify, parse, parseTree, stripComments, visit, visitCollect };
|
|
1
|
+
import { findNode, findNodeAtOffset, getNodePath, getNodeValue } from "./ast.js";
|
|
2
|
+
import { JsoncModificationError, JsoncModificationErrorBase, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail } from "./errors.js";
|
|
3
|
+
import { createScanner } from "./scanner.js";
|
|
4
|
+
import { parse, parseTree, stripComments } from "./parse.js";
|
|
5
|
+
import { equals, equalsValue } from "./equality.js";
|
|
6
|
+
import { applyEdits, format, formatAndApply, modify } from "./format.js";
|
|
7
|
+
import { JsoncFromString, makeJsoncFromString, makeJsoncSchema } from "./schema-integration.js";
|
|
8
|
+
import { JsoncEdit, JsoncFormattingOptions, JsoncNode, JsoncParseOptions, JsoncRange, JsoncToken } from "./schemas.js";
|
|
9
|
+
import { visit, visitCollect } from "./visitor.js";
|
|
10
|
+
|
|
11
|
+
export { JsoncEdit, JsoncFormattingOptions, JsoncFromString, JsoncModificationError, JsoncModificationErrorBase, JsoncNode, JsoncNodeNotFoundError, JsoncNodeNotFoundErrorBase, JsoncParseError, JsoncParseErrorBase, JsoncParseErrorDetail, JsoncParseOptions, JsoncRange, JsoncToken, applyEdits, createScanner, equals, equalsValue, findNode, findNodeAtOffset, format, formatAndApply, getNodePath, getNodeValue, makeJsoncFromString, makeJsoncSchema, modify, parse, parseTree, stripComments, visit, visitCollect };
|