cddl 0.2.2 → 0.4.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 +148 -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,21 +117,43 @@ 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
|
}
|
|
124
|
+
/**
|
|
125
|
+
* parse group within arrays, e.g.
|
|
126
|
+
* ```
|
|
127
|
+
* ActionsPerformActionsParameters = [1* {
|
|
128
|
+
* type: "key",
|
|
129
|
+
* id: text,
|
|
130
|
+
* actions: ActionItems,
|
|
131
|
+
* *text => any
|
|
132
|
+
* }]
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
if (this.curToken.Literal === Tokens.LBRACE) {
|
|
136
|
+
const innerGroup = this.parseAssignmentValue();
|
|
137
|
+
valuesOrProperties.push({
|
|
138
|
+
HasCut: false,
|
|
139
|
+
Occurrence: occurrence,
|
|
140
|
+
Name: '',
|
|
141
|
+
Type: innerGroup,
|
|
142
|
+
Comment: ''
|
|
143
|
+
});
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
81
146
|
propertyName = this.parsePropertyName();
|
|
82
147
|
/**
|
|
83
148
|
* if `,` is found we have a group reference and jump to the next line
|
|
84
149
|
*/
|
|
85
|
-
if (this.curToken.Type ===
|
|
150
|
+
if (this.curToken.Type === Tokens.COMMA || closingTokens.includes(this.curToken.Type)) {
|
|
86
151
|
const tokenType = this.curToken.Type;
|
|
87
152
|
let parsedComments = false;
|
|
88
153
|
/**
|
|
89
154
|
* check if line has a comment
|
|
90
155
|
*/
|
|
91
|
-
if (this.curToken.Type ===
|
|
156
|
+
if (this.curToken.Type === Tokens.COMMA && this.peekToken.Type === Tokens.COMMENT) {
|
|
92
157
|
this.nextToken();
|
|
93
158
|
comment = this.parseComment();
|
|
94
159
|
parsedComments = true;
|
|
@@ -97,7 +162,7 @@ class Parser {
|
|
|
97
162
|
HasCut: hasCut,
|
|
98
163
|
Occurrence: occurrence,
|
|
99
164
|
Name: '',
|
|
100
|
-
Type:
|
|
165
|
+
Type: PREDEFINED_IDENTIFIER.includes(propertyName)
|
|
101
166
|
? propertyName
|
|
102
167
|
: [{
|
|
103
168
|
Type: 'group',
|
|
@@ -112,7 +177,7 @@ class Parser {
|
|
|
112
177
|
/**
|
|
113
178
|
* only continue if next token contains a comma
|
|
114
179
|
*/
|
|
115
|
-
if (tokenType ===
|
|
180
|
+
if (tokenType === Tokens.COMMA) {
|
|
116
181
|
continue;
|
|
117
182
|
}
|
|
118
183
|
/**
|
|
@@ -125,16 +190,16 @@ class Parser {
|
|
|
125
190
|
* - `? "optional-key" ^ => int,`
|
|
126
191
|
* - `? optional-key: int,` - since the colon shortcut includes cuts
|
|
127
192
|
*/
|
|
128
|
-
if (this.curToken.Type ===
|
|
193
|
+
if (this.curToken.Type === Tokens.CARET || this.curToken.Type === Tokens.COLON) {
|
|
129
194
|
hasCut = true;
|
|
130
|
-
if (this.curToken.Type ===
|
|
195
|
+
if (this.curToken.Type === Tokens.CARET) {
|
|
131
196
|
this.nextToken(); // eat ^
|
|
132
197
|
}
|
|
133
198
|
}
|
|
134
199
|
/**
|
|
135
|
-
* check if we have a choice instead of an assignment
|
|
200
|
+
* check if we have a group choice instead of an assignment
|
|
136
201
|
*/
|
|
137
|
-
if (this.curToken.Type ===
|
|
202
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
138
203
|
const prop = {
|
|
139
204
|
HasCut: hasCut,
|
|
140
205
|
Occurrence: occurrence,
|
|
@@ -167,7 +232,7 @@ class Parser {
|
|
|
167
232
|
* else if no colon was found, throw
|
|
168
233
|
*/
|
|
169
234
|
if (!this.isPropertyValueSeparator()) {
|
|
170
|
-
throw
|
|
235
|
+
throw this.parserError('Expected ":" or "=>"');
|
|
171
236
|
}
|
|
172
237
|
this.nextToken(); // eat :
|
|
173
238
|
/**
|
|
@@ -187,7 +252,7 @@ class Parser {
|
|
|
187
252
|
* advance comma
|
|
188
253
|
*/
|
|
189
254
|
let flipIsChoice = false;
|
|
190
|
-
if (this.curToken.Type ===
|
|
255
|
+
if (this.curToken.Type === Tokens.COMMA) {
|
|
191
256
|
/**
|
|
192
257
|
* if we are in a choice, we leave it here
|
|
193
258
|
*/
|
|
@@ -235,7 +300,7 @@ class Parser {
|
|
|
235
300
|
/**
|
|
236
301
|
* if last closing token is "]" we have an array
|
|
237
302
|
*/
|
|
238
|
-
if (closingTokens[closingTokens.length - 1] ===
|
|
303
|
+
if (closingTokens[closingTokens.length - 1] === Tokens.RBRACK) {
|
|
239
304
|
return {
|
|
240
305
|
Type: 'array',
|
|
241
306
|
Name: groupName || '',
|
|
@@ -253,10 +318,10 @@ class Parser {
|
|
|
253
318
|
};
|
|
254
319
|
}
|
|
255
320
|
isPropertyValueSeparator() {
|
|
256
|
-
if (this.curToken.Type ===
|
|
321
|
+
if (this.curToken.Type === Tokens.COLON) {
|
|
257
322
|
return true;
|
|
258
323
|
}
|
|
259
|
-
if (this.curToken.Type ===
|
|
324
|
+
if (this.curToken.Type === Tokens.ASSIGN && this.peekToken.Type === Tokens.GT) {
|
|
260
325
|
this.nextToken(); // eat <
|
|
261
326
|
return true;
|
|
262
327
|
}
|
|
@@ -268,21 +333,21 @@ class Parser {
|
|
|
268
333
|
* @returns {String[]} closing tokens for group (either `}`, `)` or both)
|
|
269
334
|
*/
|
|
270
335
|
openSegment() {
|
|
271
|
-
if (this.curToken.Type ===
|
|
336
|
+
if (this.curToken.Type === Tokens.LBRACE) {
|
|
272
337
|
this.nextToken();
|
|
273
|
-
if (this.peekToken.Type ===
|
|
338
|
+
if (this.peekToken.Type === Tokens.LPAREN) {
|
|
274
339
|
this.nextToken();
|
|
275
|
-
return [
|
|
340
|
+
return [Tokens.RPAREN, Tokens.RBRACE];
|
|
276
341
|
}
|
|
277
|
-
return [
|
|
342
|
+
return [Tokens.RBRACE];
|
|
278
343
|
}
|
|
279
|
-
else if (this.curToken.Type ===
|
|
344
|
+
else if (this.curToken.Type === Tokens.LPAREN) {
|
|
280
345
|
this.nextToken();
|
|
281
|
-
return [
|
|
346
|
+
return [Tokens.RPAREN];
|
|
282
347
|
}
|
|
283
|
-
else if (this.curToken.Type ===
|
|
348
|
+
else if (this.curToken.Type === Tokens.LBRACK) {
|
|
284
349
|
this.nextToken();
|
|
285
|
-
return [
|
|
350
|
+
return [Tokens.RBRACK];
|
|
286
351
|
}
|
|
287
352
|
return [];
|
|
288
353
|
}
|
|
@@ -290,12 +355,12 @@ class Parser {
|
|
|
290
355
|
/**
|
|
291
356
|
* property name without quotes
|
|
292
357
|
*/
|
|
293
|
-
if (this.curToken.Type ===
|
|
358
|
+
if (this.curToken.Type === Tokens.IDENT || this.curToken.Type === Tokens.STRING) {
|
|
294
359
|
const name = this.curToken.Literal;
|
|
295
360
|
this.nextToken();
|
|
296
361
|
return name;
|
|
297
362
|
}
|
|
298
|
-
throw
|
|
363
|
+
throw this.parserError(`Expected property name, received ${this.curToken.Type}(${this.curToken.Literal}), ${this.peekToken.Type}(${this.peekToken.Literal})`);
|
|
299
364
|
}
|
|
300
365
|
parsePropertyType() {
|
|
301
366
|
let type;
|
|
@@ -303,57 +368,57 @@ class Parser {
|
|
|
303
368
|
/**
|
|
304
369
|
* check if variable name is unwrapped
|
|
305
370
|
*/
|
|
306
|
-
if (this.curToken.Literal ===
|
|
371
|
+
if (this.curToken.Literal === Tokens.TILDE) {
|
|
307
372
|
isUnwrapped = true;
|
|
308
373
|
this.nextToken(); // eat ~
|
|
309
374
|
}
|
|
310
375
|
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
|
|
376
|
+
case Type.BOOL:
|
|
377
|
+
case Type.INT:
|
|
378
|
+
case Type.UINT:
|
|
379
|
+
case Type.NINT:
|
|
380
|
+
case Type.FLOAT:
|
|
381
|
+
case Type.FLOAT16:
|
|
382
|
+
case Type.FLOAT32:
|
|
383
|
+
case Type.FLOAT64:
|
|
384
|
+
case Type.BSTR:
|
|
385
|
+
case Type.BYTES:
|
|
386
|
+
case Type.TSTR:
|
|
387
|
+
case Type.TEXT:
|
|
323
388
|
type = this.curToken.Literal;
|
|
324
389
|
break;
|
|
325
390
|
default: {
|
|
326
|
-
if (
|
|
391
|
+
if (BOOLEAN_LITERALS.includes(this.curToken.Literal)) {
|
|
327
392
|
type = {
|
|
328
393
|
Type: 'literal',
|
|
329
394
|
Value: this.curToken.Literal === 'true',
|
|
330
395
|
Unwrapped: isUnwrapped
|
|
331
396
|
};
|
|
332
397
|
}
|
|
333
|
-
else if (this.curToken.Type ===
|
|
398
|
+
else if (this.curToken.Type === Tokens.IDENT) {
|
|
334
399
|
type = {
|
|
335
400
|
Type: 'group',
|
|
336
401
|
Value: this.curToken.Literal,
|
|
337
402
|
Unwrapped: isUnwrapped
|
|
338
403
|
};
|
|
339
404
|
}
|
|
340
|
-
else if (this.curToken.Type ===
|
|
405
|
+
else if (this.curToken.Type === Tokens.STRING) {
|
|
341
406
|
type = {
|
|
342
407
|
Type: 'literal',
|
|
343
408
|
Value: this.curToken.Literal,
|
|
344
409
|
Unwrapped: isUnwrapped
|
|
345
410
|
};
|
|
346
411
|
}
|
|
347
|
-
else if (this.curToken.Type ===
|
|
412
|
+
else if (this.curToken.Type === Tokens.NUMBER || this.curToken.Type === Tokens.FLOAT) {
|
|
348
413
|
type = {
|
|
349
414
|
Type: 'literal',
|
|
350
|
-
Value:
|
|
415
|
+
Value: parseNumberValue(this.curToken),
|
|
351
416
|
Unwrapped: isUnwrapped
|
|
352
417
|
};
|
|
353
418
|
}
|
|
354
|
-
else if (this.curToken.Type ===
|
|
419
|
+
else if (this.curToken.Type === Tokens.HASH) {
|
|
355
420
|
this.nextToken();
|
|
356
|
-
const n =
|
|
421
|
+
const n = parseNumberValue(this.curToken);
|
|
357
422
|
this.nextToken(); // eat numeric value
|
|
358
423
|
this.nextToken(); // eat (
|
|
359
424
|
const t = this.parsePropertyType();
|
|
@@ -368,27 +433,27 @@ class Parser {
|
|
|
368
433
|
};
|
|
369
434
|
}
|
|
370
435
|
else {
|
|
371
|
-
throw
|
|
436
|
+
throw this.parserError(`Invalid property type "${this.curToken.Literal}"`);
|
|
372
437
|
}
|
|
373
438
|
}
|
|
374
439
|
}
|
|
375
440
|
/**
|
|
376
441
|
* check if type continue as a range
|
|
377
442
|
*/
|
|
378
|
-
if (this.peekToken.Type ===
|
|
443
|
+
if (this.peekToken.Type === Tokens.DOT &&
|
|
379
444
|
this.nextToken() &&
|
|
380
|
-
this.peekToken.Type ===
|
|
445
|
+
this.peekToken.Type === Tokens.DOT) {
|
|
381
446
|
this.nextToken();
|
|
382
447
|
let Inclusive = true;
|
|
383
448
|
/**
|
|
384
449
|
* check if range excludes upper bound
|
|
385
450
|
*/
|
|
386
|
-
if (this.peekToken.Type ===
|
|
451
|
+
if (this.peekToken.Type === Tokens.DOT) {
|
|
387
452
|
Inclusive = false;
|
|
388
453
|
this.nextToken();
|
|
389
454
|
}
|
|
390
455
|
this.nextToken();
|
|
391
|
-
const Min = typeof type === 'string'
|
|
456
|
+
const Min = typeof type === 'string' || typeof type.Value === 'number'
|
|
392
457
|
? type
|
|
393
458
|
: type.Value;
|
|
394
459
|
type = {
|
|
@@ -414,13 +479,13 @@ class Parser {
|
|
|
414
479
|
* city // lala: tstr / bool // per-pickup: true,
|
|
415
480
|
* )
|
|
416
481
|
*/
|
|
417
|
-
if (this.curToken.Type ===
|
|
482
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
418
483
|
return propertyTypes;
|
|
419
484
|
}
|
|
420
485
|
/**
|
|
421
486
|
* capture more if available (e.g. `tstr / float / boolean`)
|
|
422
487
|
*/
|
|
423
|
-
while (this.curToken.Type ===
|
|
488
|
+
while (this.curToken.Type === Tokens.SLASH) {
|
|
424
489
|
this.nextToken(); // eat `/`
|
|
425
490
|
propertyTypes.push(this.parsePropertyType());
|
|
426
491
|
this.nextToken();
|
|
@@ -431,7 +496,7 @@ class Parser {
|
|
|
431
496
|
* city // lala: tstr / bool // per-pickup: true,
|
|
432
497
|
* )
|
|
433
498
|
*/
|
|
434
|
-
if (this.curToken.Type ===
|
|
499
|
+
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
435
500
|
break;
|
|
436
501
|
}
|
|
437
502
|
}
|
|
@@ -453,13 +518,13 @@ class Parser {
|
|
|
453
518
|
* + bedroom: size,
|
|
454
519
|
* ```
|
|
455
520
|
*/
|
|
456
|
-
if (this.curToken.Type ===
|
|
457
|
-
const n = this.curToken.Type ===
|
|
521
|
+
if (this.curToken.Type === Tokens.QUEST || this.curToken.Type === Tokens.ASTERISK || this.curToken.Type === Tokens.PLUS) {
|
|
522
|
+
const n = this.curToken.Type === Tokens.PLUS ? 1 : 0;
|
|
458
523
|
let m = Infinity;
|
|
459
524
|
/**
|
|
460
525
|
* check if there is a max definition
|
|
461
526
|
*/
|
|
462
|
-
if (this.peekToken.Type ===
|
|
527
|
+
if (this.peekToken.Type === Tokens.NUMBER) {
|
|
463
528
|
m = parseInt(this.peekToken.Literal, 10);
|
|
464
529
|
this.nextToken();
|
|
465
530
|
}
|
|
@@ -472,8 +537,8 @@ class Parser {
|
|
|
472
537
|
* ```
|
|
473
538
|
*/
|
|
474
539
|
}
|
|
475
|
-
else if (this.curToken.Type ===
|
|
476
|
-
this.peekToken.Type ===
|
|
540
|
+
else if (this.curToken.Type === Tokens.NUMBER &&
|
|
541
|
+
this.peekToken.Type === Tokens.ASTERISK) {
|
|
477
542
|
const n = parseInt(this.curToken.Literal, 10);
|
|
478
543
|
let m = Infinity;
|
|
479
544
|
this.nextToken(); // eat "n"
|
|
@@ -481,7 +546,7 @@ class Parser {
|
|
|
481
546
|
/**
|
|
482
547
|
* check if there is a max definition
|
|
483
548
|
*/
|
|
484
|
-
if (this.curToken.Type ===
|
|
549
|
+
if (this.curToken.Type === Tokens.NUMBER) {
|
|
485
550
|
m = parseInt(this.curToken.Literal, 10);
|
|
486
551
|
this.nextToken();
|
|
487
552
|
}
|
|
@@ -494,7 +559,7 @@ class Parser {
|
|
|
494
559
|
*/
|
|
495
560
|
parseComment() {
|
|
496
561
|
let comment = '';
|
|
497
|
-
if (this.curToken.Type ===
|
|
562
|
+
if (this.curToken.Type === Tokens.COMMENT) {
|
|
498
563
|
comment = this.curToken.Literal.slice(2);
|
|
499
564
|
this.nextToken();
|
|
500
565
|
}
|
|
@@ -502,8 +567,8 @@ class Parser {
|
|
|
502
567
|
}
|
|
503
568
|
parse() {
|
|
504
569
|
const definition = [];
|
|
505
|
-
while (this.curToken.Type !==
|
|
506
|
-
if (this.curToken.Type ===
|
|
570
|
+
while (this.curToken.Type !== Tokens.EOF) {
|
|
571
|
+
if (this.curToken.Type === Tokens.COMMENT) {
|
|
507
572
|
const comment = {
|
|
508
573
|
Type: 'comment',
|
|
509
574
|
Content: this.curToken.Literal.slice(1).trim()
|
|
@@ -519,5 +584,9 @@ class Parser {
|
|
|
519
584
|
}
|
|
520
585
|
return definition;
|
|
521
586
|
}
|
|
587
|
+
parserError(message) {
|
|
588
|
+
const location = this.l.getLocation();
|
|
589
|
+
const locInfo = this.l.getLocationInfo();
|
|
590
|
+
return new Error(`${this.#filePath.replace(process.cwd(), '')}:${location.line + 1}:${location.position} - error: ${message}\n\n${locInfo}`);
|
|
591
|
+
}
|
|
522
592
|
}
|
|
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"}
|