skir 0.0.1 → 0.0.2

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 (115) hide show
  1. package/dist/casing.d.ts +1 -4
  2. package/dist/casing.d.ts.map +1 -1
  3. package/dist/casing.js +0 -29
  4. package/dist/casing.js.map +1 -1
  5. package/dist/casing.test.js +2 -13
  6. package/dist/casing.test.js.map +1 -1
  7. package/dist/command_line_parser.d.ts +3 -3
  8. package/dist/command_line_parser.d.ts.map +1 -1
  9. package/dist/command_line_parser.js +35 -38
  10. package/dist/command_line_parser.js.map +1 -1
  11. package/dist/command_line_parser.test.js +73 -78
  12. package/dist/command_line_parser.test.js.map +1 -1
  13. package/dist/compatibility_checker.d.ts +9 -3
  14. package/dist/compatibility_checker.d.ts.map +1 -1
  15. package/dist/compatibility_checker.js +17 -4
  16. package/dist/compatibility_checker.js.map +1 -1
  17. package/dist/compatibility_checker.test.js +55 -1
  18. package/dist/compatibility_checker.test.js.map +1 -1
  19. package/dist/compiler.js +34 -17
  20. package/dist/compiler.js.map +1 -1
  21. package/dist/config.d.ts +5 -35
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/definition_finder.d.ts +1 -1
  24. package/dist/definition_finder.d.ts.map +1 -1
  25. package/dist/doc_comment_parser.d.ts +3 -0
  26. package/dist/doc_comment_parser.d.ts.map +1 -0
  27. package/dist/doc_comment_parser.js +219 -0
  28. package/dist/doc_comment_parser.js.map +1 -0
  29. package/dist/doc_comment_parser.test.d.ts +2 -0
  30. package/dist/doc_comment_parser.test.d.ts.map +1 -0
  31. package/dist/doc_comment_parser.test.js +494 -0
  32. package/dist/doc_comment_parser.test.js.map +1 -0
  33. package/dist/error_renderer.d.ts +1 -1
  34. package/dist/error_renderer.d.ts.map +1 -1
  35. package/dist/error_renderer.js +84 -65
  36. package/dist/error_renderer.js.map +1 -1
  37. package/dist/formatter.d.ts +15 -2
  38. package/dist/formatter.d.ts.map +1 -1
  39. package/dist/formatter.js +191 -234
  40. package/dist/formatter.js.map +1 -1
  41. package/dist/formatter.test.js +322 -88
  42. package/dist/formatter.test.js.map +1 -1
  43. package/dist/index.d.ts +1 -5
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -4
  46. package/dist/index.js.map +1 -1
  47. package/dist/language_server.js +1 -1
  48. package/dist/language_server.js.map +1 -1
  49. package/dist/literals.d.ts +1 -2
  50. package/dist/literals.d.ts.map +1 -1
  51. package/dist/literals.js +1 -12
  52. package/dist/literals.js.map +1 -1
  53. package/dist/literals.test.js +1 -4
  54. package/dist/literals.test.js.map +1 -1
  55. package/dist/module_set.d.ts +3 -7
  56. package/dist/module_set.d.ts.map +1 -1
  57. package/dist/module_set.js +205 -51
  58. package/dist/module_set.js.map +1 -1
  59. package/dist/module_set.test.js +595 -28
  60. package/dist/module_set.test.js.map +1 -1
  61. package/dist/parser.d.ts +3 -4
  62. package/dist/parser.d.ts.map +1 -1
  63. package/dist/parser.js +185 -92
  64. package/dist/parser.js.map +1 -1
  65. package/dist/parser.test.js +243 -15
  66. package/dist/parser.test.js.map +1 -1
  67. package/dist/project_initializer.d.ts +2 -0
  68. package/dist/project_initializer.d.ts.map +1 -0
  69. package/dist/project_initializer.js +30 -0
  70. package/dist/project_initializer.js.map +1 -0
  71. package/dist/snapshotter.d.ts +3 -0
  72. package/dist/snapshotter.d.ts.map +1 -1
  73. package/dist/snapshotter.js +43 -6
  74. package/dist/snapshotter.js.map +1 -1
  75. package/dist/tokenizer.d.ts +8 -2
  76. package/dist/tokenizer.d.ts.map +1 -1
  77. package/dist/tokenizer.js +26 -20
  78. package/dist/tokenizer.js.map +1 -1
  79. package/dist/tokenizer.test.js +285 -269
  80. package/dist/tokenizer.test.js.map +1 -1
  81. package/package.json +7 -5
  82. package/src/casing.ts +1 -36
  83. package/src/command_line_parser.ts +42 -48
  84. package/src/compatibility_checker.ts +29 -7
  85. package/src/compiler.ts +35 -18
  86. package/src/definition_finder.ts +1 -1
  87. package/src/doc_comment_parser.ts +246 -0
  88. package/src/error_renderer.ts +90 -66
  89. package/src/formatter.ts +249 -238
  90. package/src/index.ts +0 -6
  91. package/src/language_server.ts +8 -8
  92. package/src/literals.ts +5 -14
  93. package/src/module_set.ts +259 -79
  94. package/src/parser.ts +213 -98
  95. package/src/project_initializer.ts +39 -0
  96. package/src/snapshotter.ts +46 -5
  97. package/src/tokenizer.ts +47 -25
  98. package/dist/encoding.d.ts +0 -2
  99. package/dist/encoding.d.ts.map +0 -1
  100. package/dist/encoding.js +0 -38
  101. package/dist/encoding.js.map +0 -1
  102. package/dist/encoding.test.d.ts +0 -2
  103. package/dist/encoding.test.d.ts.map +0 -1
  104. package/dist/encoding.test.js +0 -23
  105. package/dist/encoding.test.js.map +0 -1
  106. package/dist/index.test.d.ts +0 -2
  107. package/dist/index.test.d.ts.map +0 -1
  108. package/dist/index.test.js +0 -14
  109. package/dist/index.test.js.map +0 -1
  110. package/dist/types.d.ts +0 -375
  111. package/dist/types.d.ts.map +0 -1
  112. package/dist/types.js +0 -2
  113. package/dist/types.js.map +0 -1
  114. package/src/encoding.ts +0 -32
  115. package/src/types.ts +0 -518
