cddl 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nvmrc +1 -0
- package/README.md +5 -5
- package/bin/cddl.js +3 -1
- package/build/ast.d.ts +14 -14
- package/build/ast.d.ts.map +1 -1
- package/build/ast.js +2 -5
- package/build/cli/commands/repl.d.ts +3 -3
- package/build/cli/commands/repl.d.ts.map +1 -1
- package/build/cli/commands/repl.js +13 -20
- package/build/cli/commands/validate.d.ts +4 -4
- package/build/cli/commands/validate.d.ts.map +1 -1
- package/build/cli/commands/validate.js +14 -19
- package/build/cli/constants.d.ts +1 -1
- package/build/cli/constants.js +1 -4
- package/build/cli/index.d.ts +7 -3
- package/build/cli/index.d.ts.map +1 -1
- package/build/cli/index.js +12 -12
- package/build/constants.d.ts +4 -0
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +8 -6
- package/build/index.d.ts +6 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +16 -17
- package/build/lexer.d.ts +7 -1
- package/build/lexer.d.ts.map +1 -1
- package/build/lexer.js +81 -50
- package/build/parser.d.ts +6 -4
- package/build/parser.d.ts.map +1 -1
- package/build/parser.js +126 -79
- package/build/tokens.d.ts +2 -2
- package/build/tokens.d.ts.map +1 -1
- package/build/tokens.js +2 -5
- package/build/transform/ts.d.ts +3 -0
- package/build/transform/ts.d.ts.map +1 -0
- package/build/transform/ts.js +113 -0
- package/build/types.d.ts +5 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +1 -0
- package/build/utils.d.ts +1 -1
- package/build/utils.d.ts.map +1 -1
- package/build/utils.js +11 -18
- package/examples/webdriver/local.cddl +846 -0
- package/examples/webdriver/remote.cddl +830 -0
- package/package.json +30 -26
- package/vitest.config.ts +16 -0
package/build/parser.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const NIL_TOKEN = { Type:
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import Lexer from './lexer.js';
|
|
3
|
+
import { Tokens } from './tokens.js';
|
|
4
|
+
import { PREDEFINED_IDENTIFIER, BOOLEAN_LITERALS } from './constants.js';
|
|
5
|
+
import { parseNumberValue } from './utils.js';
|
|
6
|
+
import { Type } from './ast.js';
|
|
7
|
+
const NIL_TOKEN = { Type: Tokens.ILLEGAL, Literal: '' };
|
|
8
8
|
const DEFAULT_OCCURRENCE = { n: 1, m: 1 }; // exactly one time
|
|
9
|
-
class Parser {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
export default class Parser {
|
|
10
|
+
#filePath;
|
|
11
|
+
l;
|
|
12
|
+
curToken = NIL_TOKEN;
|
|
13
|
+
peekToken = NIL_TOKEN;
|
|
14
|
+
constructor(filePath) {
|
|
15
|
+
this.#filePath = filePath;
|
|
16
|
+
this.l = new Lexer(fs.readFileSync(filePath, 'utf-8'));
|
|
14
17
|
this.nextToken();
|
|
15
18
|
this.nextToken();
|
|
16
19
|
}
|
|
@@ -26,19 +29,19 @@ class Parser {
|
|
|
26
29
|
* groupName /=
|
|
27
30
|
* groupName //=
|
|
28
31
|
*/
|
|
29
|
-
if (this.curToken.Type !==
|
|
30
|
-
throw
|
|
32
|
+
if (this.curToken.Type !== Tokens.IDENT || !(this.peekToken.Type === Tokens.ASSIGN || this.peekToken.Type === Tokens.SLASH)) {
|
|
33
|
+
throw this.parserError(`group identifier expected, received "${JSON.stringify(this.curToken)}"`);
|
|
31
34
|
}
|
|
32
35
|
let isChoiceAddition = false;
|
|
33
36
|
const groupName = this.curToken.Literal;
|
|
34
37
|
this.nextToken(); // eat group identifier
|
|
35
38
|
// @ts-ignore
|
|
36
|
-
if (this.curToken.Type ===
|
|
39
|
+
if (this.curToken.Type === Tokens.SLASH) {
|
|
37
40
|
isChoiceAddition = true;
|
|
38
41
|
this.nextToken(); // eat `/`
|
|
39
42
|
}
|
|
40
43
|
// @ts-ignore
|
|
41
|
-
if (this.curToken.Type ===
|
|
44
|
+
if (this.curToken.Type === Tokens.SLASH) {
|
|
42
45
|
this.nextToken(); // eat `/`
|
|
43
46
|
}
|
|
44
47
|
this.nextToken(); // eat `=`
|
|
@@ -50,7 +53,10 @@ class Parser {
|
|
|
50
53
|
const closingTokens = this.openSegment();
|
|
51
54
|
/**
|
|
52
55
|
* if no group segment was opened we have a variable assignment
|
|
53
|
-
* and can return immediatelly
|
|
56
|
+
* and can return immediatelly, e.g.
|
|
57
|
+
*
|
|
58
|
+
* attire = "bow tie" / "necktie" / "Internet attire"
|
|
59
|
+
*
|
|
54
60
|
*/
|
|
55
61
|
if (closingTokens.length === 0) {
|
|
56
62
|
if (groupName) {
|
|
@@ -64,6 +70,43 @@ class Parser {
|
|
|
64
70
|
}
|
|
65
71
|
return this.parsePropertyTypes();
|
|
66
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* type or group choices can be wrapped within `(` and `)`, e.g.
|
|
75
|
+
*
|
|
76
|
+
* attireBlock = (
|
|
77
|
+
* "bow tie" /
|
|
78
|
+
* "necktie" /
|
|
79
|
+
* "Internet attire"
|
|
80
|
+
* )
|
|
81
|
+
* attireGroup = (
|
|
82
|
+
* attire //
|
|
83
|
+
* attireBlock
|
|
84
|
+
* )
|
|
85
|
+
*/
|
|
86
|
+
if (closingTokens.length && this.peekToken.Type === Tokens.SLASH) {
|
|
87
|
+
const propertyType = [];
|
|
88
|
+
while (!closingTokens.includes(this.curToken.Type)) {
|
|
89
|
+
propertyType.push(...this.parsePropertyTypes());
|
|
90
|
+
if (this.curToken.Type === Tokens.RPAREN) {
|
|
91
|
+
this.nextToken();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
this.nextToken();
|
|
95
|
+
if (this.curToken.Type === Tokens.SLASH) {
|
|
96
|
+
this.nextToken();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (groupName) {
|
|
100
|
+
const variable = {
|
|
101
|
+
Type: 'variable',
|
|
102
|
+
Name: groupName,
|
|
103
|
+
IsChoiceAddition: isChoiceAddition,
|
|
104
|
+
PropertyType: propertyType
|
|
105
|
+
};
|
|
106
|
+
return variable;
|
|
107
|
+
}
|
|
108
|
+
return propertyType;
|
|
109
|
+
}
|
|
67
110
|
while (!closingTokens.includes(this.curToken.Type)) {
|
|
68
111
|
const propertyType = [];
|
|
69
112
|
let isUnwrapped = false;
|
|
@@ -74,7 +117,7 @@ class Parser {
|
|
|
74
117
|
/**
|
|
75
118
|
* check if variable name is unwrapped
|
|
76
119
|
*/
|
|
77
|
-
if (this.curToken.Literal ===
|
|
120
|
+
if (this.curToken.Literal === Tokens.TILDE) {
|
|
78
121
|
isUnwrapped = true;
|
|
79
122
|
this.nextToken(); // eat ~
|
|
80
123
|
}
|
|
@@ -82,13 +125,13 @@ class Parser {
|
|
|
82
125
|
/**
|
|
83
126
|
* if `,` is found we have a group reference and jump to the next line
|
|
84
127
|
*/
|
|
85
|
-
if (this.curToken.Type ===
|
|
128
|
+
if (this.curToken.Type === Tokens.COMMA || closingTokens.includes(this.curToken.Type)) {
|
|
86
129
|
const tokenType = this.curToken.Type;
|
|
87
130
|
let parsedComments = false;
|
|
88
131
|
/**
|
|
89
132
|
* check if line has a comment
|
|
90
133
|
*/
|
|
91
|
-
if (this.curToken.Type ===
|
|
134
|
+
if (this.curToken.Type === Tokens.COMMA && this.peekToken.Type === Tokens.COMMENT) {
|
|
92
135
|
this.nextToken();
|
|
93
136
|
comment = this.parseComment();
|
|
94
137
|
parsedComments = true;
|
|
@@ -97,7 +140,7 @@ class Parser {
|
|
|
97
140
|
HasCut: hasCut,
|
|
98
141
|
Occurrence: occurrence,
|
|
99
142
|
Name: '',
|
|
100
|
-
Type:
|
|
143
|
+
Type: PREDEFINED_IDENTIFIER.includes(propertyName)
|
|
101
144
|
? propertyName
|
|
102
145
|
: [{
|
|
103
146
|
Type: 'group',
|
|
@@ -112,7 +155,7 @@ class Parser {
|
|
|
112
155
|
/**
|
|
113
156
|
* only continue if next token contains a comma
|
|
114
157
|
*/
|
|
115
|
-
if (tokenType ===
|
|
158
|
+
if (tokenType === Tokens.COMMA) {
|
|
116
159
|
continue;
|
|
117
160
|
}
|
|
118
161
|
/**
|
|
@@ -125,16 +168,16 @@ class Parser {
|
|
|
125
168
|
* - `? "optional-key" ^ => int,`
|
|
126
169
|
* - `? optional-key: int,` - since the colon shortcut includes cuts
|
|
127
170
|
*/
|
|
128
|
-
if (this.curToken.Type ===
|
|
171
|
+
if (this.curToken.Type === Tokens.CARET || this.curToken.Type === Tokens.COLON) {
|
|
129
172
|
hasCut = true;
|
|
130
|
-
if (this.curToken.Type ===
|
|
173
|
+
if (this.curToken.Type === Tokens.CARET) {
|
|
131
174
|
this.nextToken(); // eat ^
|
|
132
175
|
}
|
|
133
176
|
}
|
|
134
177
|
/**
|
|
135
|
-
* check if we have a choice instead of an assignment
|
|
178
|
+
* check if we have a group choice instead of an assignment
|
|
136
179
|
*/
|
|
137
|
-
if (this.curToken.Type ===
|
|
180
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
138
181
|
const prop = {
|
|
139
182
|
HasCut: hasCut,
|
|
140
183
|
Occurrence: occurrence,
|
|
@@ -167,7 +210,7 @@ class Parser {
|
|
|
167
210
|
* else if no colon was found, throw
|
|
168
211
|
*/
|
|
169
212
|
if (!this.isPropertyValueSeparator()) {
|
|
170
|
-
throw
|
|
213
|
+
throw this.parserError('Expected ":" or "=>"');
|
|
171
214
|
}
|
|
172
215
|
this.nextToken(); // eat :
|
|
173
216
|
/**
|
|
@@ -187,7 +230,7 @@ class Parser {
|
|
|
187
230
|
* advance comma
|
|
188
231
|
*/
|
|
189
232
|
let flipIsChoice = false;
|
|
190
|
-
if (this.curToken.Type ===
|
|
233
|
+
if (this.curToken.Type === Tokens.COMMA) {
|
|
191
234
|
/**
|
|
192
235
|
* if we are in a choice, we leave it here
|
|
193
236
|
*/
|
|
@@ -235,7 +278,7 @@ class Parser {
|
|
|
235
278
|
/**
|
|
236
279
|
* if last closing token is "]" we have an array
|
|
237
280
|
*/
|
|
238
|
-
if (closingTokens[closingTokens.length - 1] ===
|
|
281
|
+
if (closingTokens[closingTokens.length - 1] === Tokens.RBRACK) {
|
|
239
282
|
return {
|
|
240
283
|
Type: 'array',
|
|
241
284
|
Name: groupName || '',
|
|
@@ -253,10 +296,10 @@ class Parser {
|
|
|
253
296
|
};
|
|
254
297
|
}
|
|
255
298
|
isPropertyValueSeparator() {
|
|
256
|
-
if (this.curToken.Type ===
|
|
299
|
+
if (this.curToken.Type === Tokens.COLON) {
|
|
257
300
|
return true;
|
|
258
301
|
}
|
|
259
|
-
if (this.curToken.Type ===
|
|
302
|
+
if (this.curToken.Type === Tokens.ASSIGN && this.peekToken.Type === Tokens.GT) {
|
|
260
303
|
this.nextToken(); // eat <
|
|
261
304
|
return true;
|
|
262
305
|
}
|
|
@@ -268,21 +311,21 @@ class Parser {
|
|
|
268
311
|
* @returns {String[]} closing tokens for group (either `}`, `)` or both)
|
|
269
312
|
*/
|
|
270
313
|
openSegment() {
|
|
271
|
-
if (this.curToken.Type ===
|
|
314
|
+
if (this.curToken.Type === Tokens.LBRACE) {
|
|
272
315
|
this.nextToken();
|
|
273
|
-
if (this.peekToken.Type ===
|
|
316
|
+
if (this.peekToken.Type === Tokens.LPAREN) {
|
|
274
317
|
this.nextToken();
|
|
275
|
-
return [
|
|
318
|
+
return [Tokens.RPAREN, Tokens.RBRACE];
|
|
276
319
|
}
|
|
277
|
-
return [
|
|
320
|
+
return [Tokens.RBRACE];
|
|
278
321
|
}
|
|
279
|
-
else if (this.curToken.Type ===
|
|
322
|
+
else if (this.curToken.Type === Tokens.LPAREN) {
|
|
280
323
|
this.nextToken();
|
|
281
|
-
return [
|
|
324
|
+
return [Tokens.RPAREN];
|
|
282
325
|
}
|
|
283
|
-
else if (this.curToken.Type ===
|
|
326
|
+
else if (this.curToken.Type === Tokens.LBRACK) {
|
|
284
327
|
this.nextToken();
|
|
285
|
-
return [
|
|
328
|
+
return [Tokens.RBRACK];
|
|
286
329
|
}
|
|
287
330
|
return [];
|
|
288
331
|
}
|
|
@@ -290,12 +333,12 @@ class Parser {
|
|
|
290
333
|
/**
|
|
291
334
|
* property name without quotes
|
|
292
335
|
*/
|
|
293
|
-
if (this.curToken.Type ===
|
|
336
|
+
if (this.curToken.Type === Tokens.IDENT || this.curToken.Type === Tokens.STRING) {
|
|
294
337
|
const name = this.curToken.Literal;
|
|
295
338
|
this.nextToken();
|
|
296
339
|
return name;
|
|
297
340
|
}
|
|
298
|
-
throw
|
|
341
|
+
throw this.parserError(`Expected property name, received ${this.curToken.Type}(${this.curToken.Literal}), ${this.peekToken.Type}(${this.peekToken.Literal})`);
|
|
299
342
|
}
|
|
300
343
|
parsePropertyType() {
|
|
301
344
|
let type;
|
|
@@ -303,57 +346,57 @@ class Parser {
|
|
|
303
346
|
/**
|
|
304
347
|
* check if variable name is unwrapped
|
|
305
348
|
*/
|
|
306
|
-
if (this.curToken.Literal ===
|
|
349
|
+
if (this.curToken.Literal === Tokens.TILDE) {
|
|
307
350
|
isUnwrapped = true;
|
|
308
351
|
this.nextToken(); // eat ~
|
|
309
352
|
}
|
|
310
353
|
switch (this.curToken.Literal) {
|
|
311
|
-
case
|
|
312
|
-
case
|
|
313
|
-
case
|
|
314
|
-
case
|
|
315
|
-
case
|
|
316
|
-
case
|
|
317
|
-
case
|
|
318
|
-
case
|
|
319
|
-
case
|
|
320
|
-
case
|
|
321
|
-
case
|
|
322
|
-
case
|
|
354
|
+
case Type.BOOL:
|
|
355
|
+
case Type.INT:
|
|
356
|
+
case Type.UINT:
|
|
357
|
+
case Type.NINT:
|
|
358
|
+
case Type.FLOAT:
|
|
359
|
+
case Type.FLOAT16:
|
|
360
|
+
case Type.FLOAT32:
|
|
361
|
+
case Type.FLOAT64:
|
|
362
|
+
case Type.BSTR:
|
|
363
|
+
case Type.BYTES:
|
|
364
|
+
case Type.TSTR:
|
|
365
|
+
case Type.TEXT:
|
|
323
366
|
type = this.curToken.Literal;
|
|
324
367
|
break;
|
|
325
368
|
default: {
|
|
326
|
-
if (
|
|
369
|
+
if (BOOLEAN_LITERALS.includes(this.curToken.Literal)) {
|
|
327
370
|
type = {
|
|
328
371
|
Type: 'literal',
|
|
329
372
|
Value: this.curToken.Literal === 'true',
|
|
330
373
|
Unwrapped: isUnwrapped
|
|
331
374
|
};
|
|
332
375
|
}
|
|
333
|
-
else if (this.curToken.Type ===
|
|
376
|
+
else if (this.curToken.Type === Tokens.IDENT) {
|
|
334
377
|
type = {
|
|
335
378
|
Type: 'group',
|
|
336
379
|
Value: this.curToken.Literal,
|
|
337
380
|
Unwrapped: isUnwrapped
|
|
338
381
|
};
|
|
339
382
|
}
|
|
340
|
-
else if (this.curToken.Type ===
|
|
383
|
+
else if (this.curToken.Type === Tokens.STRING) {
|
|
341
384
|
type = {
|
|
342
385
|
Type: 'literal',
|
|
343
386
|
Value: this.curToken.Literal,
|
|
344
387
|
Unwrapped: isUnwrapped
|
|
345
388
|
};
|
|
346
389
|
}
|
|
347
|
-
else if (this.curToken.Type ===
|
|
390
|
+
else if (this.curToken.Type === Tokens.NUMBER || this.curToken.Type === Tokens.FLOAT) {
|
|
348
391
|
type = {
|
|
349
392
|
Type: 'literal',
|
|
350
|
-
Value:
|
|
393
|
+
Value: parseNumberValue(this.curToken),
|
|
351
394
|
Unwrapped: isUnwrapped
|
|
352
395
|
};
|
|
353
396
|
}
|
|
354
|
-
else if (this.curToken.Type ===
|
|
397
|
+
else if (this.curToken.Type === Tokens.HASH) {
|
|
355
398
|
this.nextToken();
|
|
356
|
-
const n =
|
|
399
|
+
const n = parseNumberValue(this.curToken);
|
|
357
400
|
this.nextToken(); // eat numeric value
|
|
358
401
|
this.nextToken(); // eat (
|
|
359
402
|
const t = this.parsePropertyType();
|
|
@@ -368,27 +411,27 @@ class Parser {
|
|
|
368
411
|
};
|
|
369
412
|
}
|
|
370
413
|
else {
|
|
371
|
-
throw
|
|
414
|
+
throw this.parserError(`Invalid property type "${this.curToken.Literal}"`);
|
|
372
415
|
}
|
|
373
416
|
}
|
|
374
417
|
}
|
|
375
418
|
/**
|
|
376
419
|
* check if type continue as a range
|
|
377
420
|
*/
|
|
378
|
-
if (this.peekToken.Type ===
|
|
421
|
+
if (this.peekToken.Type === Tokens.DOT &&
|
|
379
422
|
this.nextToken() &&
|
|
380
|
-
this.peekToken.Type ===
|
|
423
|
+
this.peekToken.Type === Tokens.DOT) {
|
|
381
424
|
this.nextToken();
|
|
382
425
|
let Inclusive = true;
|
|
383
426
|
/**
|
|
384
427
|
* check if range excludes upper bound
|
|
385
428
|
*/
|
|
386
|
-
if (this.peekToken.Type ===
|
|
429
|
+
if (this.peekToken.Type === Tokens.DOT) {
|
|
387
430
|
Inclusive = false;
|
|
388
431
|
this.nextToken();
|
|
389
432
|
}
|
|
390
433
|
this.nextToken();
|
|
391
|
-
const Min = typeof type === 'string'
|
|
434
|
+
const Min = typeof type === 'string' || typeof type.Value === 'number'
|
|
392
435
|
? type
|
|
393
436
|
: type.Value;
|
|
394
437
|
type = {
|
|
@@ -414,13 +457,13 @@ class Parser {
|
|
|
414
457
|
* city // lala: tstr / bool // per-pickup: true,
|
|
415
458
|
* )
|
|
416
459
|
*/
|
|
417
|
-
if (this.curToken.Type ===
|
|
460
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
418
461
|
return propertyTypes;
|
|
419
462
|
}
|
|
420
463
|
/**
|
|
421
464
|
* capture more if available (e.g. `tstr / float / boolean`)
|
|
422
465
|
*/
|
|
423
|
-
while (this.curToken.Type ===
|
|
466
|
+
while (this.curToken.Type === Tokens.SLASH) {
|
|
424
467
|
this.nextToken(); // eat `/`
|
|
425
468
|
propertyTypes.push(this.parsePropertyType());
|
|
426
469
|
this.nextToken();
|
|
@@ -431,7 +474,7 @@ class Parser {
|
|
|
431
474
|
* city // lala: tstr / bool // per-pickup: true,
|
|
432
475
|
* )
|
|
433
476
|
*/
|
|
434
|
-
if (this.curToken.Type ===
|
|
477
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
435
478
|
break;
|
|
436
479
|
}
|
|
437
480
|
}
|
|
@@ -453,13 +496,13 @@ class Parser {
|
|
|
453
496
|
* + bedroom: size,
|
|
454
497
|
* ```
|
|
455
498
|
*/
|
|
456
|
-
if (this.curToken.Type ===
|
|
457
|
-
const n = this.curToken.Type ===
|
|
499
|
+
if (this.curToken.Type === Tokens.QUEST || this.curToken.Type === Tokens.ASTERISK || this.curToken.Type === Tokens.PLUS) {
|
|
500
|
+
const n = this.curToken.Type === Tokens.PLUS ? 1 : 0;
|
|
458
501
|
let m = Infinity;
|
|
459
502
|
/**
|
|
460
503
|
* check if there is a max definition
|
|
461
504
|
*/
|
|
462
|
-
if (this.peekToken.Type ===
|
|
505
|
+
if (this.peekToken.Type === Tokens.NUMBER) {
|
|
463
506
|
m = parseInt(this.peekToken.Literal, 10);
|
|
464
507
|
this.nextToken();
|
|
465
508
|
}
|
|
@@ -472,8 +515,8 @@ class Parser {
|
|
|
472
515
|
* ```
|
|
473
516
|
*/
|
|
474
517
|
}
|
|
475
|
-
else if (this.curToken.Type ===
|
|
476
|
-
this.peekToken.Type ===
|
|
518
|
+
else if (this.curToken.Type === Tokens.NUMBER &&
|
|
519
|
+
this.peekToken.Type === Tokens.ASTERISK) {
|
|
477
520
|
const n = parseInt(this.curToken.Literal, 10);
|
|
478
521
|
let m = Infinity;
|
|
479
522
|
this.nextToken(); // eat "n"
|
|
@@ -481,7 +524,7 @@ class Parser {
|
|
|
481
524
|
/**
|
|
482
525
|
* check if there is a max definition
|
|
483
526
|
*/
|
|
484
|
-
if (this.curToken.Type ===
|
|
527
|
+
if (this.curToken.Type === Tokens.NUMBER) {
|
|
485
528
|
m = parseInt(this.curToken.Literal, 10);
|
|
486
529
|
this.nextToken();
|
|
487
530
|
}
|
|
@@ -494,7 +537,7 @@ class Parser {
|
|
|
494
537
|
*/
|
|
495
538
|
parseComment() {
|
|
496
539
|
let comment = '';
|
|
497
|
-
if (this.curToken.Type ===
|
|
540
|
+
if (this.curToken.Type === Tokens.COMMENT) {
|
|
498
541
|
comment = this.curToken.Literal.slice(2);
|
|
499
542
|
this.nextToken();
|
|
500
543
|
}
|
|
@@ -502,8 +545,8 @@ class Parser {
|
|
|
502
545
|
}
|
|
503
546
|
parse() {
|
|
504
547
|
const definition = [];
|
|
505
|
-
while (this.curToken.Type !==
|
|
506
|
-
if (this.curToken.Type ===
|
|
548
|
+
while (this.curToken.Type !== Tokens.EOF) {
|
|
549
|
+
if (this.curToken.Type === Tokens.COMMENT) {
|
|
507
550
|
const comment = {
|
|
508
551
|
Type: 'comment',
|
|
509
552
|
Content: this.curToken.Literal.slice(1).trim()
|
|
@@ -519,5 +562,9 @@ class Parser {
|
|
|
519
562
|
}
|
|
520
563
|
return definition;
|
|
521
564
|
}
|
|
565
|
+
parserError(message) {
|
|
566
|
+
const location = this.l.getLocation();
|
|
567
|
+
const locInfo = this.l.getLocationInfo();
|
|
568
|
+
return new Error(`${this.#filePath.replace(process.cwd(), '')}:${location.line + 1}:${location.position} - error: ${message}\n\n${locInfo}`);
|
|
569
|
+
}
|
|
522
570
|
}
|
|
523
|
-
exports.default = Parser;
|
package/build/tokens.d.ts
CHANGED
package/build/tokens.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAE9B,MAAM,MAAM,KAAK,GAAG;IAChB,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,oBAAY,MAAM;IACd,OAAO,YAAY;IACnB,GAAG,QAAQ;IACX,EAAE,OAAO;IACT,KAAK,MAAM;IACX,UAAU,MAAM;IAChB,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,IAAI,MAAM;IACV,KAAK,MAAM;IAGX,KAAK,UAAU;IACf,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,KAAK,UAAU;IAGf,MAAM,MAAM;IACZ,IAAI,MAAM;IACV,KAAK,MAAM;IACX,KAAK,MAAM;IACX,KAAK,MAAM;IACX,QAAQ,MAAM;IAGd,KAAK,MAAM;IACX,GAAG,MAAM;IACT,KAAK,MAAM;IACX,SAAS,MAAM;IACf,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,EAAE,MAAM;IACR,EAAE,MAAM;IACR,IAAI,OAAM;CACb"}
|
package/build/tokens.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Tokens = void 0;
|
|
4
|
-
var Tokens;
|
|
1
|
+
export var Tokens;
|
|
5
2
|
(function (Tokens) {
|
|
6
3
|
Tokens["ILLEGAL"] = "ILLEGAL";
|
|
7
4
|
Tokens["EOF"] = "EOF";
|
|
@@ -41,4 +38,4 @@ var Tokens;
|
|
|
41
38
|
Tokens["LT"] = "<";
|
|
42
39
|
Tokens["GT"] = ">";
|
|
43
40
|
Tokens["QUOT"] = "\"";
|
|
44
|
-
})(Tokens
|
|
41
|
+
})(Tokens || (Tokens = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ts.d.ts","sourceRoot":"","sources":["../../src/transform/ts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA6C,MAAM,QAAQ,CAAA;AAanF,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,UAoBnD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import camelcase from 'camelcase';
|
|
2
|
+
import { parse, print, types } from 'recast';
|
|
3
|
+
import typescriptParser from 'recast/parsers/typescript.js';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import pkg from '../../package.json' assert { type: 'json' };
|
|
6
|
+
const b = types.builders;
|
|
7
|
+
const comments = [];
|
|
8
|
+
const NATIVE_TYPES = {
|
|
9
|
+
number: 'number',
|
|
10
|
+
uint: 'Uint32Array',
|
|
11
|
+
bool: 'boolean',
|
|
12
|
+
str: 'string',
|
|
13
|
+
text: 'string',
|
|
14
|
+
tstr: 'string'
|
|
15
|
+
};
|
|
16
|
+
export function transform(assignments) {
|
|
17
|
+
let ast = parse(`// compiled with https://www.npmjs.com/package/cddl v${pkg.version}`, {
|
|
18
|
+
parser: typescriptParser,
|
|
19
|
+
sourceFileName: 'cddl2Ts.ts',
|
|
20
|
+
sourceRoot: process.cwd()
|
|
21
|
+
});
|
|
22
|
+
for (const assignment of assignments) {
|
|
23
|
+
const statement = parseAssignment(ast, assignment);
|
|
24
|
+
if (!statement) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
ast.program.body.push(statement);
|
|
28
|
+
}
|
|
29
|
+
ast.program.comments = comments.map((c) => b.commentLine(c, false, false));
|
|
30
|
+
return print(ast).code;
|
|
31
|
+
}
|
|
32
|
+
function parseAssignment(ast, assignment) {
|
|
33
|
+
if (assignment.Type === 'comment') {
|
|
34
|
+
comments.push(assignment.Content);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (assignment.Type === 'variable') {
|
|
38
|
+
const propType = Array.isArray(assignment.PropertyType)
|
|
39
|
+
? assignment.PropertyType
|
|
40
|
+
: [assignment.PropertyType];
|
|
41
|
+
const id = b.identifier(camelcase(assignment.Name, { pascalCase: true }));
|
|
42
|
+
let typeParameters;
|
|
43
|
+
// @ts-expect-error e.g. "js-int = -9007199254740991..9007199254740991"
|
|
44
|
+
if (propType.length === 1 && propType[0].Type === 'range') {
|
|
45
|
+
typeParameters = b.tsNumberKeyword();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
typeParameters = b.tsUnionType(propType.map(parsePropertyType));
|
|
49
|
+
}
|
|
50
|
+
const expr = b.tsTypeAliasDeclaration(id, typeParameters);
|
|
51
|
+
expr.comments = comments.map((c) => b.commentLine(c, true));
|
|
52
|
+
return expr;
|
|
53
|
+
}
|
|
54
|
+
if (assignment.Type === 'group') {
|
|
55
|
+
const id = b.identifier(camelcase(assignment.Name, { pascalCase: true }));
|
|
56
|
+
const objectType = parseObjectType(assignment.Properties);
|
|
57
|
+
const expr = b.interfaceDeclaration(id, objectType, []);
|
|
58
|
+
return expr;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function parsePropertyType(propType) {
|
|
62
|
+
if (typeof propType === 'string') {
|
|
63
|
+
return b.tsStringKeyword();
|
|
64
|
+
}
|
|
65
|
+
if (propType.Type === 'group') {
|
|
66
|
+
return b.tsTypeReference(b.identifier(propType.Value.toString()));
|
|
67
|
+
}
|
|
68
|
+
if (propType.Type === 'literal') {
|
|
69
|
+
return b.tsLiteralType(b.stringLiteral(propType.Value.toString()));
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Couldn't parse property type ${JSON.stringify(propType, null, 4)}`);
|
|
72
|
+
}
|
|
73
|
+
function parseObjectType(props) {
|
|
74
|
+
const propItems = [];
|
|
75
|
+
for (const prop of props) {
|
|
76
|
+
/**
|
|
77
|
+
* ToDo(Christian): support Extensible
|
|
78
|
+
*/
|
|
79
|
+
if (prop.Name === '') {
|
|
80
|
+
propItems[propItems.length - 1].comments = [b.commentLine(`Missing: ${JSON.stringify(prop)}`)];
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const id = b.identifier(camelcase(prop.Name));
|
|
84
|
+
const cddlType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
|
|
85
|
+
const typeParameters = b.unionTypeAnnotation(cddlType.map((t) => {
|
|
86
|
+
if (typeof t === 'string') {
|
|
87
|
+
if (!NATIVE_TYPES[t]) {
|
|
88
|
+
throw new Error(`Unknown native type: "${t}`);
|
|
89
|
+
}
|
|
90
|
+
return b.typeParameter(NATIVE_TYPES[t]);
|
|
91
|
+
}
|
|
92
|
+
else if (t.Value === 'null') {
|
|
93
|
+
return b.nullTypeAnnotation();
|
|
94
|
+
}
|
|
95
|
+
else if (t.Type === 'group') {
|
|
96
|
+
const value = t.Value;
|
|
97
|
+
return b.typeParameter(
|
|
98
|
+
/**
|
|
99
|
+
* transform native CDDL types into TypeScript types
|
|
100
|
+
*/
|
|
101
|
+
NATIVE_TYPES[value] ? value : camelcase(value.toString(), { pascalCase: true }));
|
|
102
|
+
}
|
|
103
|
+
else if (t.Type === 'literal' && typeof t.Value === 'string') {
|
|
104
|
+
return b.stringLiteralTypeAnnotation(t.Value, t.Value);
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Couldn't parse property ${JSON.stringify(t)}`);
|
|
107
|
+
}));
|
|
108
|
+
const isOptional = prop.Occurrence.n === 0;
|
|
109
|
+
propItems.push(b.objectTypeProperty(id, typeParameters, isOptional));
|
|
110
|
+
}
|
|
111
|
+
const obj = b.objectTypeAnnotation(propItems);
|
|
112
|
+
return obj;
|
|
113
|
+
}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,YAAY,CAAA;CACvB"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/utils.d.ts
CHANGED
package/build/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,aAAa,CAAA;AAE3C,wBAAgB,QAAQ,CAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,wBAAgB,qBAAqB,CAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,wBAAgB,OAAO,CAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE5C;AAED,wBAAgB,yBAAyB,CAAE,EAAE,EAAE,MAAM,WAOpD;AAED,wBAAgB,gBAAgB,CAAE,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAa/D"}
|