skir 1.2.4 → 1.2.5

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.
Files changed (96) hide show
  1. package/dist/config_parser.d.ts.map +1 -1
  2. package/dist/module_collector.d.ts.map +1 -1
  3. package/package.json +5 -3
  4. package/src/config_parser.ts +4 -1
  5. package/src/module_collector.ts +18 -20
  6. package/src/project_initializer.ts +18 -0
  7. package/dist/casing.d.ts +0 -5
  8. package/dist/casing.js +0 -23
  9. package/dist/casing.js.map +0 -1
  10. package/dist/command_line_parser.d.ts +0 -36
  11. package/dist/command_line_parser.d.ts.map +0 -1
  12. package/dist/command_line_parser.js +0 -240
  13. package/dist/command_line_parser.js.map +0 -1
  14. package/dist/compatibility_checker.d.ts +0 -74
  15. package/dist/compatibility_checker.js +0 -331
  16. package/dist/compatibility_checker.js.map +0 -1
  17. package/dist/compiler.d.ts +0 -3
  18. package/dist/compiler.js +0 -406
  19. package/dist/compiler.js.map +0 -1
  20. package/dist/completion_helper.d.ts +0 -27
  21. package/dist/completion_helper.js +0 -101
  22. package/dist/completion_helper.js.map +0 -1
  23. package/dist/config.d.ts +0 -18
  24. package/dist/config.js +0 -19
  25. package/dist/config.js.map +0 -1
  26. package/dist/config_parser.d.ts +0 -34
  27. package/dist/config_parser.js +0 -214
  28. package/dist/config_parser.js.map +0 -1
  29. package/dist/definition_finder.d.ts +0 -16
  30. package/dist/definition_finder.js +0 -375
  31. package/dist/definition_finder.js.map +0 -1
  32. package/dist/dependency_manager.d.ts +0 -14
  33. package/dist/dependency_manager.js +0 -109
  34. package/dist/dependency_manager.js.map +0 -1
  35. package/dist/doc_comment_parser.d.ts +0 -6
  36. package/dist/doc_comment_parser.js +0 -269
  37. package/dist/doc_comment_parser.js.map +0 -1
  38. package/dist/doc_reference_resolver.d.ts +0 -5
  39. package/dist/doc_reference_resolver.js +0 -232
  40. package/dist/doc_reference_resolver.js.map +0 -1
  41. package/dist/error_renderer.d.ts +0 -24
  42. package/dist/error_renderer.js +0 -326
  43. package/dist/error_renderer.js.map +0 -1
  44. package/dist/exit_error.d.ts +0 -8
  45. package/dist/exit_error.d.ts.map +0 -1
  46. package/dist/exit_error.js +0 -8
  47. package/dist/exit_error.js.map +0 -1
  48. package/dist/expected_names.d.ts +0 -11
  49. package/dist/expected_names.js +0 -34
  50. package/dist/expected_names.js.map +0 -1
  51. package/dist/formatter.d.ts +0 -28
  52. package/dist/formatter.js +0 -338
  53. package/dist/formatter.js.map +0 -1
  54. package/dist/get_dependencies_flow.d.ts +0 -40
  55. package/dist/get_dependencies_flow.js +0 -177
  56. package/dist/get_dependencies_flow.js.map +0 -1
  57. package/dist/import_block_formatter.d.ts +0 -3
  58. package/dist/import_block_formatter.js +0 -36
  59. package/dist/import_block_formatter.js.map +0 -1
  60. package/dist/index.d.ts +0 -2
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/index.js +0 -2
  63. package/dist/index.js.map +0 -1
  64. package/dist/io.d.ts +0 -23
  65. package/dist/io.d.ts.map +0 -1
  66. package/dist/io.js +0 -58
  67. package/dist/io.js.map +0 -1
  68. package/dist/literals.d.ts +0 -12
  69. package/dist/literals.js +0 -96
  70. package/dist/literals.js.map +0 -1
  71. package/dist/module_collector.d.ts +0 -9
  72. package/dist/module_collector.js +0 -75
  73. package/dist/module_collector.js.map +0 -1
  74. package/dist/module_set.d.ts +0 -51
  75. package/dist/module_set.js +0 -1331
  76. package/dist/module_set.js.map +0 -1
  77. package/dist/package_downloader.d.ts +0 -4
  78. package/dist/package_downloader.js +0 -256
  79. package/dist/package_downloader.js.map +0 -1
  80. package/dist/package_types.d.ts +0 -30
  81. package/dist/package_types.d.ts.map +0 -1
  82. package/dist/package_types.js +0 -2
  83. package/dist/package_types.js.map +0 -1
  84. package/dist/parser.d.ts +0 -7
  85. package/dist/parser.js +0 -1335
  86. package/dist/parser.js.map +0 -1
  87. package/dist/project_initializer.d.ts +0 -2
  88. package/dist/project_initializer.d.ts.map +0 -1
  89. package/dist/project_initializer.js +0 -189
  90. package/dist/project_initializer.js.map +0 -1
  91. package/dist/snapshotter.d.ts +0 -16
  92. package/dist/snapshotter.js +0 -263
  93. package/dist/snapshotter.js.map +0 -1
  94. package/dist/tokenizer.d.ts +0 -18
  95. package/dist/tokenizer.js +0 -244
  96. package/dist/tokenizer.js.map +0 -1
