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.
- package/dist/casing.d.ts +1 -4
- package/dist/casing.d.ts.map +1 -1
- package/dist/casing.js +0 -29
- package/dist/casing.js.map +1 -1
- package/dist/casing.test.js +2 -13
- package/dist/casing.test.js.map +1 -1
- package/dist/command_line_parser.d.ts +3 -3
- package/dist/command_line_parser.d.ts.map +1 -1
- package/dist/command_line_parser.js +35 -38
- package/dist/command_line_parser.js.map +1 -1
- package/dist/command_line_parser.test.js +73 -78
- package/dist/command_line_parser.test.js.map +1 -1
- package/dist/compatibility_checker.d.ts +9 -3
- package/dist/compatibility_checker.d.ts.map +1 -1
- package/dist/compatibility_checker.js +17 -4
- package/dist/compatibility_checker.js.map +1 -1
- package/dist/compatibility_checker.test.js +55 -1
- package/dist/compatibility_checker.test.js.map +1 -1
- package/dist/compiler.js +34 -17
- package/dist/compiler.js.map +1 -1
- package/dist/config.d.ts +5 -35
- package/dist/config.d.ts.map +1 -1
- package/dist/definition_finder.d.ts +1 -1
- package/dist/definition_finder.d.ts.map +1 -1
- package/dist/doc_comment_parser.d.ts +3 -0
- package/dist/doc_comment_parser.d.ts.map +1 -0
- package/dist/doc_comment_parser.js +219 -0
- package/dist/doc_comment_parser.js.map +1 -0
- package/dist/doc_comment_parser.test.d.ts +2 -0
- package/dist/doc_comment_parser.test.d.ts.map +1 -0
- package/dist/doc_comment_parser.test.js +494 -0
- package/dist/doc_comment_parser.test.js.map +1 -0
- package/dist/error_renderer.d.ts +1 -1
- package/dist/error_renderer.d.ts.map +1 -1
- package/dist/error_renderer.js +84 -65
- package/dist/error_renderer.js.map +1 -1
- package/dist/formatter.d.ts +15 -2
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +191 -234
- package/dist/formatter.js.map +1 -1
- package/dist/formatter.test.js +322 -88
- package/dist/formatter.test.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/language_server.js +1 -1
- package/dist/language_server.js.map +1 -1
- package/dist/literals.d.ts +1 -2
- package/dist/literals.d.ts.map +1 -1
- package/dist/literals.js +1 -12
- package/dist/literals.js.map +1 -1
- package/dist/literals.test.js +1 -4
- package/dist/literals.test.js.map +1 -1
- package/dist/module_set.d.ts +3 -7
- package/dist/module_set.d.ts.map +1 -1
- package/dist/module_set.js +205 -51
- package/dist/module_set.js.map +1 -1
- package/dist/module_set.test.js +595 -28
- package/dist/module_set.test.js.map +1 -1
- package/dist/parser.d.ts +3 -4
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +185 -92
- package/dist/parser.js.map +1 -1
- package/dist/parser.test.js +243 -15
- package/dist/parser.test.js.map +1 -1
- package/dist/project_initializer.d.ts +2 -0
- package/dist/project_initializer.d.ts.map +1 -0
- package/dist/project_initializer.js +30 -0
- package/dist/project_initializer.js.map +1 -0
- package/dist/snapshotter.d.ts +3 -0
- package/dist/snapshotter.d.ts.map +1 -1
- package/dist/snapshotter.js +43 -6
- package/dist/snapshotter.js.map +1 -1
- package/dist/tokenizer.d.ts +8 -2
- package/dist/tokenizer.d.ts.map +1 -1
- package/dist/tokenizer.js +26 -20
- package/dist/tokenizer.js.map +1 -1
- package/dist/tokenizer.test.js +285 -269
- package/dist/tokenizer.test.js.map +1 -1
- package/package.json +7 -5
- package/src/casing.ts +1 -36
- package/src/command_line_parser.ts +42 -48
- package/src/compatibility_checker.ts +29 -7
- package/src/compiler.ts +35 -18
- package/src/definition_finder.ts +1 -1
- package/src/doc_comment_parser.ts +246 -0
- package/src/error_renderer.ts +90 -66
- package/src/formatter.ts +249 -238
- package/src/index.ts +0 -6
- package/src/language_server.ts +8 -8
- package/src/literals.ts +5 -14
- package/src/module_set.ts +259 -79
- package/src/parser.ts +213 -98
- package/src/project_initializer.ts +39 -0
- package/src/snapshotter.ts +46 -5
- package/src/tokenizer.ts +47 -25
- package/dist/encoding.d.ts +0 -2
- package/dist/encoding.d.ts.map +0 -1
- package/dist/encoding.js +0 -38
- package/dist/encoding.js.map +0 -1
- package/dist/encoding.test.d.ts +0 -2
- package/dist/encoding.test.d.ts.map +0 -1
- package/dist/encoding.test.js +0 -23
- package/dist/encoding.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -14
- package/dist/index.test.js.map +0 -1
- package/dist/types.d.ts +0 -375
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/src/encoding.ts +0 -32
- 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(
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
87
|
+
(it.current !== "" && !isLastToken(it.previous))) {
|
|
77
88
|
let nestedLevel = 0;
|
|
78
89
|
while (true) {
|
|
79
|
-
const token = it.
|
|
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.
|
|
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
|
|
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
|
-
|
|
274
|
-
if (
|
|
275
|
-
|
|
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.
|
|
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.
|
|
317
|
+
if (it.expectThenNext([")"]).case < 0) {
|
|
286
318
|
return null;
|
|
287
319
|
}
|
|
288
320
|
}
|
|
289
|
-
if (it.
|
|
321
|
+
if (it.expectThenNext(["{"]).case < 0) {
|
|
290
322
|
return null;
|
|
291
323
|
}
|
|
292
324
|
const declarations = parseDeclarations(it, recordType);
|
|
293
|
-
it.
|
|
294
|
-
const builder = new RecordBuilder(
|
|
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
|
|
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.
|
|
349
|
+
const match = it.expectThenNext(expected);
|
|
314
350
|
switch (match.case) {
|
|
315
351
|
case 0: {
|
|
316
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
543
|
+
while (it.current === ".") {
|
|
476
544
|
it.next();
|
|
477
|
-
const match = it.
|
|
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.
|
|
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
|
|
504
|
-
// Assumes the current token is the token after
|
|
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.
|
|
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.
|
|
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.
|
|
665
|
+
if (it.expectThenNext(["as"]).case < 0)
|
|
598
666
|
return null;
|
|
599
|
-
const aliasMatch = it.
|
|
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.
|
|
672
|
+
if (it.expectThenNext(["from"]).case < 0)
|
|
605
673
|
return null;
|
|
606
|
-
const modulePathMatch = it.
|
|
674
|
+
const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
|
|
607
675
|
if (modulePathMatch.case < 0) {
|
|
608
676
|
return null;
|
|
609
677
|
}
|
|
610
|
-
it.
|
|
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.
|
|
688
|
+
while (it.current === ",") {
|
|
621
689
|
it.next();
|
|
622
|
-
const nameMatch = it.
|
|
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.
|
|
696
|
+
if (it.expectThenNext(["from"]).case < 0)
|
|
629
697
|
return null;
|
|
630
|
-
const modulePathMatch = it.
|
|
698
|
+
const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
|
|
631
699
|
if (modulePathMatch.case < 0) {
|
|
632
700
|
return null;
|
|
633
701
|
}
|
|
634
|
-
it.
|
|
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.
|
|
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
|
-
|
|
648
|
-
|
|
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
|
|
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.
|
|
728
|
+
if (it.expectThenNext([")"]).case < 0 || it.expectThenNext([":"]).case < 0) {
|
|
656
729
|
return null;
|
|
657
730
|
}
|
|
658
|
-
const
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
855
|
+
if (it.current === closingToken) {
|
|
775
856
|
it.next();
|
|
776
857
|
return entries;
|
|
777
858
|
}
|
|
778
|
-
const fieldNameMatch = it.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
887
|
-
|
|
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() : `
|
|
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
|
-
|
|
925
|
-
return this.tokens[this.tokenIndex]
|
|
1026
|
+
get currentToken() {
|
|
1027
|
+
return this.tokens[this.tokenIndex];
|
|
926
1028
|
}
|
|
927
|
-
|
|
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) => {
|