cddl 0.6.0 → 0.7.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/.editorconfig +18 -0
- package/README.md +49 -2
- package/build/ast.d.ts +14 -5
- package/build/ast.d.ts.map +1 -1
- package/build/ast.js +5 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/parser.d.ts.map +1 -1
- package/build/parser.js +70 -29
- package/build/transform/ts.d.ts.map +1 -1
- package/build/transform/ts.js +113 -41
- package/package.json +1 -1
package/.editorconfig
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# EditorConfig helps developers define and maintain consistent
|
|
2
|
+
# coding styles between different editors and IDEs
|
|
3
|
+
# editorconfig.org
|
|
4
|
+
|
|
5
|
+
root = true
|
|
6
|
+
|
|
7
|
+
[*]
|
|
8
|
+
|
|
9
|
+
indent_style = space
|
|
10
|
+
indent_size = 4
|
|
11
|
+
|
|
12
|
+
end_of_line = lf
|
|
13
|
+
charset = utf-8
|
|
14
|
+
trim_trailing_whitespace = true
|
|
15
|
+
insert_final_newline = true
|
|
16
|
+
|
|
17
|
+
[{.travis.yml,**/*.json,.github/**/*.yml}]
|
|
18
|
+
indent_size = 2
|
package/README.md
CHANGED
|
@@ -20,7 +20,25 @@ $ npm install cddl
|
|
|
20
20
|
|
|
21
21
|
## Using this package
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
This package exposes a CLI as well as a programmatic interface for parsing and transforming CDDL.
|
|
24
|
+
|
|
25
|
+
### CLI
|
|
26
|
+
|
|
27
|
+
The `cddl` CLI offers a `validate` command that helps identify invalid CDDL formats, e.g.:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npx cddl validate ./path/to/interface.cddl
|
|
31
|
+
✅ Valid CDDL file!
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Programmatic Interface
|
|
35
|
+
|
|
36
|
+
You can also use this package to parse a CDDL file into:
|
|
37
|
+
|
|
38
|
+
- an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST).
|
|
39
|
+
- or a [TypeScript](https://www.typescriptlang.org/) definition
|
|
40
|
+
|
|
41
|
+
For example, given the following CDDL file:
|
|
24
42
|
|
|
25
43
|
```cddl
|
|
26
44
|
person = {
|
|
@@ -29,7 +47,7 @@ person = {
|
|
|
29
47
|
}
|
|
30
48
|
```
|
|
31
49
|
|
|
32
|
-
|
|
50
|
+
By default the package parses the content into an AST:
|
|
33
51
|
|
|
34
52
|
```js
|
|
35
53
|
import { parse } from 'cddl'
|
|
@@ -49,6 +67,35 @@ console.log(ast)
|
|
|
49
67
|
*/
|
|
50
68
|
```
|
|
51
69
|
|
|
70
|
+
You can apply a target specifier to transform the AST into a different language or format (currently supported: `ts` for TypeScript). Note that this is highly experimental and work in progress.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import { parse } from 'cddl'
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* spec.cddl:
|
|
77
|
+
*
|
|
78
|
+
* session.CapabilityRequest = {
|
|
79
|
+
* ?acceptInsecureCerts: bool,
|
|
80
|
+
* ?browserName: text,
|
|
81
|
+
* ?browserVersion: text,
|
|
82
|
+
* ?platformName: text,
|
|
83
|
+
* };
|
|
84
|
+
*/
|
|
85
|
+
const ts = parse('./spec.cddl', { target: 'ts' })
|
|
86
|
+
console.log(ts)
|
|
87
|
+
/**
|
|
88
|
+
* outputs:
|
|
89
|
+
*
|
|
90
|
+
* interface SessionCapabilityRequest {
|
|
91
|
+
* acceptInsecureCerts?: boolean,
|
|
92
|
+
* browserName?: string,
|
|
93
|
+
* browserVersion?: string,
|
|
94
|
+
* platformName?: string,
|
|
95
|
+
* }
|
|
96
|
+
*/
|
|
97
|
+
```
|
|
98
|
+
|
|
52
99
|
---
|
|
53
100
|
|
|
54
101
|
If you are interested in this project, please feel free to contribute ideas or code patches. Have a look at our [contributing](https://github.com/christian-bromann/cddl/blob/master/LICENSE) guidelines](https://github.com/christian-bromann/cddl/blob/master/LICENSE) to get started.
|
package/build/ast.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type Group = {
|
|
|
13
13
|
Name: string;
|
|
14
14
|
IsChoiceAddition: boolean;
|
|
15
15
|
Properties: (Property | Property[])[];
|
|
16
|
+
Comments: Comment[];
|
|
16
17
|
};
|
|
17
18
|
/**
|
|
18
19
|
* an array definition
|
|
@@ -26,6 +27,7 @@ export type Array = {
|
|
|
26
27
|
Type: 'array';
|
|
27
28
|
Name: string;
|
|
28
29
|
Values: (Property | Property[])[];
|
|
30
|
+
Comments: Comment[];
|
|
29
31
|
};
|
|
30
32
|
/**
|
|
31
33
|
* a tag definition
|
|
@@ -49,6 +51,7 @@ export type Variable = {
|
|
|
49
51
|
IsChoiceAddition: boolean;
|
|
50
52
|
PropertyType: PropertyType | PropertyType[];
|
|
51
53
|
Operator?: Operator;
|
|
54
|
+
Comments: Comment[];
|
|
52
55
|
};
|
|
53
56
|
/**
|
|
54
57
|
* a comment statement
|
|
@@ -59,6 +62,7 @@ export type Variable = {
|
|
|
59
62
|
export type Comment = {
|
|
60
63
|
Type: 'comment';
|
|
61
64
|
Content: string;
|
|
65
|
+
Leading: boolean;
|
|
62
66
|
};
|
|
63
67
|
export type Occurrence = {
|
|
64
68
|
n: number;
|
|
@@ -69,7 +73,7 @@ export type Property = {
|
|
|
69
73
|
Occurrence: Occurrence;
|
|
70
74
|
Name: PropertyName;
|
|
71
75
|
Type: PropertyType | PropertyType[];
|
|
72
|
-
|
|
76
|
+
Comments: Comment[];
|
|
73
77
|
Operator?: Operator;
|
|
74
78
|
};
|
|
75
79
|
export declare enum Type {
|
|
@@ -93,7 +97,12 @@ export declare enum Type {
|
|
|
93
97
|
BSTR = "bstr",
|
|
94
98
|
BYTES = "bytes",
|
|
95
99
|
TSTR = "tstr",
|
|
96
|
-
TEXT = "text"
|
|
100
|
+
TEXT = "text",
|
|
101
|
+
/**
|
|
102
|
+
* null types
|
|
103
|
+
*/
|
|
104
|
+
NIL = "nil",
|
|
105
|
+
NULL = "null"
|
|
97
106
|
}
|
|
98
107
|
/**
|
|
99
108
|
* can be a number, e.g. "foo = 0..10"
|
|
@@ -122,7 +131,7 @@ export interface Operator {
|
|
|
122
131
|
Type: OperatorType;
|
|
123
132
|
Value: PropertyType;
|
|
124
133
|
}
|
|
125
|
-
export type PropertyReferenceType = 'literal' | 'group' | 'group_array' | 'range' | 'tag';
|
|
134
|
+
export type PropertyReferenceType = 'literal' | 'group' | 'group_array' | 'array' | 'range' | 'tag';
|
|
126
135
|
export type PropertyReference = {
|
|
127
136
|
Type: PropertyReferenceType;
|
|
128
137
|
Value: string | number | boolean | Group | Array | Range | Tag;
|
|
@@ -130,10 +139,10 @@ export type PropertyReference = {
|
|
|
130
139
|
Operator?: Operator;
|
|
131
140
|
};
|
|
132
141
|
export interface NativeTypeWithOperator {
|
|
133
|
-
Type: Type;
|
|
142
|
+
Type: Type | PropertyReference;
|
|
134
143
|
Operator?: Operator;
|
|
135
144
|
}
|
|
136
|
-
export type Assignment = Group | Array | Variable
|
|
145
|
+
export type Assignment = Group | Array | Variable;
|
|
137
146
|
export type PropertyType = Assignment | Array | PropertyReference | string | NativeTypeWithOperator;
|
|
138
147
|
export type PropertyName = string;
|
|
139
148
|
//# sourceMappingURL=ast.d.ts.map
|
package/build/ast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,GAAG;IAChB,IAAI,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
|
package/build/ast.js
CHANGED
package/build/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import Parser from './parser.js';
|
|
|
3
3
|
import { ParseTargets } from './constants.js';
|
|
4
4
|
import type { ParseOptions } from './types.js';
|
|
5
5
|
declare const _default: {
|
|
6
|
-
parse: (filePath: string, opts
|
|
6
|
+
parse: (filePath: string, opts?: ParseOptions) => string | import("./ast.js").Assignment[];
|
|
7
7
|
};
|
|
8
8
|
export default _default;
|
|
9
9
|
export { Lexer, Parser, ParseTargets };
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,MAAM,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;;sBAGxB,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,MAAM,MAAM,aAAa,CAAA;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;;sBAGxB,MAAM,SAAQ,YAAY;;AADhD,wBAcC;AAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import Parser from './parser.js';
|
|
|
3
3
|
import { transform as transformTS } from './transform/ts.js';
|
|
4
4
|
import { ParseTargets } from './constants.js';
|
|
5
5
|
export default {
|
|
6
|
-
parse: (filePath, opts) => {
|
|
6
|
+
parse: (filePath, opts = { target: ParseTargets.AST }) => {
|
|
7
7
|
const parser = new Parser(filePath);
|
|
8
8
|
const ast = parser.parse();
|
|
9
9
|
if (opts.target === ParseTargets.AST) {
|
package/build/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,OAAO,EAAE,KAAK,EAAU,MAAM,aAAa,CAAC;AAG5C,OAAO,EAE2C,UAAU,EAE3D,MAAM,UAAU,CAAA;AAoBjB,MAAM,CAAC,OAAO,OAAO,MAAM;;IAEvB,CAAC,EAAE,KAAK,CAAC;IAET,QAAQ,EAAE,KAAK,CAAa;IAC5B,SAAS,EAAE,KAAK,CAAa;gBAEhB,QAAQ,EAAE,MAAM;IAQ7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,YAAY,CAAA;AAE9B,OAAO,EAAE,KAAK,EAAU,MAAM,aAAa,CAAC;AAG5C,OAAO,EAE2C,UAAU,EAE3D,MAAM,UAAU,CAAA;AAoBjB,MAAM,CAAC,OAAO,OAAO,MAAM;;IAEvB,CAAC,EAAE,KAAK,CAAC;IAET,QAAQ,EAAE,KAAK,CAAa;IAC5B,SAAS,EAAE,KAAK,CAAa;gBAEhB,QAAQ,EAAE,MAAM;IAQ7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;IA2CxB,OAAO,CAAC,oBAAoB;IA6Y5B,OAAO,CAAC,wBAAwB;IAahC;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,iBAAiB;IA6HzB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,gBAAgB;IA4DxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB,KAAK;IAaL,OAAO,CAAC,WAAW;CAKtB"}
|
package/build/parser.js
CHANGED
|
@@ -38,6 +38,10 @@ export default class Parser {
|
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
40
|
parseAssignments() {
|
|
41
|
+
const comments = [];
|
|
42
|
+
while (this.curToken.Type === Tokens.COMMENT) {
|
|
43
|
+
comments.push(this.parseComment());
|
|
44
|
+
}
|
|
41
45
|
/**
|
|
42
46
|
* expect group identifier, e.g.
|
|
43
47
|
* groupName =
|
|
@@ -60,7 +64,14 @@ export default class Parser {
|
|
|
60
64
|
this.nextToken(); // eat `/`
|
|
61
65
|
}
|
|
62
66
|
this.nextToken(); // eat `=`
|
|
63
|
-
|
|
67
|
+
const assignmentValue = this.parseAssignmentValue(groupName, isChoiceAddition);
|
|
68
|
+
// @ts-expect-error curToken can be changed by now but TS doesn't understand this
|
|
69
|
+
while (this.curToken.Type === Tokens.COMMENT) {
|
|
70
|
+
const comment = this.parseComment();
|
|
71
|
+
comment && comments.push(comment);
|
|
72
|
+
}
|
|
73
|
+
assignmentValue.Comments = comments;
|
|
74
|
+
return assignmentValue;
|
|
64
75
|
}
|
|
65
76
|
parseAssignmentValue(groupName, isChoiceAddition = false) {
|
|
66
77
|
let isChoice = false;
|
|
@@ -79,7 +90,8 @@ export default class Parser {
|
|
|
79
90
|
Type: 'variable',
|
|
80
91
|
Name: groupName,
|
|
81
92
|
IsChoiceAddition: isChoiceAddition,
|
|
82
|
-
PropertyType: this.parsePropertyTypes()
|
|
93
|
+
PropertyType: this.parsePropertyTypes(),
|
|
94
|
+
Comments: []
|
|
83
95
|
};
|
|
84
96
|
return variable;
|
|
85
97
|
}
|
|
@@ -116,7 +128,8 @@ export default class Parser {
|
|
|
116
128
|
Type: 'variable',
|
|
117
129
|
Name: groupName,
|
|
118
130
|
IsChoiceAddition: isChoiceAddition,
|
|
119
|
-
PropertyType: propertyType
|
|
131
|
+
PropertyType: propertyType,
|
|
132
|
+
Comments: []
|
|
120
133
|
};
|
|
121
134
|
if (this.isOperator()) {
|
|
122
135
|
variable.Operator = this.parseOperator();
|
|
@@ -140,7 +153,8 @@ export default class Parser {
|
|
|
140
153
|
Name: groupName,
|
|
141
154
|
IsChoiceAddition: isChoiceAddition,
|
|
142
155
|
PropertyType: prop,
|
|
143
|
-
Operator: this.parseOperator()
|
|
156
|
+
Operator: this.parseOperator(),
|
|
157
|
+
Comments: []
|
|
144
158
|
};
|
|
145
159
|
return variable;
|
|
146
160
|
}
|
|
@@ -148,10 +162,12 @@ export default class Parser {
|
|
|
148
162
|
}
|
|
149
163
|
while (!closingTokens.includes(this.curToken.Type)) {
|
|
150
164
|
const propertyType = [];
|
|
165
|
+
const comments = [];
|
|
151
166
|
let isUnwrapped = false;
|
|
152
167
|
let hasCut = false;
|
|
153
168
|
let propertyName = '';
|
|
154
|
-
|
|
169
|
+
const leadingComment = this.parseComment(true);
|
|
170
|
+
leadingComment && comments.push(leadingComment);
|
|
155
171
|
const occurrence = this.parseOccurrences();
|
|
156
172
|
/**
|
|
157
173
|
* check if variable name is unwrapped
|
|
@@ -184,7 +200,7 @@ export default class Parser {
|
|
|
184
200
|
Occurrence: occurrence,
|
|
185
201
|
Name: '',
|
|
186
202
|
Type: innerGroup,
|
|
187
|
-
|
|
203
|
+
Comments: []
|
|
188
204
|
});
|
|
189
205
|
continue;
|
|
190
206
|
}
|
|
@@ -202,6 +218,7 @@ export default class Parser {
|
|
|
202
218
|
if (this.curToken.Type === Tokens.COMMA || closingTokens.includes(this.curToken.Type)) {
|
|
203
219
|
const tokenType = this.curToken.Type;
|
|
204
220
|
let parsedComments = false;
|
|
221
|
+
let comment;
|
|
205
222
|
/**
|
|
206
223
|
* check if line has a comment
|
|
207
224
|
*/
|
|
@@ -221,7 +238,7 @@ export default class Parser {
|
|
|
221
238
|
Value: propertyName,
|
|
222
239
|
Unwrapped: isUnwrapped
|
|
223
240
|
}],
|
|
224
|
-
|
|
241
|
+
Comments: comment ? [comment] : []
|
|
225
242
|
});
|
|
226
243
|
if (this.curToken.Literal === Tokens.COMMA || this.curToken.Literal === closingTokens[0]) {
|
|
227
244
|
if (this.curToken.Literal === Tokens.COMMA) {
|
|
@@ -263,11 +280,11 @@ export default class Parser {
|
|
|
263
280
|
Occurrence: occurrence,
|
|
264
281
|
Name: '',
|
|
265
282
|
Type: {
|
|
266
|
-
Type:
|
|
283
|
+
Type: 'group',
|
|
267
284
|
Value: propertyName,
|
|
268
285
|
Unwrapped: isUnwrapped
|
|
269
286
|
},
|
|
270
|
-
|
|
287
|
+
Comments: comments
|
|
271
288
|
};
|
|
272
289
|
if (isChoice) {
|
|
273
290
|
/**
|
|
@@ -318,14 +335,15 @@ export default class Parser {
|
|
|
318
335
|
flipIsChoice = true;
|
|
319
336
|
this.nextToken(); // eat ,
|
|
320
337
|
}
|
|
321
|
-
|
|
338
|
+
const trailingComment = this.parseComment();
|
|
339
|
+
trailingComment && comments.push(trailingComment);
|
|
322
340
|
const prop = {
|
|
323
341
|
HasCut: hasCut,
|
|
324
342
|
Occurrence: occurrence,
|
|
325
343
|
Name: propertyName,
|
|
326
344
|
Type: propertyType,
|
|
327
|
-
|
|
328
|
-
...(operator ? operator : {})
|
|
345
|
+
Comments: comments,
|
|
346
|
+
...(operator ? { Operator: operator } : {})
|
|
329
347
|
};
|
|
330
348
|
if (isChoice) {
|
|
331
349
|
valuesOrProperties[valuesOrProperties.length - 1].push(prop);
|
|
@@ -364,9 +382,35 @@ export default class Parser {
|
|
|
364
382
|
return {
|
|
365
383
|
Type: 'array',
|
|
366
384
|
Name: groupName || '',
|
|
367
|
-
Values: valuesOrProperties
|
|
385
|
+
Values: valuesOrProperties,
|
|
386
|
+
Comments: []
|
|
368
387
|
};
|
|
369
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* simplify wrapped types, e.g. from
|
|
391
|
+
* {
|
|
392
|
+
* "Type": "group",
|
|
393
|
+
* "Name": "",
|
|
394
|
+
* "Properties": [
|
|
395
|
+
* {
|
|
396
|
+
* "HasCut": false,
|
|
397
|
+
* "Occurrence": {
|
|
398
|
+
* "n": 1,
|
|
399
|
+
* "m": 1
|
|
400
|
+
* },
|
|
401
|
+
* "Name": "",
|
|
402
|
+
* "Type": "bool",
|
|
403
|
+
* "Comment": ""
|
|
404
|
+
* }
|
|
405
|
+
* ],
|
|
406
|
+
* "IsChoiceAddition": false
|
|
407
|
+
* }
|
|
408
|
+
* back to:
|
|
409
|
+
* bool
|
|
410
|
+
*/
|
|
411
|
+
if (!groupName && valuesOrProperties.length === 1 && PREDEFINED_IDENTIFIER.includes(valuesOrProperties[0].Type)) {
|
|
412
|
+
return valuesOrProperties[0].Type;
|
|
413
|
+
}
|
|
370
414
|
/**
|
|
371
415
|
* otherwise a group
|
|
372
416
|
*/
|
|
@@ -374,7 +418,8 @@ export default class Parser {
|
|
|
374
418
|
Type: 'group',
|
|
375
419
|
Name: groupName || '',
|
|
376
420
|
Properties: valuesOrProperties,
|
|
377
|
-
IsChoiceAddition: isChoiceAddition
|
|
421
|
+
IsChoiceAddition: isChoiceAddition,
|
|
422
|
+
Comments: []
|
|
378
423
|
};
|
|
379
424
|
}
|
|
380
425
|
isPropertyValueSeparator() {
|
|
@@ -446,6 +491,8 @@ export default class Parser {
|
|
|
446
491
|
case Type.BYTES:
|
|
447
492
|
case Type.TSTR:
|
|
448
493
|
case Type.TEXT:
|
|
494
|
+
case Type.NIL:
|
|
495
|
+
case Type.NULL:
|
|
449
496
|
type = this.curToken.Literal;
|
|
450
497
|
break;
|
|
451
498
|
default: {
|
|
@@ -656,26 +703,20 @@ export default class Parser {
|
|
|
656
703
|
/**
|
|
657
704
|
* check if line has a comment
|
|
658
705
|
*/
|
|
659
|
-
parseComment() {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
comment = this.curToken.Literal.slice(2);
|
|
663
|
-
this.nextToken();
|
|
706
|
+
parseComment(isLeading) {
|
|
707
|
+
if (this.curToken.Type !== Tokens.COMMENT) {
|
|
708
|
+
return;
|
|
664
709
|
}
|
|
665
|
-
|
|
710
|
+
const comment = this.curToken.Literal.replace(/^;(\s*)/, '');
|
|
711
|
+
this.nextToken();
|
|
712
|
+
if (comment.trim().length === 0) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
return { Type: 'comment', Content: comment, Leading: Boolean(isLeading) };
|
|
666
716
|
}
|
|
667
717
|
parse() {
|
|
668
718
|
const definition = [];
|
|
669
719
|
while (this.curToken.Type !== Tokens.EOF) {
|
|
670
|
-
if (this.curToken.Type === Tokens.COMMENT) {
|
|
671
|
-
const comment = {
|
|
672
|
-
Type: 'comment',
|
|
673
|
-
Content: this.curToken.Literal.slice(1).trim()
|
|
674
|
-
};
|
|
675
|
-
definition.push(comment);
|
|
676
|
-
this.nextToken();
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
720
|
const group = this.parseAssignments();
|
|
680
721
|
if (group) {
|
|
681
722
|
definition.push(group);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts.d.ts","sourceRoot":"","sources":["../../src/transform/ts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"ts.d.ts","sourceRoot":"","sources":["../../src/transform/ts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAmG,MAAM,QAAQ,CAAA;AAmBzI,wBAAgB,SAAS,CAAE,WAAW,EAAE,UAAU,EAAE,UAkBnD"}
|
package/build/transform/ts.js
CHANGED
|
@@ -4,14 +4,17 @@ import typescriptParser from 'recast/parsers/typescript.js';
|
|
|
4
4
|
// @ts-ignore
|
|
5
5
|
import pkg from '../../package.json' assert { type: 'json' };
|
|
6
6
|
const b = types.builders;
|
|
7
|
-
const comments = [];
|
|
8
7
|
const NATIVE_TYPES = {
|
|
9
|
-
number:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
number: b.tsNumberKeyword(),
|
|
9
|
+
float: b.tsNumberKeyword(),
|
|
10
|
+
uint: b.tsNumberKeyword(),
|
|
11
|
+
bool: b.tsBooleanKeyword(),
|
|
12
|
+
str: b.tsStringKeyword(),
|
|
13
|
+
text: b.tsStringKeyword(),
|
|
14
|
+
tstr: b.tsStringKeyword(),
|
|
15
|
+
range: b.tsNumberKeyword(),
|
|
16
|
+
nil: b.tsNullKeyword(),
|
|
17
|
+
null: b.tsNullKeyword()
|
|
15
18
|
};
|
|
16
19
|
export function transform(assignments) {
|
|
17
20
|
let ast = parse(`// compiled with https://www.npmjs.com/package/cddl v${pkg.version}`, {
|
|
@@ -26,14 +29,9 @@ export function transform(assignments) {
|
|
|
26
29
|
}
|
|
27
30
|
ast.program.body.push(statement);
|
|
28
31
|
}
|
|
29
|
-
ast.program.comments = comments.map((c) => b.commentLine(c, false, false));
|
|
30
32
|
return print(ast).code;
|
|
31
33
|
}
|
|
32
34
|
function parseAssignment(ast, assignment) {
|
|
33
|
-
if (assignment.Type === 'comment') {
|
|
34
|
-
comments.push(assignment.Content);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
35
|
if (assignment.Type === 'variable') {
|
|
38
36
|
const propType = Array.isArray(assignment.PropertyType)
|
|
39
37
|
? assignment.PropertyType
|
|
@@ -48,13 +46,18 @@ function parseAssignment(ast, assignment) {
|
|
|
48
46
|
typeParameters = b.tsUnionType(propType.map(parsePropertyType));
|
|
49
47
|
}
|
|
50
48
|
const expr = b.tsTypeAliasDeclaration(id, typeParameters);
|
|
51
|
-
expr.comments =
|
|
49
|
+
expr.comments = assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true));
|
|
52
50
|
return expr;
|
|
53
51
|
}
|
|
54
52
|
if (assignment.Type === 'group') {
|
|
55
53
|
const id = b.identifier(camelcase(assignment.Name, { pascalCase: true }));
|
|
56
54
|
const objectType = parseObjectType(assignment.Properties);
|
|
57
|
-
const
|
|
55
|
+
const extendInterfaces = assignment.Properties
|
|
56
|
+
.filter((prop) => prop.Name === '')
|
|
57
|
+
.map((prop) => b.tsExpressionWithTypeArguments(b.identifier(camelcase(prop.Type[0].Value, { pascalCase: true }))));
|
|
58
|
+
const expr = b.tsInterfaceDeclaration(id, b.tsInterfaceBody(objectType));
|
|
59
|
+
expr.extends = extendInterfaces;
|
|
60
|
+
expr.comments = assignment.Comments.map((c) => b.commentLine(` ${c.Content}`, true));
|
|
58
61
|
return expr;
|
|
59
62
|
}
|
|
60
63
|
}
|
|
@@ -63,7 +66,7 @@ function parsePropertyType(propType) {
|
|
|
63
66
|
return b.tsStringKeyword();
|
|
64
67
|
}
|
|
65
68
|
if (propType.Type === 'group') {
|
|
66
|
-
return b.tsTypeReference(b.identifier(propType.Value.toString()));
|
|
69
|
+
return b.tsTypeReference(b.identifier(camelcase(propType.Value.toString(), { pascalCase: true })));
|
|
67
70
|
}
|
|
68
71
|
if (propType.Type === 'literal') {
|
|
69
72
|
return b.tsLiteralType(b.stringLiteral(propType.Value.toString()));
|
|
@@ -74,40 +77,109 @@ function parseObjectType(props) {
|
|
|
74
77
|
const propItems = [];
|
|
75
78
|
for (const prop of props) {
|
|
76
79
|
/**
|
|
77
|
-
*
|
|
80
|
+
* Empty groups like
|
|
81
|
+
* {
|
|
82
|
+
* HasCut: false,
|
|
83
|
+
* Occurrence: { n: 1, m: 1 },
|
|
84
|
+
* Name: '',
|
|
85
|
+
* Type: [ { Type: 'group', Value: 'Extensible', Unwrapped: false } ],
|
|
86
|
+
* Comment: ''
|
|
87
|
+
* }
|
|
88
|
+
* are ignored and later added as interface extensions
|
|
78
89
|
*/
|
|
79
90
|
if (prop.Name === '') {
|
|
80
|
-
propItems[propItems.length - 1].comments = [b.commentLine(`Missing: ${JSON.stringify(prop)}`)];
|
|
81
91
|
continue;
|
|
82
92
|
}
|
|
83
93
|
const id = b.identifier(camelcase(prop.Name));
|
|
84
94
|
const cddlType = Array.isArray(prop.Type) ? prop.Type : [prop.Type];
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return
|
|
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);
|
|
95
|
+
const comments = prop.Comments.map((c) => ` ${c.Content}`);
|
|
96
|
+
if (prop.Operator && prop.Operator.Type === 'default') {
|
|
97
|
+
const defaultValue = parseDefaultValue(prop.Operator);
|
|
98
|
+
defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
|
|
99
|
+
defaultValue && comments.push(` @default ${defaultValue}`);
|
|
100
|
+
}
|
|
101
|
+
const type = cddlType.map((t) => {
|
|
102
|
+
const unionType = parseUnionType(t);
|
|
103
|
+
if (unionType) {
|
|
104
|
+
const defaultValue = parseDefaultValue(t.Operator);
|
|
105
|
+
defaultValue && comments.length && comments.push(''); // add empty line if we have previous comments
|
|
106
|
+
defaultValue && comments.push(` @default ${defaultValue}`);
|
|
107
|
+
return unionType;
|
|
105
108
|
}
|
|
106
109
|
throw new Error(`Couldn't parse property ${JSON.stringify(t)}`);
|
|
107
|
-
})
|
|
110
|
+
});
|
|
111
|
+
const typeAnnotation = b.tsTypeAnnotation(b.tsUnionType(type));
|
|
108
112
|
const isOptional = prop.Occurrence.n === 0;
|
|
109
|
-
|
|
113
|
+
const propSignature = b.tsPropertySignature(id, typeAnnotation, isOptional);
|
|
114
|
+
propSignature.comments = comments.length ? [b.commentBlock(`*\n *${comments.join('\n *')}\n `)] : [];
|
|
115
|
+
propItems.push(propSignature);
|
|
116
|
+
}
|
|
117
|
+
return propItems;
|
|
118
|
+
}
|
|
119
|
+
function parseUnionType(t) {
|
|
120
|
+
if (typeof t === 'string') {
|
|
121
|
+
if (!NATIVE_TYPES[t]) {
|
|
122
|
+
throw new Error(`Unknown native type: "${t}`);
|
|
123
|
+
}
|
|
124
|
+
return NATIVE_TYPES[t];
|
|
125
|
+
}
|
|
126
|
+
else if (NATIVE_TYPES[t.Type]) {
|
|
127
|
+
return NATIVE_TYPES[t.Type];
|
|
128
|
+
}
|
|
129
|
+
else if (t.Value === 'null') {
|
|
130
|
+
return b.tsNullKeyword();
|
|
131
|
+
}
|
|
132
|
+
else if (t.Type === 'group') {
|
|
133
|
+
const value = t.Value;
|
|
134
|
+
/**
|
|
135
|
+
* check if we have
|
|
136
|
+
* ?attributes: {*text => text},
|
|
137
|
+
*/
|
|
138
|
+
if (!value && t.Properties) {
|
|
139
|
+
return b.tsTypeLiteral(parseObjectType(t.Properties));
|
|
140
|
+
}
|
|
141
|
+
return b.tsTypeReference(b.identifier(camelcase(value.toString(), { pascalCase: true })));
|
|
142
|
+
}
|
|
143
|
+
else if (t.Type === 'literal' && typeof t.Value === 'string') {
|
|
144
|
+
return b.tsLiteralType(b.stringLiteral(t.Value));
|
|
145
|
+
}
|
|
146
|
+
else if (t.Type === 'literal' && typeof t.Value === 'number') {
|
|
147
|
+
return b.tsLiteralType(b.numericLiteral(t.Value));
|
|
148
|
+
}
|
|
149
|
+
else if (t.Type === 'array') {
|
|
150
|
+
const types = t.Values[0].Type;
|
|
151
|
+
const typedTypes = (Array.isArray(types) ? types : [types]).map((val) => {
|
|
152
|
+
return typeof val === 'string' && NATIVE_TYPES[val]
|
|
153
|
+
? NATIVE_TYPES[val]
|
|
154
|
+
: b.tsTypeReference(b.identifier(camelcase(val.Value, { pascalCase: true })));
|
|
155
|
+
});
|
|
156
|
+
return b.tsArrayType(typedTypes.length > 1
|
|
157
|
+
? b.tsParenthesizedType(b.tsUnionType(typedTypes))
|
|
158
|
+
: b.tsUnionType(typedTypes));
|
|
159
|
+
}
|
|
160
|
+
else if (typeof t.Type === 'object' && t.Type.Type === 'range') {
|
|
161
|
+
return b.tsNumberKeyword();
|
|
162
|
+
}
|
|
163
|
+
else if (typeof t.Type === 'object' && t.Type.Type === 'group') {
|
|
164
|
+
/**
|
|
165
|
+
* e.g. ?pointerType: input.PointerType .default "mouse"
|
|
166
|
+
*/
|
|
167
|
+
const referenceValue = camelcase(t.Type.Value, { pascalCase: true });
|
|
168
|
+
return b.tsTypeReference(b.identifier(referenceValue));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function parseDefaultValue(operator) {
|
|
172
|
+
if (!operator || operator.Type !== 'default') {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const operatorValue = operator.Value;
|
|
176
|
+
if (operator.Value === 'null') {
|
|
177
|
+
return operator.Value;
|
|
178
|
+
}
|
|
179
|
+
if (operatorValue.Type !== 'literal') {
|
|
180
|
+
throw new Error(`Can't parse operator default value of ${JSON.stringify(operator)}`);
|
|
110
181
|
}
|
|
111
|
-
|
|
112
|
-
|
|
182
|
+
return typeof operatorValue.Value === 'string'
|
|
183
|
+
? `'${operatorValue.Value}'`
|
|
184
|
+
: operatorValue.Value;
|
|
113
185
|
}
|
package/package.json
CHANGED