package/dist/parser.js DELETED
@@ -1,1335 +0,0 @@
1
- import { convertCase, unquoteAndUnescape } from "skir-internal";
2
- import * as Casing from "./casing.js";
3
- import { mergeDocs } from "./doc_comment_parser.js";
4
- /** Runs syntactic analysis on a module. */
5
- export function parseModule(moduleTokens, mode) {
6
- const { modulePath, sourceCode } = moduleTokens;
7
- const errors = [];
8
- const it = new TokenIterator(moduleTokens, mode, errors);
9
- const maybeBrokenDeclarations = parseDeclarations(it, "module");
10
- const brokenConstants = maybeBrokenDeclarations.filter((d) => d.kind === "broken-constant");
11
- const brokenMethods = maybeBrokenDeclarations.filter((d) => d.kind === "broken-method");
12
- const declarations = maybeBrokenDeclarations.filter((d) => d.kind !== "broken-constant" && d.kind !== "broken-method");
13
- it.expectThenNext([""]);
14
- // Create a mappinng from names to declarations, and check for duplicates.
15
- const nameToDeclaration = {};
16
- const pathToImports = new Map();
17
- for (const declaration of declarations) {
18
- let nameTokens;
19
- let otherModulePath;
20
- let importDeclaration;
21
- if (declaration.kind === "import") {
22
- nameTokens = declaration.importedNames;
23
- otherModulePath = resolveModulePath(declaration.modulePath, modulePath, errors);
24
- importDeclaration = declaration;
25
- }
26
- else if (declaration.kind === "import-alias") {
27
- nameTokens = [declaration.name];
28
- otherModulePath = resolveModulePath(declaration.modulePath, modulePath, errors);
29
- importDeclaration = declaration;
30
- }
31
- else {
32
- nameTokens = [declaration.name];
33
- }
34
- if (otherModulePath) {
35
- let imports = pathToImports.get(otherModulePath);
36
- if (!imports) {
37
- imports = [];
38
- pathToImports.set(otherModulePath, imports);
39
- }
40
- imports.push(importDeclaration);
41
- importDeclaration.resolvedModulePath = otherModulePath;
42
- }
43
- for (const nameToken of nameTokens) {
44
- const name = nameToken.text;
45
- if (name in nameToDeclaration) {
46
- errors.push({
47
- token: nameToken,
48
- message: `Duplicate identifier '${name}'`,
49
- });
50
- }
51
- else {
52
- nameToDeclaration[name] = declaration;
53
- }
54
- }
55
- }
56
- const importBlockRange = validateImportBlock(declarations, moduleTokens, errors);
57
- const pathToImportedNames = {};
58
- for (const [path, imports] of pathToImports) {
59
- const importsNoAlias = imports.filter((i) => i.kind === "import");
60
- const importsWithAlias = imports.filter((i) => i.kind === "import-alias");
61
- if (importsNoAlias.length && importsWithAlias.length) {
62
- for (const importNoAlias of importsNoAlias) {
63
- errors.push({
64
- token: importNoAlias.modulePath,
65
- message: "Module already imported with an alias",
66
- });
67
- }
68
- continue;
69
- }
70
- if (importsWithAlias.length >= 2) {
71
- for (const importWithAlias of importsWithAlias.slice(1)) {
72
- errors.push({
73
- token: importWithAlias.modulePath,
74
- message: "Module already imported with a different alias",
75
- });
76
- }
77
- continue;
78
- }
79
- if (importsNoAlias.length) {
80
- const names = new Set();
81
- for (const importNoAlias of importsNoAlias) {
82
- for (const importedName of importNoAlias.importedNames) {
83
- names.add(importedName.text);
84
- }
85
- }
86
- pathToImportedNames[path] = {
87
- kind: "some",
88
- names: names,
89
- };
90
- }
91
- else {
92
- const alias = importsWithAlias[0].name.text;
93
- pathToImportedNames[path] = {
94
- kind: "all",
95
- alias: alias,
96
- };
97
- }
98
- }
99
- const methods = declarations.filter((d) => d.kind === "method");
100
- const constants = declarations.filter((d) => d.kind === "constant");
101
- return {
102
- result: {
103
- kind: "module",
104
- path: modulePath,
105
- sourceCode: sourceCode,
106
- nameToDeclaration: nameToDeclaration,
107
- declarations: declarations,
108
- importBlockRange: importBlockRange,
109
- records: collectModuleRecords(declarations),
110
- pathToImportedNames: pathToImportedNames,
111
- methods: methods,
112
- brokenMethods: brokenMethods,
113
- constants: constants,
114
- brokenConstants: brokenConstants,
115
- },
116
- errors: errors,
117
- };
118
- }
119
- function parseDeclarations(it, parentNode) {
120
- const result = [];
121
- // Returns true on a next token if it indicates that the statement is over.
122
- const isEndToken = (t) => t === "" || (parentNode !== "module" && t === "}");
123
- // Returns true if the token may be the last token of a valid statement.
124
- const isLastToken = (t) => t === "}" || t === ";";
125
- while (!isEndToken(it.current)) {
126
- const startIndex = it.index;
127
- const declaration = parseDeclaration(it, parentNode);
128
- if (declaration !== null) {
129
- result.push(declaration);
130
- if (declaration.kind === "method" ||
131
- declaration.kind === "broken-method") {
132
- if (declaration.inlineRequestRecord) {
133
- result.push(declaration.inlineRequestRecord);
134
- }
135
- if (declaration.inlineResponseRecord) {
136
- result.push(declaration.inlineResponseRecord);
137
- }
138
- }
139
- }
140
- else {
141
- // We have an invalid statement. An error was already registered. Perhaps
142
- // the statement was parsed entirely but was incorrect (`removed 1, 1;`), or
143
- // zero tokens were consumed (`a`), or a few tokens were consumed but did
144
- // not form a statement. We want to recover from whichever scenario to avoid
145
- // showing unhelpful extra error messages.
146
- const noTokenWasConsumed = it.index === startIndex;
147
- if (noTokenWasConsumed) {
148
- it.next();
149
- if (isLastToken(it.previous)) {
150
- // For example: two semicolons in a row.
151
- continue;
152
- }
153
- }
154
- if (noTokenWasConsumed ||
155
- (it.current !== "" && !isLastToken(it.previous))) {
156
- let nestedLevel = 0;
157
- while (true) {
158
- const token = it.current;
159
- if (token === "") {
160
- break;
161
- }
162
- it.next();
163
- if (token === "{") {
164
- ++nestedLevel;
165
- }
166
- else if (token === "}") {
167
- --nestedLevel;
168
- }
169
- if (nestedLevel <= 0 && isLastToken(token)) {
170
- break;
171
- }
172
- }
173
- }
174
- }
175
- }
176
- return result;
177
- }
178
- function parseDeclaration(it, parentNode) {
179
- const doc = collectDoc(it);
180
- let recordType = "enum";
181
- const parentIsRoot = parentNode === "module";
182
- const expected = [
183
- /*0:*/ "struct",
184
- /*1:*/ "enum",
185
- /*2:*/ parentIsRoot ? null : "removed",
186
- /*3:*/ parentIsRoot ? null : TOKEN_IS_IDENTIFIER,
187
- /*4:*/ parentIsRoot ? "import" : null,
188
- /*5:*/ parentIsRoot ? "method" : null,
189
- /*6:*/ parentIsRoot ? "const" : null,
190
- ];
191
- const match = it.expectThenNext(expected);
192
- switch (match.case) {
193
- case 0:
194
- recordType = "struct";
195
- // Falls through.
196
- case 1:
197
- return parseRecord(it, recordType, doc);
198
- case 2:
199
- return parseRemoved(it, match.token);
200
- case 3:
201
- return parseField(it, match.token, doc, parentNode);
202
- case 4:
203
- return parseImport(it, match.token);
204
- case 5:
205
- return parseMethod(it, doc);
206
- case 6:
207
- return parseConstant(it, doc);
208
- default:
209
- return null;
210
- }
211
- }
212
- class RecordBuilder {
213
- constructor(recordName, recordType, doc, stableId, inlineContext, errors) {
214
- this.recordName = recordName;
215
- this.recordType = recordType;
216
- this.doc = doc;
217
- this.stableId = stableId;
218
- this.inlineContext = inlineContext;
219
- this.errors = errors;
220
- this.nameToDeclaration = {};
221
- this.enumFoldedVariantNames = new Set();
222
- this.numbers = new Set();
223
- this.numbering = "";
224
- this.removedNumbers = [];
225
- }
226
- addDeclaration(declaration) {
227
- if (this.numbering === "broken") {
228
- return;
229
- }
230
- let nameToken;
231
- let errorToken;
232
- let numbers = [];
233
- let newNumbering = this.numbering;
234
- // Unless explicitly specified, the number assigned to the first field of an
235
- // enum is 1.
236
- const nextImplicitNumber = this.numbers.size + (this.recordType === "enum" ? 1 : 0);
237
- switch (declaration.kind) {
238
- case "field": {
239
- nameToken = declaration.name;
240
- errorToken = nameToken;
241
- newNumbering = declaration.number < 0 ? "implicit" : "explicit";
242
- if (declaration.number < 0) {
243
- declaration = { ...declaration, number: nextImplicitNumber };
244
- }
245
- numbers = [declaration.number];
246
- break;
247
- }
248
- case "record": {
249
- nameToken = declaration.name;
250
- errorToken = nameToken;
251
- break;
252
- }
253
- case "removed": {
254
- errorToken = declaration.removedToken;
255
- if (declaration.numbers.length) {
256
- newNumbering = "explicit";
257
- numbers = declaration.numbers;
258
- }
259
- else {
260
- newNumbering = "implicit";
261
- numbers = [nextImplicitNumber];
262
- }
263
- }
264
- }
265
- // Make sure we're not mixing implicit and explicit numbering.
266
- if (this.numbering === "") {
267
- this.numbering = newNumbering;
268
- }
269
- else if (this.numbering !== newNumbering) {
270
- this.errors.push({
271
- token: errorToken,
272
- message: "Cannot mix implicit and explicit numbering",
273
- });
274
- this.numbering = "broken";
275
- }
276
- // Register the record/field name and make sure it's unique.
277
- if (nameToken !== undefined) {
278
- const name = nameToken.text;
279
- if (this.recordType === "enum" && declaration.kind === "field") {
280
- const foldedName = name.toLowerCase();
281
- if (this.enumFoldedVariantNames.has(foldedName)) {
282
- this.errors.push({
283
- token: nameToken,
284
- message: `Duplicate identifier '${name}'`,
285
- });
286
- return;
287
- }
288
- this.enumFoldedVariantNames.add(foldedName);
289
- }
290
- if (name in this.nameToDeclaration) {
291
- this.errors.push({
292
- token: nameToken,
293
- message: `Duplicate identifier '${name}'`,
294
- });
295
- return;
296
- }
297
- this.nameToDeclaration[name] = declaration;
298
- }
299
- // Register the field number and make sure it's unique.
300
- for (const number of numbers) {
301
- if (this.numbers.has(number)) {
302
- this.errors.push({
303
- token: errorToken,
304
- message: `Duplicate field number ${number}`,
305
- });
306
- this.numbering = "broken";
307
- return;
308
- }
309
- else if (number === 0 && this.recordType === "enum") {
310
- this.errors.push({
311
- token: errorToken,
312
- message: "Number 0 is reserved for UNKNOWN field",
313
- });
314
- return;
315
- }
316
- this.numbers.add(number);
317
- }
318
- // Register the removed field numbers.
319
- if (declaration.kind === "removed") {
320
- this.removedNumbers.push(...numbers);
321
- }
322
- }
323
- build() {
324
- const isStruct = this.recordType === "struct";
325
- // If the record is a struct, make sure that all field numbers are
326
- // consecutive starting from 0. The fields of an enum, on the other hand,
327
- // can be sparse.
328
- if (isStruct) {
329
- for (let i = 0; i < this.numbers.size; ++i) {
330
- if (this.numbers.has(i)) {
331
- continue;
332
- }
333
- this.errors.push({
334
- token: this.recordName,
335
- message: `Missing field number ${i}`,
336
- });
337
- break;
338
- }
339
- }
340
- const declarations = Object.values(this.nameToDeclaration);
341
- const fields = declarations.filter((d) => d.kind === "field");
342
- const nestedRecords = declarations.filter((d) => d.kind === "record");
343
- const { recordName } = this;
344
- let key = `${recordName.line.modulePath}:${recordName.position}`;
345
- if (this.inlineContext) {
346
- key += `:${this.inlineContext.context}`;
347
- }
348
- const numSlots = isStruct && fields.length
349
- ? Math.max(...fields.map((f) => f.number)) + 1
350
- : 0;
351
- const numSlotsInclRemovedNumbers = isStruct ? this.numbers.size : 0;
352
- return {
353
- kind: "record",
354
- key: key,
355
- name: this.recordName,
356
- recordType: this.recordType,
357
- doc: this.doc,
358
- nameToDeclaration: this.nameToDeclaration,
359
- declarations: Object.values(this.nameToDeclaration),
360
- fields: fields,
361
- nestedRecords: nestedRecords,
362
- removedNumbers: this.removedNumbers.sort(),
363
- recordNumber: this.stableId,
364
- numSlots: numSlots,
365
- numSlotsInclRemovedNumbers: numSlotsInclRemovedNumbers,
366
- };
367
- }
368
- }
369
- function parseRecord(it, recordType, doc, inlineContext) {
370
- // A struct or an enum.
371
- let nameToken;
372
- if (inlineContext) {
373
- const { originalName } = inlineContext;
374
- let transformedName = convertCase(originalName.text, "UpperCamel");
375
- if (inlineContext.context === "method-request") {
376
- transformedName += "Request";
377
- }
378
- else if (inlineContext.context === "method-response") {
379
- transformedName += "Response";
380
- }
381
- nameToken = {
382
- ...originalName,
383
- text: transformedName,
384
- };
385
- }
386
- else {
387
- // Read the name.
388
- const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
389
- if (nameMatch.case < 0) {
390
- return null;
391
- }
392
- Casing.validate(nameMatch.token, "UpperCamel", it.errors);
393
- nameToken = nameMatch.token;
394
- }
395
- let stableId = null;
396
- if (it.current === "(") {
397
- it.next();
398
- stableId = parseUint32(it, "?");
399
- if (stableId === -2) {
400
- return null;
401
- }
402
- if (it.expectThenNext([")"]).case < 0) {
403
- return null;
404
- }
405
- }
406
- if (it.expectThenNext(["{"]).case < 0) {
407
- return null;
408
- }
409
- const declarations = parseDeclarations(it, recordType);
410
- it.expectThenNext(["}"]);
411
- const builder = new RecordBuilder(nameToken, recordType, doc, stableId, inlineContext, it.errors);
412
- for (const declaration of declarations) {
413
- builder.addDeclaration(declaration);
414
- if (declaration.kind === "field" && declaration.inlineRecord) {
415
- builder.addDeclaration(declaration.inlineRecord);
416
- }
417
- }
418
- return builder.build();
419
- }
420
- function parseField(it, name, doc, recordType) {
421
- // May only be undefined if the type is an enum.
422
- let type;
423
- let inlineRecord;
424
- let number = -1;
425
- const makeField = () => {
426
- return {
427
- kind: "field",
428
- name: name,
429
- number: number,
430
- doc: doc,
431
- unresolvedType: type,
432
- // Will be populated at a later stage.
433
- type: undefined,
434
- // Will be populated at a later stage.
435
- isRecursive: false,
436
- inlineRecord: inlineRecord,
437
- };
438
- };
439
- while (true) {
440
- const typeAllowed = type === undefined && number < 0;
441
- const endAllowed = type !== undefined || recordType === "enum";
442
- const numberAllowed = number < 0 && endAllowed;
443
- const expected = [
444
- /*0:*/ typeAllowed ? ":" : null,
445
- /*1:*/ numberAllowed ? "=" : null,
446
- /*2:*/ endAllowed ? ";" : null,
447
- ];
448
- const match = it.expectThenNext(expected);
449
- switch (match.case) {
450
- case 0: {
451
- const inlineContext = {
452
- context: "field",
453
- originalName: name,
454
- };
455
- const typeOrInlineRecord = parseTypeOrInlineRecord(it, inlineContext);
456
- type = typeOrInlineRecord.type;
457
- inlineRecord = typeOrInlineRecord.inlineRecord;
458
- if (type === undefined) {
459
- return null;
460
- }
461
- break;
462
- }
463
- case 1: {
464
- number = parseUint32(it);
465
- if (number < 0) {
466
- return null;
467
- }
468
- break;
469
- }
470
- case 2: {
471
- const expectedCasing = type ? "lower_underscore" : "UPPER_UNDERSCORE";
472
- Casing.validate(name, expectedCasing, it.errors);
473
- if (recordType === "enum" && name.text === "UNKNOWN") {
474
- it.errors.push({
475
- token: name,
476
- message: `Cannot name field of enum: UNKNOWN`,
477
- });
478
- }
479
- return makeField();
480
- }
481
- case -1: {
482
- if (endAllowed) {
483
- return makeField();
484
- }
485
- else {
486
- return null;
487
- }
488
- }
489
- }
490
- }
491
- }
492
- const PRIMITIVE_TYPES = new Set([
493
- "bool",
494
- "int32",
495
- "int64",
496
- "hash64",
497
- "float32",
498
- "float64",
499
- "timestamp",
500
- "string",
501
- "bytes",
502
- ]);
503
- function parseTypeOrInlineRecord(it, inlineContext) {
504
- if (it.current === "struct" || it.current === "enum") {
505
- const recordType = it.current;
506
- it.next();
507
- const inlineRecord = parseRecord(it, recordType, EMPTY_DOC, inlineContext);
508
- const type = inlineRecord
509
- ? {
510
- kind: "record",
511
- nameParts: [inlineRecord.name],
512
- absolute: false,
513
- }
514
- : undefined;
515
- return {
516
- type: type,
517
- inlineRecord: inlineRecord ? inlineRecord : undefined,
518
- };
519
- }
520
- else {
521
- return {
522
- type: parseType(it),
523
- inlineRecord: undefined,
524
- };
525
- }
526
- }
527
- function parseType(it) {
528
- const match = it.expectThenNext([
529
- /*0:*/ "[",
530
- /*1:*/ TOKEN_IS_IDENTIFIER,
531
- /*2:*/ ".",
532
- ]);
533
- let value;
534
- switch (match.case) {
535
- case 0: {
536
- // Left square bracket.
537
- value = parseArrayType(it);
538
- break;
539
- }
540
- case 1:
541
- // An identifier.
542
- if (PRIMITIVE_TYPES.has(match.token.text)) {
543
- value = {
544
- kind: "primitive",
545
- primitive: match.token.text,
546
- };
547
- break;
548
- }
549
- // Falls through.
550
- case 2: {
551
- // Dot.
552
- value = parseRecordRef(it, match.token);
553
- break;
554
- }
555
- default:
556
- return undefined;
557
- }
558
- if (value === undefined) {
559
- return undefined;
560
- }
561
- if (it.current === "?") {
562
- it.next();
563
- return { kind: "optional", other: value };
564
- }
565
- else {
566
- return value;
567
- }
568
- }
569
- function parseArrayType(it) {
570
- const item = parseType(it);
571
- if (item === undefined) {
572
- return undefined;
573
- }
574
- let key = undefined;
575
- while (true) {
576
- const keyAllowed = !key && item.kind === "record";
577
- const match = it.expectThenNext([
578
- /*0:*/ keyAllowed ? "|" : null,
579
- /*1:*/ "]",
580
- ]);
581
- switch (match.case) {
582
- case 0: {
583
- // '|'
584
- key = parseFieldPath(it, match.token);
585
- if (key === null) {
586
- return undefined;
587
- }
588
- break;
589
- }
590
- case 1:
591
- return { kind: "array", item: item, key: key };
592
- default:
593
- return undefined;
594
- }
595
- }
596
- }
597
- function parseFieldPath(it, pipeToken) {
598
- const fieldNames = [];
599
- while (true) {
600
- const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
601
- if (match.case < 0) {
602
- return undefined;
603
- }
604
- fieldNames.push(match.token);
605
- if (it.current === ".") {
606
- it.next();
607
- }
608
- else {
609
- break;
610
- }
611
- }
612
- const path = fieldNames.map((name) => ({
613
- name: name,
614
- }));
615
- return {
616
- pipeToken: pipeToken,
617
- path,
618
- // Just because we need to provide a value.
619
- // The correct value will be populated at a later stage.
620
- keyType: { kind: "primitive", primitive: "bool" },
621
- };
622
- }
623
- function parseRecordRef(it, nameOrDot) {
624
- const absolute = nameOrDot.text === ".";
625
- const nameParts = [];
626
- if (nameOrDot.text === ".") {
627
- const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
628
- if (match.case < 0) {
629
- return undefined;
630
- }
631
- nameParts.push(match.token);
632
- }
633
- else {
634
- nameParts.push(nameOrDot);
635
- }
636
- while (it.current === ".") {
637
- it.next();
638
- const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
639
- if (match.case < 0) {
640
- return undefined;
641
- }
642
- nameParts.push(match.token);
643
- }
644
- return { kind: "record", nameParts: nameParts, absolute: absolute };
645
- }
646
- function parseUint32(it, maybeQuestionMark) {
647
- if (maybeQuestionMark && it.mode === "lenient" && it.current === "?") {
648
- it.next();
649
- return -1;
650
- }
651
- const match = it.expectThenNext([TOKEN_IS_POSITIVE_INT]);
652
- if (match.case < 0) {
653
- return -2;
654
- }
655
- const { text } = match.token;
656
- const valueAsBigInt = BigInt(text);
657
- if (valueAsBigInt < BigInt(2 ** 32)) {
658
- return +text;
659
- }
660
- else {
661
- it.errors.push({
662
- token: match.token,
663
- message: "Value out of uint32 range",
664
- });
665
- return -2;
666
- }
667
- }
668
- // Parses the 'removed' declaration.
669
- // Assumes the current token is the token after 'removed'.
670
- function parseRemoved(it, removedToken) {
671
- const numbers = [];
672
- // The 5 states are:
673
- // · '?': expect a number or a semicolon
674
- // · ',': expect a comma or a semicolon
675
- // · '..': expect a comma, a semicolon or a '..'
676
- // · '0': expect a single number or the lower bound of a range
677
- // · '1': expect the upper bound of a range
678
- let expect = "?";
679
- let lowerBound;
680
- loop: while (true) {
681
- const endAllowed = expect === "?" || expect === "," || expect === "..";
682
- const expected = [
683
- /*0:*/ expect === "," || expect === ".." ? "," : null,
684
- /*1:*/ expect === "?" || expect === "0" || expect === "1"
685
- ? TOKEN_IS_POSITIVE_INT
686
- : null,
687
- /*2:*/ endAllowed ? ";" : null,
688
- /*3:*/ expect === ".." ? ".." : null,
689
- ];
690
- const match = it.expectThenNext(expected);
691
- switch (match.case) {
692
- case 0: {
693
- // A comma.
694
- expect = "0";
695
- break;
696
- }
697
- case 1: {
698
- // A number.
699
- const number = +match.token.text;
700
- if (lowerBound === undefined) {
701
- expect = "..";
702
- numbers.push(number);
703
- }
704
- else {
705
- expect = ",";
706
- if (number <= lowerBound) {
707
- it.errors.push({
708
- token: removedToken,
709
- message: "Upper bound must be greater than lower bound",
710
- });
711
- }
712
- for (let n = lowerBound; n <= number; ++n) {
713
- numbers.push(n);
714
- }
715
- lowerBound = undefined;
716
- }
717
- break;
718
- }
719
- case 2: {
720
- // A semicolon.
721
- break loop;
722
- }
723
- case 3: {
724
- // A '..'
725
- expect = "1";
726
- lowerBound = numbers.pop();
727
- break;
728
- }
729
- case -1:
730
- if (endAllowed) {
731
- break loop;
732
- }
733
- else {
734
- return null;
735
- }
736
- }
737
- }
738
- // Make sure we don't have a duplicate number.
739
- const seenNumbers = new Set();
740
- for (const number of numbers) {
741
- if (seenNumbers.has(number)) {
742
- it.errors.push({
743
- token: removedToken,
744
- message: `Duplicate field number ${number}`,
745
- });
746
- return null;
747
- }
748
- seenNumbers.add(number);
749
- }
750
- return {
751
- kind: "removed",
752
- removedToken: removedToken,
753
- numbers: numbers,
754
- };
755
- }
756
- function parseImport(it, importToken) {
757
- const tokenMatch = it.expectThenNext(["*", "{", TOKEN_IS_IDENTIFIER]);
758
- switch (tokenMatch.case) {
759
- case 0:
760
- return parseImportAs(it, importToken);
761
- case 1:
762
- return parseImportGivenNames(it, importToken);
763
- case 2:
764
- // TODO: drop this case once the curly bracket syntax becomes mandatory.
765
- return parseImportGivenNames(it, importToken, tokenMatch.token);
766
- default:
767
- return null;
768
- }
769
- }
770
- function parseImportAs(it, importToken) {
771
- if (it.expectThenNext(["as"]).case < 0)
772
- return null;
773
- const aliasMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
774
- if (aliasMatch.case < 0) {
775
- return null;
776
- }
777
- Casing.validate(aliasMatch.token, "lower_underscore", it.errors);
778
- if (it.expectThenNext(["from"]).case < 0)
779
- return null;
780
- const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
781
- if (modulePathMatch.case < 0) {
782
- return null;
783
- }
784
- const range = {
785
- start: importToken.position,
786
- end: it.currentToken.position + it.current.length,
787
- };
788
- it.expectThenNext([";"]);
789
- const modulePath = modulePathMatch.token;
790
- return {
791
- kind: "import-alias",
792
- importToken: importToken,
793
- name: aliasMatch.token,
794
- range: range,
795
- modulePath: modulePath,
796
- };
797
- }
798
- function parseImportGivenNames(it, importToken,
799
- // TODO: remove this parameter once the curly bracket syntax becomes mandatory
800
- firstName) {
801
- const importedNames = firstName ? [firstName] : [];
802
- if (firstName) {
803
- // Old syntax
804
- while (it.current === ",") {
805
- it.next();
806
- const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
807
- if (nameMatch.case < 0) {
808
- return null;
809
- }
810
- importedNames.push(nameMatch.token);
811
- }
812
- }
813
- else {
814
- // New syntax with curly brackets
815
- while (true) {
816
- let match = it.expectThenNext([TOKEN_IS_IDENTIFIER, "}"]);
817
- if (match.case === 0) {
818
- importedNames.push(match.token);
819
- }
820
- else if (match.case === 1) {
821
- break;
822
- }
823
- else {
824
- return null;
825
- }
826
- match = it.expectThenNext([",", "}"]);
827
- if (match.case === 1) {
828
- break;
829
- }
830
- else if (match.case < 0) {
831
- return null;
832
- }
833
- }
834
- }
835
- if (it.expectThenNext(["from"]).case < 0)
836
- return null;
837
- const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
838
- if (modulePathMatch.case < 0) {
839
- return null;
840
- }
841
- const range = {
842
- start: importToken.position,
843
- end: it.currentToken.position + it.current.length,
844
- };
845
- it.expectThenNext([";"]);
846
- const modulePath = modulePathMatch.token;
847
- return {
848
- kind: "import",
849
- importToken: importToken,
850
- importedNames: importedNames,
851
- range: range,
852
- modulePath: modulePath,
853
- };
854
- }
855
- function parseMethod(it, doc) {
856
- const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
857
- if (nameMatch.case < 0) {
858
- return null;
859
- }
860
- const name = nameMatch.token;
861
- Casing.validate(name, "UpperCamel", it.errors);
862
- if (it.expectThenNext(["("]).case < 0) {
863
- return null;
864
- }
865
- const requestTypeOrInlineRecord = parseTypeOrInlineRecord(it, {
866
- context: "method-request",
867
- originalName: name,
868
- });
869
- const requestType = requestTypeOrInlineRecord.type;
870
- if (!requestType) {
871
- return null;
872
- }
873
- if (it.expectThenNext([")"]).case < 0) {
874
- return null;
875
- }
876
- if (it.expectThenNext([":"]).case < 0) {
877
- return {
878
- kind: "broken-method",
879
- unresolvedRequestType: requestType,
880
- inlineRequestRecord: requestTypeOrInlineRecord.inlineRecord,
881
- unresolvedResponseType: undefined,
882
- inlineResponseRecord: undefined,
883
- };
884
- }
885
- const responseTypeOrInlineRecord = parseTypeOrInlineRecord(it, {
886
- context: "method-response",
887
- originalName: name,
888
- });
889
- const responseType = responseTypeOrInlineRecord.type;
890
- if (!responseType) {
891
- return null;
892
- }
893
- if (it.expectThenNext(["="]).case < 0) {
894
- return {
895
- kind: "broken-method",
896
- unresolvedRequestType: requestType,
897
- inlineRequestRecord: requestTypeOrInlineRecord.inlineRecord,
898
- unresolvedResponseType: responseType,
899
- inlineResponseRecord: responseTypeOrInlineRecord.inlineRecord,
900
- };
901
- }
902
- const number = parseUint32(it, "?");
903
- if (number === -2) {
904
- return null;
905
- }
906
- it.expectThenNext([";"]);
907
- return {
908
- kind: "method",
909
- name: nameMatch.token,
910
- doc: doc,
911
- unresolvedRequestType: requestType,
912
- unresolvedResponseType: responseType,
913
- // Will be populated at a later stage.
914
- requestType: undefined,
915
- // Will be populated at a later stage.
916
- responseType: undefined,
917
- number: number,
918
- inlineRequestRecord: requestTypeOrInlineRecord.inlineRecord,
919
- inlineResponseRecord: responseTypeOrInlineRecord.inlineRecord,
920
- };
921
- }
922
- function parseConstant(it, doc) {
923
- const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
924
- if (nameMatch.case < 0) {
925
- return null;
926
- }
927
- Casing.validate(nameMatch.token, "UPPER_UNDERSCORE", it.errors);
928
- if (it.expectThenNext([":"]).case < 0) {
929
- return null;
930
- }
931
- const type = parseType(it);
932
- if (!type) {
933
- return null;
934
- }
935
- if (it.expectThenNext(["="]).case < 0) {
936
- return {
937
- kind: "broken-constant",
938
- unresolvedType: type,
939
- };
940
- }
941
- const value = parseValue(it);
942
- if (value === null) {
943
- return null;
944
- }
945
- it.expectThenNext([";"]);
946
- return {
947
- kind: "constant",
948
- name: nameMatch.token,
949
- doc: doc,
950
- unresolvedType: type,
951
- type: undefined,
952
- value: value,
953
- valueAsDenseJson: undefined,
954
- };
955
- }
956
- function parseValue(it) {
957
- const expected = [
958
- /*0:*/ "{",
959
- /*1:*/ "{|",
960
- /*2:*/ "[",
961
- /*3:*/ "false",
962
- /*4:*/ "true",
963
- /*5:*/ "null",
964
- /*6:*/ TOKEN_IS_NUMBER,
965
- /*7:*/ TOKEN_IS_STRING_LITERAL,
966
- ];
967
- const match = it.expectThenNext(expected);
968
- switch (match.case) {
969
- case 0:
970
- case 1: {
971
- const partial = match.case === 1;
972
- return parseObjectValue(match.token, it, partial);
973
- }
974
- case 2: {
975
- const items = parseArrayValue(it);
976
- if (items === null) {
977
- return null;
978
- }
979
- return {
980
- kind: "array",
981
- token: match.token,
982
- items: items,
983
- };
984
- }
985
- case 3:
986
- case 4:
987
- case 5:
988
- case 6:
989
- case 7:
990
- return {
991
- kind: "literal",
992
- token: match.token,
993
- };
994
- default:
995
- return null;
996
- }
997
- }
998
- function parseObjectValue(objectToken, it, partial) {
999
- const closingToken = partial ? "|}" : "}";
1000
- const entries = {};
1001
- const orphanNames = [];
1002
- const makeObjectValue = () => ({
1003
- kind: "object",
1004
- token: objectToken,
1005
- entries: entries,
1006
- orphanNames: orphanNames,
1007
- partial: partial,
1008
- });
1009
- while (true) {
1010
- if (it.current === closingToken) {
1011
- it.next();
1012
- return makeObjectValue();
1013
- }
1014
- const fieldNameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
1015
- if (fieldNameMatch.case < 0) {
1016
- return null;
1017
- }
1018
- const fieldNameToken = fieldNameMatch.token;
1019
- const fieldName = fieldNameMatch.token.text;
1020
- if (it.expectThenNext([":"]).case < 0) {
1021
- orphanNames.push(fieldNameToken);
1022
- continue;
1023
- }
1024
- if (fieldName in entries) {
1025
- it.errors.push({
1026
- token: fieldNameMatch.token,
1027
- message: "Duplicate field",
1028
- });
1029
- }
1030
- const value = parseValue(it);
1031
- if (value === null) {
1032
- return null;
1033
- }
1034
- entries[fieldName] = {
1035
- name: fieldNameToken,
1036
- value: value,
1037
- };
1038
- const endMatch = it.expectThenNext([",", closingToken]);
1039
- if (endMatch.case < 0) {
1040
- return null;
1041
- }
1042
- if (endMatch.token.text === closingToken) {
1043
- return makeObjectValue();
1044
- }
1045
- }
1046
- }
1047
- function parseArrayValue(it) {
1048
- if (it.current === "]") {
1049
- it.next();
1050
- return [];
1051
- }
1052
- const items = [];
1053
- while (true) {
1054
- const item = parseValue(it);
1055
- if (item === null) {
1056
- return null;
1057
- }
1058
- items.push(item);
1059
- const match = it.expectThenNext([",", "]"]);
1060
- if (match.case < 0) {
1061
- return null;
1062
- }
1063
- if (match.token.text === "]") {
1064
- return items;
1065
- }
1066
- if (it.current === "]") {
1067
- it.next();
1068
- return items;
1069
- }
1070
- }
1071
- }
1072
- function collectDoc(it) {
1073
- const { moduleTokens } = it;
1074
- const docComments = [];
1075
- while (it.current.startsWith("///")) {
1076
- docComments.push(it.currentToken);
1077
- it.next();
1078
- }
1079
- return mergeDocs(docComments.map((c) => moduleTokens.posToDoc[c.position] ?? EMPTY_DOC));
1080
- }
1081
- const EMPTY_DOC = Object.freeze({
1082
- text: "",
1083
- pieces: Object.freeze([]),
1084
- });
1085
- class TokenPredicate {
1086
- }
1087
- class TokenIsIdentifier extends TokenPredicate {
1088
- matches(token) {
1089
- // "..." is the autocompletion placeholder
1090
- return /^\w/.test(token) || token === "...";
1091
- }
1092
- what() {
1093
- return "identifier";
1094
- }
1095
- }
1096
- const TOKEN_IS_IDENTIFIER = new TokenIsIdentifier();
1097
- class TokenIsPositiveInt extends TokenPredicate {
1098
- matches(token) {
1099
- return /^[0-9]+$/.test(token);
1100
- }
1101
- what() {
1102
- return "positive integer";
1103
- }
1104
- }
1105
- const TOKEN_IS_POSITIVE_INT = new TokenIsPositiveInt();
1106
- class TokenIsNumber extends TokenPredicate {
1107
- matches(token) {
1108
- return /^[0-9-]/.test(token);
1109
- }
1110
- what() {
1111
- return "number";
1112
- }
1113
- }
1114
- const TOKEN_IS_NUMBER = new TokenIsNumber();
1115
- class TokenIsStringLiteral extends TokenPredicate {
1116
- matches(token) {
1117
- return /^["']/.test(token);
1118
- }
1119
- what() {
1120
- return "string literal";
1121
- }
1122
- }
1123
- const TOKEN_IS_STRING_LITERAL = new TokenIsStringLiteral();
1124
- class TokenIterator {
1125
- constructor(moduleTokens, mode, errors) {
1126
- this.moduleTokens = moduleTokens;
1127
- this.mode = mode;
1128
- this.errors = errors;
1129
- this.tokenIndex = 0;
1130
- this.tokens = moduleTokens.tokens;
1131
- }
1132
- // Returns both:
1133
- // · the index of the first predicate matching the current token, or -1 if
1134
- // there is none
1135
- // · the current token (before the move)
1136
- //
1137
- // If the current token matches any predicate, i.e. if the index is not -1,
1138
- // moves to the next token before returning. Otherwise, registers an error.
1139
- expectThenNext(expected) {
1140
- let token = this.tokens[this.tokenIndex];
1141
- while (token.text.startsWith("///")) {
1142
- this.errors.push({
1143
- token: token,
1144
- message: "Doc comments can only precede declarations",
1145
- });
1146
- ++this.tokenIndex;
1147
- token = this.tokens[this.tokenIndex];
1148
- }
1149
- for (let i = 0; i < expected.length; ++i) {
1150
- const e = expected[i];
1151
- if (e === null) {
1152
- continue;
1153
- }
1154
- const match = e instanceof TokenPredicate ? e.matches(token.text) : token.text === e;
1155
- if (!match) {
1156
- continue;
1157
- }
1158
- ++this.tokenIndex;
1159
- return {
1160
- case: i,
1161
- token: token,
1162
- };
1163
- }
1164
- // No match: register an error.
1165
- const expectedParts = [];
1166
- for (let i = 0; i < expected.length; ++i) {
1167
- const e = expected[i];
1168
- if (e === null) {
1169
- continue;
1170
- }
1171
- expectedParts.push(e instanceof TokenPredicate ? e.what() : `'${e}'`);
1172
- }
1173
- const expectedMsg = expectedParts.length === 1
1174
- ? expectedParts[0]
1175
- : `one of: ${expectedParts.join(", ")}`;
1176
- this.errors.push({
1177
- token: token,
1178
- expected: expectedMsg,
1179
- });
1180
- return {
1181
- case: -1,
1182
- token: token,
1183
- };
1184
- }
1185
- get currentToken() {
1186
- return this.tokens[this.tokenIndex];
1187
- }
1188
- get current() {
1189
- return this.currentToken.text;
1190
- }
1191
- get previous() {
1192
- return this.tokens[this.tokenIndex - 1].text;
1193
- }
1194
- next() {
1195
- ++this.tokenIndex;
1196
- }
1197
- get index() {
1198
- return this.tokenIndex;
1199
- }
1200
- }
1201
- function collectModuleRecords(declarations) {
1202
- const result = [];
1203
- const collect = (declarations, ancestors) => {
1204
- for (const record of declarations) {
1205
- if (record.kind !== "record")
1206
- continue;
1207
- const updatedRecordAncestors = ancestors.concat([record]);
1208
- const modulePath = record.name.line.modulePath;
1209
- const recordLocation = {
1210
- kind: "record-location",
1211
- record: record,
1212
- recordAncestors: updatedRecordAncestors,
1213
- modulePath: modulePath,
1214
- };
1215
- // We want depth-first.
1216
- collect(record.declarations, updatedRecordAncestors);
1217
- result.push(recordLocation);
1218
- }
1219
- };
1220
- collect(declarations, []);
1221
- return result;
1222
- }
1223
- function validateImportBlock(declarations, moduleTokens, errors) {
1224
- let state = "no-import-seen";
1225
- let startPosition;
1226
- let endPosition = -1;
1227
- let anyError = false;
1228
- for (const declaration of declarations) {
1229
- if (declaration.kind === "import" || declaration.kind === "import-alias") {
1230
- if (state === "no-import-seen") {
1231
- state = "first-legal-import-seen";
1232
- startPosition = declaration.range.start;
1233
- }
1234
- else if (state === "last-legal-import-seen") {
1235
- errors.push({
1236
- token: declaration.importToken,
1237
- message: "Import declarations must be grouped together at the top",
1238
- });
1239
- anyError = true;
1240
- }
1241
- endPosition = Math.max(endPosition, declaration.range.end);
1242
- }
1243
- else {
1244
- // Not an import
1245
- state = "last-legal-import-seen";
1246
- }
1247
- }
1248
- if (startPosition === undefined || anyError) {
1249
- return null;
1250
- }
1251
- const range = {
1252
- start: startPosition,
1253
- end: endPosition,
1254
- };
1255
- // Look for comments within `range`.
1256
- {
1257
- let tokens = moduleTokens.tokensWithComments;
1258
- tokens = tokens.slice(tokens.findIndex((t) => t.position >= range.start));
1259
- tokens = tokens.slice(0, tokens.findIndex((t) => t.position >= range.end));
1260
- for (const token of tokens) {
1261
- if (token.text.startsWith("//")) {
1262
- errors.push({
1263
- token: token,
1264
- message: "Comments not allowed within import block",
1265
- });
1266
- anyError = true;
1267
- }
1268
- }
1269
- }
1270
- return anyError ? null : range;
1271
- }
1272
- /** Extracts the "@{...}/{...}/" package prefix from a module path. */
1273
- export function extractPackagePrefix(modulePath) {
1274
- const match = modulePath.match(/^(@[^/]+\/[^/]+\/)/);
1275
- return match?.at(1) ?? "";
1276
- }
1277
- function posixDirname(p) {
1278
- const i = p.lastIndexOf("/");
1279
- return i >= 0 ? p.slice(0, i) : "";
1280
- }
1281
- function posixNormalize(p) {
1282
- const parts = p.split("/");
1283
- const result = [];
1284
- for (const part of parts) {
1285
- if (part === "..") {
1286
- if (result.length > 0 && result[result.length - 1] !== "..") {
1287
- result.pop();
1288
- }
1289
- else {
1290
- result.push("..");
1291
- }
1292
- }
1293
- else if (part !== "." && part !== "") {
1294
- result.push(part);
1295
- }
1296
- }
1297
- return result.length === 0 ? "." : result.join("/");
1298
- }
1299
- function resolveModulePath(pathToken, originModulePath, errors) {
1300
- let modulePath = unquoteAndUnescape(pathToken.text);
1301
- if (/\\/.test(modulePath)) {
1302
- errors.push({
1303
- token: pathToken,
1304
- message: "Replace backslash with slash",
1305
- });
1306
- return undefined;
1307
- }
1308
- if (modulePath.startsWith("./") || modulePath.startsWith("../")) {
1309
- if (modulePath.includes("/@")) {
1310
- errors.push({
1311
- token: pathToken,
1312
- message: "Use absolute path",
1313
- });
1314
- return undefined;
1315
- }
1316
- // This is a relative path from the module. Let's transform it into a
1317
- // relative path from root.
1318
- modulePath = posixNormalize(posixDirname(originModulePath) + "/" + modulePath);
1319
- }
1320
- else if (originModulePath.startsWith("@") && !modulePath.startsWith("@")) {
1321
- const packagePrefix = extractPackagePrefix(originModulePath);
1322
- modulePath = packagePrefix + modulePath;
1323
- }
1324
- // "a/./b/../c" => "a/c"
1325
- modulePath = posixNormalize(modulePath);
1326
- if (modulePath.startsWith(`../`)) {
1327
- errors.push({
1328
- token: pathToken,
1329
- message: "Module path must point to a file within skir-src",
1330
- });
1331
- return undefined;
1332
- }
1333
- return modulePath;
1334
- }
1335
- //# sourceMappingURL=parser.js.map