package/dist/parser.js CHANGED
@@ -1,10 +1,13 @@
1
+ import { convertCase, simpleHash } from "skir-internal";
1
2
  import * as casing from "./casing.js";
3
+ import { parseDocComments } from "./doc_comment_parser.js";
2
4
  /** Runs syntactic analysis on a module. */
3
- export function parseModule(tokens, modulePath, sourceCode) {
5
+ export function parseModule(moduleTokens) {
6
+ const { tokens, modulePath, sourceCode } = moduleTokens;
4
7
  const errors = [];
5
8
  const it = new TokenIterator(tokens, errors);
6
9
  const declarations = parseDeclarations(it, "module");
7
- it.expectThenMove([""]);
10
+ it.expectThenNext([""]);
8
11
  // Create a mappinng from names to declarations, and check for duplicates.
9
12
  const nameToDeclaration = {};
10
13
  for (const declaration of declarations) {
@@ -20,7 +23,7 @@ export function parseModule(tokens, modulePath, sourceCode) {
20
23
  if (name in nameToDeclaration) {
21
24
  errors.push({
22
25
  token: nameToken,
23
- message: `Duplicate identifier "${name}"`,
26
+ message: `Duplicate identifier '${name}'`,
24
27
  });
25
28
  }
26
29
  else {
@@ -52,11 +55,19 @@ function parseDeclarations(it, parentNode) {
52
55
  const isEndToken = (t) => t === "" || (parentNode !== "module" && t === "}");
53
56
  // Returns true if the token may be the last token of a valid statement.
54
57
  const isLastToken = (t) => t === "}" || t === ";";
55
- while (!isEndToken(it.peek())) {
58
+ while (!isEndToken(it.current)) {
56
59
  const startIndex = it.index;
57
60
  const declaration = parseDeclaration(it, parentNode);
58
61
  if (declaration !== null) {
59
62
  result.push(declaration);
63
+ if (declaration.kind === "method") {
64
+ if (declaration.inlineRequestRecord) {
65
+ result.push(declaration.inlineRequestRecord);
66
+ }
67
+ if (declaration.inlineResponseRecord) {
68
+ result.push(declaration.inlineResponseRecord);
69
+ }
70
+ }
60
71
  continue;
61
72
  }
62
73
  // We have an invalid statement. An error was already registered. Perhaps
@@ -67,16 +78,16 @@ function parseDeclarations(it, parentNode) {
67
78
  const noTokenWasConsumed = it.index === startIndex;
68
79
  if (noTokenWasConsumed) {
69
80
  it.next();
70
- if (isLastToken(it.peekBack())) {
81
+ if (isLastToken(it.previous)) {
71
82
  // For example: two semicolons in a row.
72
83
  continue;
73
84
  }
74
85
  }
75
86
  if (noTokenWasConsumed ||
76
- (it.peek() !== "" && !isLastToken(it.peekBack()))) {
87
+ (it.current !== "" && !isLastToken(it.previous))) {
77
88
  let nestedLevel = 0;
78
89
  while (true) {
79
- const token = it.peek();
90
+ const token = it.current;
80
91
  if (token === "") {
81
92
  break;
82
93
  }
@@ -96,6 +107,7 @@ function parseDeclarations(it, parentNode) {
96
107
  return result;
97
108
  }
98
109
  function parseDeclaration(it, parentNode) {
110
+ const doc = parseDoc(it);
99
111
  let recordType = "enum";
100
112
  const parentIsRoot = parentNode === "module";
101
113
  const expected = [
@@ -107,31 +119,32 @@ function parseDeclaration(it, parentNode) {
107
119
  /*5:*/ parentIsRoot ? "method" : null,
108
120
  /*6:*/ parentIsRoot ? "const" : null,
109
121
  ];
110
- const match = it.expectThenMove(expected);
122
+ const match = it.expectThenNext(expected);
111
123
  switch (match.case) {
112
124
  case 0:
113
125
  recordType = "struct";
114
126
  // Falls through.
115
127
  case 1:
116
- return parseRecord(it, recordType);
128
+ return parseRecord(it, recordType, doc);
117
129
  case 2:
118
130
  return parseRemoved(it, match.token);
119
131
  case 3:
120
- return parseField(it, match.token, parentNode);
132
+ return parseField(it, match.token, doc, parentNode);
121
133
  case 4:
122
134
  return parseImport(it);
123
135
  case 5:
124
- return parseMethod(it);
136
+ return parseMethod(it, doc);
125
137
  case 6:
126
- return parseConstant(it);
138
+ return parseConstant(it, doc);
127
139
  default:
128
140
  return null;
129
141
  }
130
142
  }
131
143
  class RecordBuilder {
132
- constructor(recordName, recordType, stableId, errors) {
144
+ constructor(recordName, recordType, doc, stableId, errors) {
133
145
  this.recordName = recordName;
134
146
  this.recordType = recordType;
147
+ this.doc = doc;
135
148
  this.stableId = stableId;
136
149
  this.errors = errors;
137
150
  this.nameToDeclaration = {};
@@ -195,7 +208,7 @@ class RecordBuilder {
195
208
  if (name in this.nameToDeclaration) {
196
209
  this.errors.push({
197
210
  token: nameToken,
198
- message: `Duplicate identifier "${name}"`,
211
+ message: `Duplicate identifier '${name}'`,
199
212
  });
200
213
  return;
201
214
  }
@@ -256,11 +269,11 @@ class RecordBuilder {
256
269
  key: key,
257
270
  name: this.recordName,
258
271
  recordType: this.recordType,
272
+ doc: this.doc,
259
273
  nameToDeclaration: this.nameToDeclaration,
260
274
  declarations: Object.values(this.nameToDeclaration),
261
275
  fields: fields,
262
276
  nestedRecords: nestedRecords,
263
- numbering: this.numbering,
264
277
  removedNumbers: this.removedNumbers.sort(),
265
278
  recordNumber: this.stableId,
266
279
  numSlots: numSlots,
@@ -268,38 +281,61 @@ class RecordBuilder {
268
281
  };
269
282
  }
270
283
  }
271
- function parseRecord(it, recordType) {
284
+ function parseRecord(it, recordType, doc, inlineContext) {
272
285
  // A struct or an enum.
273
- const nameMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
274
- if (nameMatch.case < 0) {
275
- return null;
286
+ let nameToken;
287
+ if (inlineContext) {
288
+ const { originalName } = inlineContext;
289
+ let transformedName = convertCase(originalName.text, "UpperCamel");
290
+ if (inlineContext.context === "method-request") {
291
+ transformedName += "Request";
292
+ }
293
+ else if (inlineContext.context === "method-response") {
294
+ transformedName += "Response";
295
+ }
296
+ nameToken = {
297
+ ...originalName,
298
+ text: transformedName,
299
+ };
300
+ }
301
+ else {
302
+ // Read the name.
303
+ const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
304
+ if (nameMatch.case < 0) {
305
+ return null;
306
+ }
307
+ casing.validate(nameMatch.token, "UpperCamel", it.errors);
308
+ nameToken = nameMatch.token;
276
309
  }
277
- casing.validate(nameMatch.token, "UpperCamel", it.errors);
278
310
  let stableId = null;
279
- if (it.peek() === "(") {
311
+ if (it.current === "(") {
280
312
  it.next();
281
313
  stableId = parseUint32(it);
282
314
  if (stableId < 0) {
283
315
  return null;
284
316
  }
285
- if (it.expectThenMove([")"]).case < 0) {
317
+ if (it.expectThenNext([")"]).case < 0) {
286
318
  return null;
287
319
  }
288
320
  }
289
- if (it.expectThenMove(["{"]).case < 0) {
321
+ if (it.expectThenNext(["{"]).case < 0) {
290
322
  return null;
291
323
  }
292
324
  const declarations = parseDeclarations(it, recordType);
293
- it.expectThenMove(["}"]);
294
- const builder = new RecordBuilder(nameMatch.token, recordType, stableId, it.errors);
325
+ it.expectThenNext(["}"]);
326
+ const builder = new RecordBuilder(nameToken, recordType, doc, stableId, it.errors);
295
327
  for (const declaration of declarations) {
296
328
  builder.addDeclaration(declaration);
329
+ if (declaration.kind === "field" && declaration.inlineRecord) {
330
+ builder.addDeclaration(declaration.inlineRecord);
331
+ }
297
332
  }
298
333
  return builder.build();
299
334
  }
300
- function parseField(it, name, recordType) {
335
+ function parseField(it, name, doc, recordType) {
301
336
  // May only be undefined if the type is an enum.
302
- let type = undefined;
337
+ let type;
338
+ let inlineRecord;
303
339
  let number = -1;
304
340
  while (true) {
305
341
  const typeAllowed = type === undefined && number < 0;
@@ -310,10 +346,16 @@ function parseField(it, name, recordType) {
310
346
  /*1:*/ numberAllowed ? "=" : null,
311
347
  /*2:*/ endAllowed ? ";" : null,
312
348
  ];
313
- const match = it.expectThenMove(expected);
349
+ const match = it.expectThenNext(expected);
314
350
  switch (match.case) {
315
351
  case 0: {
316
- type = parseType(it);
352
+ const inlineContext = {
353
+ context: "field",
354
+ originalName: name,
355
+ };
356
+ const typeOrInlineRecord = parseTypeOrInlineRecord(it, inlineContext);
357
+ type = typeOrInlineRecord.type;
358
+ inlineRecord = typeOrInlineRecord.inlineRecord;
317
359
  if (type === undefined) {
318
360
  return null;
319
361
  }
@@ -340,11 +382,13 @@ function parseField(it, name, recordType) {
340
382
  kind: "field",
341
383
  name: name,
342
384
  number: number,
385
+ doc: doc,
343
386
  unresolvedType: type,
344
387
  // Will be populated at a later stage.
345
388
  type: undefined,
346
389
  // Will be populated at a later stage.
347
390
  isRecursive: false,
391
+ inlineRecord: inlineRecord,
348
392
  };
349
393
  }
350
394
  case -1:
@@ -363,8 +407,32 @@ const PRIMITIVE_TYPES = new Set([
363
407
  "string",
364
408
  "bytes",
365
409
  ]);
410
+ function parseTypeOrInlineRecord(it, inlineContext) {
411
+ if (it.current === "struct" || it.current === "enum") {
412
+ const recordType = it.current;
413
+ it.next();
414
+ const inlineRecord = parseRecord(it, recordType, EMPTY_DOC, inlineContext);
415
+ const type = inlineRecord
416
+ ? {
417
+ kind: "record",
418
+ nameParts: [inlineRecord.name],
419
+ absolute: false,
420
+ }
421
+ : undefined;
422
+ return {
423
+ type: type,
424
+ inlineRecord: inlineRecord ? inlineRecord : undefined,
425
+ };
426
+ }
427
+ else {
428
+ return {
429
+ type: parseType(it),
430
+ inlineRecord: undefined,
431
+ };
432
+ }
433
+ }
366
434
  function parseType(it) {
367
- const match = it.expectThenMove([
435
+ const match = it.expectThenNext([
368
436
  /*0:*/ "[",
369
437
  /*1:*/ TOKEN_IS_IDENTIFIER,
370
438
  /*2:*/ ".",
@@ -397,7 +465,7 @@ function parseType(it) {
397
465
  if (value === undefined) {
398
466
  return undefined;
399
467
  }
400
- if (it.peek() === "?") {
468
+ if (it.current === "?") {
401
469
  it.next();
402
470
  return { kind: "optional", other: value };
403
471
  }
@@ -413,7 +481,7 @@ function parseArrayType(it) {
413
481
  let key = undefined;
414
482
  while (true) {
415
483
  const keyAllowed = !key && item.kind === "record";
416
- const match = it.expectThenMove([
484
+ const match = it.expectThenNext([
417
485
  /*0:*/ keyAllowed ? "|" : null,
418
486
  /*1:*/ "]",
419
487
  ]);
@@ -436,12 +504,12 @@ function parseArrayType(it) {
436
504
  function parseFieldPath(it, pipeToken) {
437
505
  const fieldNames = [];
438
506
  while (true) {
439
- const match = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
507
+ const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
440
508
  if (match.case < 0) {
441
509
  return undefined;
442
510
  }
443
511
  fieldNames.push(match.token);
444
- if (it.peek() === ".") {
512
+ if (it.current === ".") {
445
513
  it.next();
446
514
  }
447
515
  else {
@@ -463,7 +531,7 @@ function parseRecordRef(it, nameOrDot) {
463
531
  const absolute = nameOrDot.text === ".";
464
532
  const nameParts = [];
465
533
  if (nameOrDot.text === ".") {
466
- const match = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
534
+ const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
467
535
  if (match.case < 0) {
468
536
  return undefined;
469
537
  }
@@ -472,9 +540,9 @@ function parseRecordRef(it, nameOrDot) {
472
540
  else {
473
541
  nameParts.push(nameOrDot);
474
542
  }
475
- while (it.peek() === ".") {
543
+ while (it.current === ".") {
476
544
  it.next();
477
- const match = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
545
+ const match = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
478
546
  if (match.case < 0) {
479
547
  return undefined;
480
548
  }
@@ -483,7 +551,7 @@ function parseRecordRef(it, nameOrDot) {
483
551
  return { kind: "record", nameParts: nameParts, absolute: absolute };
484
552
  }
485
553
  function parseUint32(it) {
486
- const match = it.expectThenMove([TOKEN_IS_POSITIVE_INT]);
554
+ const match = it.expectThenNext([TOKEN_IS_POSITIVE_INT]);
487
555
  if (match.case < 0) {
488
556
  return -1;
489
557
  }
@@ -500,8 +568,8 @@ function parseUint32(it) {
500
568
  return -1;
501
569
  }
502
570
  }
503
- // Parses the "removed" declaration.
504
- // Assumes the current token is the token after "removed".
571
+ // Parses the 'removed' declaration.
572
+ // Assumes the current token is the token after 'removed'.
505
573
  function parseRemoved(it, removedToken) {
506
574
  const numbers = [];
507
575
  // The 5 states are:
@@ -521,7 +589,7 @@ function parseRemoved(it, removedToken) {
521
589
  /*2:*/ expect === "?" || expect === "," || expect === ".." ? ";" : null,
522
590
  /*3:*/ expect === ".." ? ".." : null,
523
591
  ];
524
- const match = it.expectThenMove(expected);
592
+ const match = it.expectThenNext(expected);
525
593
  switch (match.case) {
526
594
  case 0: {
527
595
  // A comma.
@@ -583,7 +651,7 @@ function parseRemoved(it, removedToken) {
583
651
  };
584
652
  }
585
653
  function parseImport(it) {
586
- const tokenMatch = it.expectThenMove(["*", TOKEN_IS_IDENTIFIER]);
654
+ const tokenMatch = it.expectThenNext(["*", TOKEN_IS_IDENTIFIER]);
587
655
  switch (tokenMatch.case) {
588
656
  case 0:
589
657
  return parseImportAs(it);
@@ -594,20 +662,20 @@ function parseImport(it) {
594
662
  }
595
663
  }
596
664
  function parseImportAs(it) {
597
- if (it.expectThenMove(["as"]).case < 0)
665
+ if (it.expectThenNext(["as"]).case < 0)
598
666
  return null;
599
- const aliasMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
667
+ const aliasMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
600
668
  if (aliasMatch.case < 0) {
601
669
  return null;
602
670
  }
603
671
  casing.validate(aliasMatch.token, "lower_underscore", it.errors);
604
- if (it.expectThenMove(["from"]).case < 0)
672
+ if (it.expectThenNext(["from"]).case < 0)
605
673
  return null;
606
- const modulePathMatch = it.expectThenMove([TOKEN_IS_STRING_LITERAL]);
674
+ const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
607
675
  if (modulePathMatch.case < 0) {
608
676
  return null;
609
677
  }
610
- it.expectThenMove([";"]);
678
+ it.expectThenNext([";"]);
611
679
  const modulePath = modulePathMatch.token;
612
680
  return {
613
681
  kind: "import-alias",
@@ -617,21 +685,21 @@ function parseImportAs(it) {
617
685
  }
618
686
  function parseImportGivenNames(firstName, it) {
619
687
  const importedNames = [firstName];
620
- while (it.peek() === ",") {
688
+ while (it.current === ",") {
621
689
  it.next();
622
- const nameMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
690
+ const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
623
691
  if (nameMatch.case < 0) {
624
692
  return null;
625
693
  }
626
694
  importedNames.push(nameMatch.token);
627
695
  }
628
- if (it.expectThenMove(["from"]).case < 0)
696
+ if (it.expectThenNext(["from"]).case < 0)
629
697
  return null;
630
- const modulePathMatch = it.expectThenMove([TOKEN_IS_STRING_LITERAL]);
698
+ const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
631
699
  if (modulePathMatch.case < 0) {
632
700
  return null;
633
701
  }
634
- it.expectThenMove([";"]);
702
+ it.expectThenNext([";"]);
635
703
  const modulePath = modulePathMatch.token;
636
704
  return {
637
705
  kind: "import",
@@ -639,34 +707,43 @@ function parseImportGivenNames(firstName, it) {
639
707
  modulePath,
640
708
  };
641
709
  }
642
- function parseMethod(it) {
643
- const nameMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
710
+ function parseMethod(it, doc) {
711
+ const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
644
712
  if (nameMatch.case < 0) {
645
713
  return null;
646
714
  }
647
- casing.validate(nameMatch.token, "UpperCamel", it.errors);
648
- if (it.expectThenMove(["("]).case < 0) {
715
+ const name = nameMatch.token;
716
+ casing.validate(name, "UpperCamel", it.errors);
717
+ if (it.expectThenNext(["("]).case < 0) {
649
718
  return null;
650
719
  }
651
- const requestType = parseType(it);
720
+ const requestTypeOrInlineRecord = parseTypeOrInlineRecord(it, {
721
+ context: "method-request",
722
+ originalName: name,
723
+ });
724
+ const requestType = requestTypeOrInlineRecord.type;
652
725
  if (!requestType) {
653
726
  return null;
654
727
  }
655
- if (it.expectThenMove([")"]).case < 0 || it.expectThenMove([":"]).case < 0) {
728
+ if (it.expectThenNext([")"]).case < 0 || it.expectThenNext([":"]).case < 0) {
656
729
  return null;
657
730
  }
658
- const responseType = parseType(it);
731
+ const responseTypeOrInlineRecord = parseTypeOrInlineRecord(it, {
732
+ context: "method-response",
733
+ originalName: name,
734
+ });
735
+ const responseType = responseTypeOrInlineRecord.type;
659
736
  if (!responseType) {
660
737
  return null;
661
738
  }
662
- const explicitNumber = it.expectThenMove(["=", ";"]).case === 0;
739
+ const explicitNumber = it.expectThenNext(["=", ";"]).case === 0;
663
740
  let number;
664
741
  if (explicitNumber) {
665
742
  number = parseUint32(it);
666
743
  if (number < 0) {
667
744
  return null;
668
745
  }
669
- it.expectThenMove([";"]);
746
+ it.expectThenNext([";"]);
670
747
  }
671
748
  else {
672
749
  const methodName = nameMatch.token.text;
@@ -676,6 +753,7 @@ function parseMethod(it) {
676
753
  return {
677
754
  kind: "method",
678
755
  name: nameMatch.token,
756
+ doc: doc,
679
757
  unresolvedRequestType: requestType,
680
758
  unresolvedResponseType: responseType,
681
759
  // Will be populated at a later stage.
@@ -684,32 +762,35 @@ function parseMethod(it) {
684
762
  responseType: undefined,
685
763
  number: number,
686
764
  hasExplicitNumber: explicitNumber,
765
+ inlineRequestRecord: requestTypeOrInlineRecord.inlineRecord,
766
+ inlineResponseRecord: responseTypeOrInlineRecord.inlineRecord,
687
767
  };
688
768
  }
689
- function parseConstant(it) {
690
- const nameMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
769
+ function parseConstant(it, doc) {
770
+ const nameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
691
771
  if (nameMatch.case < 0) {
692
772
  return null;
693
773
  }
694
774
  casing.validate(nameMatch.token, "UPPER_UNDERSCORE", it.errors);
695
- if (it.expectThenMove([":"]).case < 0) {
775
+ if (it.expectThenNext([":"]).case < 0) {
696
776
  return null;
697
777
  }
698
778
  const type = parseType(it);
699
779
  if (!type) {
700
780
  return null;
701
781
  }
702
- if (it.expectThenMove(["="]).case < 0) {
782
+ if (it.expectThenNext(["="]).case < 0) {
703
783
  return null;
704
784
  }
705
785
  const value = parseValue(it);
706
786
  if (value === null) {
707
787
  return null;
708
788
  }
709
- it.expectThenMove([";"]);
789
+ it.expectThenNext([";"]);
710
790
  return {
711
791
  kind: "constant",
712
792
  name: nameMatch.token,
793
+ doc: doc,
713
794
  unresolvedType: type,
714
795
  type: undefined,
715
796
  value: value,
@@ -727,7 +808,7 @@ function parseValue(it) {
727
808
  /*6:*/ TOKEN_IS_NUMBER,
728
809
  /*7:*/ TOKEN_IS_STRING_LITERAL,
729
810
  ];
730
- const match = it.expectThenMove(expected);
811
+ const match = it.expectThenNext(expected);
731
812
  switch (match.case) {
732
813
  case 0:
733
814
  case 1: {
@@ -771,17 +852,17 @@ function parseObjectValue(it, partial) {
771
852
  const closingToken = partial ? "|}" : "}";
772
853
  const entries = {};
773
854
  while (true) {
774
- if (it.peek() === closingToken) {
855
+ if (it.current === closingToken) {
775
856
  it.next();
776
857
  return entries;
777
858
  }
778
- const fieldNameMatch = it.expectThenMove([TOKEN_IS_IDENTIFIER]);
859
+ const fieldNameMatch = it.expectThenNext([TOKEN_IS_IDENTIFIER]);
779
860
  if (fieldNameMatch.case < 0) {
780
861
  return null;
781
862
  }
782
863
  const fieldNameToken = fieldNameMatch.token;
783
864
  const fieldName = fieldNameMatch.token.text;
784
- if (it.expectThenMove([":"]).case < 0) {
865
+ if (it.expectThenNext([":"]).case < 0) {
785
866
  return null;
786
867
  }
787
868
  const value = parseValue(it);
@@ -798,7 +879,7 @@ function parseObjectValue(it, partial) {
798
879
  name: fieldNameToken,
799
880
  value: value,
800
881
  };
801
- const endMatch = it.expectThenMove([",", closingToken]);
882
+ const endMatch = it.expectThenNext([",", closingToken]);
802
883
  if (endMatch.case < 0) {
803
884
  return null;
804
885
  }
@@ -808,7 +889,7 @@ function parseObjectValue(it, partial) {
808
889
  }
809
890
  }
810
891
  function parseArrayValue(it) {
811
- if (it.peek() === "]") {
892
+ if (it.current === "]") {
812
893
  it.next();
813
894
  return [];
814
895
  }
@@ -819,19 +900,32 @@ function parseArrayValue(it) {
819
900
  return null;
820
901
  }
821
902
  items.push(item);
822
- const match = it.expectThenMove([",", "]"]);
903
+ const match = it.expectThenNext([",", "]"]);
823
904
  if (match.case < 0) {
824
905
  return null;
825
906
  }
826
907
  if (match.token.text === "]") {
827
908
  return items;
828
909
  }
829
- if (it.peek() === "]") {
910
+ if (it.current === "]") {
830
911
  it.next();
831
912
  return items;
832
913
  }
833
914
  }
834
915
  }
916
+ function parseDoc(it) {
917
+ const docComments = [];
918
+ while (it.current.startsWith("///")) {
919
+ docComments.push(it.currentToken);
920
+ it.next();
921
+ }
922
+ const result = parseDocComments(docComments);
923
+ result.errors.forEach((e) => it.errors.push(e));
924
+ return result.result;
925
+ }
926
+ const EMPTY_DOC = {
927
+ pieces: [],
928
+ };
835
929
  class TokenPredicate {
836
930
  }
837
931
  class TokenIsIdentifier extends TokenPredicate {
@@ -883,8 +977,16 @@ class TokenIterator {
883
977
  //
884
978
  // If the current token matches any predicate, i.e. if the index is not -1,
885
979
  // moves to the next token before returning. Otherwise, registers an error.
886
- expectThenMove(expected) {
887
- const token = this.tokens[this.tokenIndex];
980
+ expectThenNext(expected) {
981
+ let token = this.tokens[this.tokenIndex];
982
+ while (token.text.startsWith("///")) {
983
+ this.errors.push({
984
+ token: token,
985
+ message: "Doc comments can only precede declarations",
986
+ });
987
+ ++this.tokenIndex;
988
+ token = this.tokens[this.tokenIndex];
989
+ }
888
990
  for (let i = 0; i < expected.length; ++i) {
889
991
  const e = expected[i];
890
992
  if (e === null) {
@@ -907,7 +1009,7 @@ class TokenIterator {
907
1009
  if (e === null) {
908
1010
  continue;
909
1011
  }
910
- expectedParts.push(e instanceof TokenPredicate ? e.what() : `"${e}"`);
1012
+ expectedParts.push(e instanceof TokenPredicate ? e.what() : `'${e}'`);
911
1013
  }
912
1014
  const expectedMsg = expectedParts.length === 1
913
1015
  ? expectedParts[0]
@@ -921,10 +1023,13 @@ class TokenIterator {
921
1023
  token: token,
922
1024
  };
923
1025
  }
924
- peek() {
925
- return this.tokens[this.tokenIndex].text;
1026
+ get currentToken() {
1027
+ return this.tokens[this.tokenIndex];
926
1028
  }
927
- peekBack() {
1029
+ get current() {
1030
+ return this.currentToken.text;
1031
+ }
1032
+ get previous() {
928
1033
  return this.tokens[this.tokenIndex - 1].text;
929
1034
  }
930
1035
  next() {
@@ -934,18 +1039,6 @@ class TokenIterator {
934
1039
  return this.tokenIndex;
935
1040
  }
936
1041
  }
937
- /** Returns a uint32 hash of the given string. */
938
- export function simpleHash(input) {
939
- // From https://stackoverflow.com/questions/6122571/simple-non-secure-hash-function-for-javascript
940
- let hash = 0;
941
- for (let i = 0; i < input.length; i++) {
942
- const char = input.charCodeAt(i);
943
- hash = (hash << 5) - hash + char;
944
- hash |= 0;
945
- }
946
- // Signed int32 to unsigned int32.
947
- return hash >>> 0;
948
- }
949
1042
  function collectModuleRecords(declarations) {
950
1043
  const result = [];
951
1044
  const collect = (declarations, ancestors) => {