skir-cc-gen 0.0.1
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/README.md +335 -0
- package/client/BUILD.bazel +33 -0
- package/client/MODULE.bazel +11 -0
- package/client/MODULE.bazel.lock +225 -0
- package/client/skir.cc +2687 -0
- package/client/skir.h +2946 -0
- package/client/skir.testing.h +282 -0
- package/dist/enum_field.d.ts +33 -0
- package/dist/enum_field.d.ts.map +1 -0
- package/dist/enum_field.js +85 -0
- package/dist/enum_field.js.map +1 -0
- package/dist/enum_variant.d.ts +31 -0
- package/dist/enum_variant.d.ts.map +1 -0
- package/dist/enum_variant.js +85 -0
- package/dist/enum_variant.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1627 -0
- package/dist/index.js.map +1 -0
- package/dist/keywords.d.ts +2 -0
- package/dist/keywords.d.ts.map +1 -0
- package/dist/keywords.js +101 -0
- package/dist/keywords.js.map +1 -0
- package/dist/recursivity_resolver.d.ts +58 -0
- package/dist/recursivity_resolver.d.ts.map +1 -0
- package/dist/recursivity_resolver.js +142 -0
- package/dist/recursivity_resolver.js.map +1 -0
- package/dist/type_speller.d.ts +17 -0
- package/dist/type_speller.d.ts.map +1 -0
- package/dist/type_speller.js +87 -0
- package/dist/type_speller.js.map +1 -0
- package/package.json +55 -0
- package/src/enum_variant.ts +126 -0
- package/src/index.ts +2089 -0
- package/src/keywords.ts +100 -0
- package/src/recursivity_resolver.ts +161 -0
- package/src/type_speller.ts +100 -0
package/src/keywords.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// From https://en.cppreference.com/w/cpp/keyword
|
|
2
|
+
export const CC_KEYWORDS: ReadonlySet<string> = new Set<string>([
|
|
3
|
+
"alignas",
|
|
4
|
+
"alignof",
|
|
5
|
+
"and",
|
|
6
|
+
"and_eq",
|
|
7
|
+
"asm",
|
|
8
|
+
"atomic_cancel",
|
|
9
|
+
"atomic_commit",
|
|
10
|
+
"atomic_noexcept",
|
|
11
|
+
"auto",
|
|
12
|
+
"bitand",
|
|
13
|
+
"bitor",
|
|
14
|
+
"bool",
|
|
15
|
+
"break",
|
|
16
|
+
"case",
|
|
17
|
+
"catch",
|
|
18
|
+
"char",
|
|
19
|
+
"char8_t",
|
|
20
|
+
"char16_t",
|
|
21
|
+
"char32_t",
|
|
22
|
+
"class",
|
|
23
|
+
"compl",
|
|
24
|
+
"concept",
|
|
25
|
+
"const",
|
|
26
|
+
"consteval",
|
|
27
|
+
"constexpr",
|
|
28
|
+
"constinit",
|
|
29
|
+
"const_cast",
|
|
30
|
+
"continue",
|
|
31
|
+
"co_await",
|
|
32
|
+
"co_return",
|
|
33
|
+
"co_yield",
|
|
34
|
+
"decltype",
|
|
35
|
+
"default",
|
|
36
|
+
"delete",
|
|
37
|
+
"do",
|
|
38
|
+
"double",
|
|
39
|
+
"dynamic_cast",
|
|
40
|
+
"else",
|
|
41
|
+
"enum",
|
|
42
|
+
"explicit",
|
|
43
|
+
"export",
|
|
44
|
+
"extern",
|
|
45
|
+
"false",
|
|
46
|
+
"float",
|
|
47
|
+
"for",
|
|
48
|
+
"friend",
|
|
49
|
+
"goto",
|
|
50
|
+
"if",
|
|
51
|
+
"inline",
|
|
52
|
+
"int",
|
|
53
|
+
"long",
|
|
54
|
+
"mutable",
|
|
55
|
+
"namespace",
|
|
56
|
+
"new",
|
|
57
|
+
"noexcept",
|
|
58
|
+
"not",
|
|
59
|
+
"not_eq",
|
|
60
|
+
"nullptr",
|
|
61
|
+
"operator",
|
|
62
|
+
"or",
|
|
63
|
+
"or_eq",
|
|
64
|
+
"private",
|
|
65
|
+
"protected",
|
|
66
|
+
"public",
|
|
67
|
+
"reflexpr",
|
|
68
|
+
"register",
|
|
69
|
+
"reinterpret_cast",
|
|
70
|
+
"requires",
|
|
71
|
+
"return",
|
|
72
|
+
"short",
|
|
73
|
+
"signed",
|
|
74
|
+
"sizeof",
|
|
75
|
+
"static",
|
|
76
|
+
"static_assert",
|
|
77
|
+
"static_cast",
|
|
78
|
+
"struct",
|
|
79
|
+
"switch",
|
|
80
|
+
"synchronized",
|
|
81
|
+
"template",
|
|
82
|
+
"this",
|
|
83
|
+
"thread_local",
|
|
84
|
+
"throw",
|
|
85
|
+
"true",
|
|
86
|
+
"try",
|
|
87
|
+
"typedef",
|
|
88
|
+
"typeid",
|
|
89
|
+
"typename",
|
|
90
|
+
"union",
|
|
91
|
+
"unsigned",
|
|
92
|
+
"using",
|
|
93
|
+
"virtual",
|
|
94
|
+
"void",
|
|
95
|
+
"volatile",
|
|
96
|
+
"wchar_t",
|
|
97
|
+
"while",
|
|
98
|
+
"xor",
|
|
99
|
+
"xor_eq",
|
|
100
|
+
]);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Field,
|
|
3
|
+
Module,
|
|
4
|
+
RecordKey,
|
|
5
|
+
RecordLocation,
|
|
6
|
+
ResolvedType,
|
|
7
|
+
} from "skir-internal";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* In C++, a struct type can not refer to itself directly. The only way to create a
|
|
11
|
+
* recursive struct is to use pointers or containers which allocate their elements on
|
|
12
|
+
* the heap, e.g. `std::vector`. Neither `absl::optional` nor `absl::variant` qualify
|
|
13
|
+
* because they allocate the element on the stack.
|
|
14
|
+
*
|
|
15
|
+
* When generating C++ types from recursive Skir types, Skir uses `rec<T>` instead of
|
|
16
|
+
* `T` for the fields which cause the type to be recursive.
|
|
17
|
+
*
|
|
18
|
+
* Consider this example:
|
|
19
|
+
*
|
|
20
|
+
* struct Foo {
|
|
21
|
+
* bar: Bar;
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* struct Bar {
|
|
25
|
+
* foo: Foo?;
|
|
26
|
+
* x: int32;
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* Both Foo and Bar are recursive since Foo refers to Bar and Bar refers to Foo, so Foo
|
|
30
|
+
* indirectly refers to itself and so does Bar.
|
|
31
|
+
*
|
|
32
|
+
* This is the C++ code Skir generates:
|
|
33
|
+
* struct Foo;
|
|
34
|
+
* struct Bar;
|
|
35
|
+
*
|
|
36
|
+
* struct Foo {
|
|
37
|
+
* rec<Bar> bar;
|
|
38
|
+
* };
|
|
39
|
+
*
|
|
40
|
+
* struct Bar {
|
|
41
|
+
* absl::optional<Foo> foo;
|
|
42
|
+
* int32 x = 0;
|
|
43
|
+
* };
|
|
44
|
+
*
|
|
45
|
+
* Because the type of Foo:bar is `rec<Bar>` instead of `Bar`, Foo no longer refers to
|
|
46
|
+
* Bar and thus Foo no longer refers to itself, and Bar no longer refers to itself
|
|
47
|
+
* either: problem solved.
|
|
48
|
+
* The same outcome would have been reached by changing the type of Bar::foo instead of
|
|
49
|
+
* Foo::bar.
|
|
50
|
+
*
|
|
51
|
+
* This class finds the fields of the skir types which should have a `rec<...>` type in
|
|
52
|
+
* their C++ representation.
|
|
53
|
+
*/
|
|
54
|
+
export class RecursvityResolver {
|
|
55
|
+
static resolve(
|
|
56
|
+
recordMap: ReadonlyMap<RecordKey, RecordLocation>,
|
|
57
|
+
module: Module,
|
|
58
|
+
): RecursvityResolver {
|
|
59
|
+
const sortedRecords = [...module.records].sort((a, b) => {
|
|
60
|
+
const aKey = a.recordAncestors.map((r) => r.name).join(".");
|
|
61
|
+
const bKey = b.recordAncestors.map((r) => r.name).join(".");
|
|
62
|
+
return aKey.localeCompare(bKey, "en-US");
|
|
63
|
+
});
|
|
64
|
+
const recursiveFields = new Set<Field>();
|
|
65
|
+
const recordToDeps = new Map<RecordKey, Set<RecordKey>>();
|
|
66
|
+
for (const record of sortedRecords) {
|
|
67
|
+
const recordDeps = new Set<RecordKey>();
|
|
68
|
+
for (const field of record.record.fields) {
|
|
69
|
+
if (!field.type) continue;
|
|
70
|
+
const depsCollector = new DepsCollector(
|
|
71
|
+
recordMap,
|
|
72
|
+
module.path,
|
|
73
|
+
recursiveFields,
|
|
74
|
+
);
|
|
75
|
+
depsCollector.collect(field.type);
|
|
76
|
+
if (depsCollector.deps.has(record.record.key)) {
|
|
77
|
+
recursiveFields.add(field);
|
|
78
|
+
} else {
|
|
79
|
+
depsCollector.deps.forEach((dep) => recordDeps.add(dep));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
recordToDeps.set(record.record.key, recordDeps);
|
|
83
|
+
}
|
|
84
|
+
const reorderedRecords = reorderRecords(
|
|
85
|
+
sortedRecords,
|
|
86
|
+
recordToDeps,
|
|
87
|
+
recordMap,
|
|
88
|
+
);
|
|
89
|
+
return new RecursvityResolver(recursiveFields, reorderedRecords);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private constructor(
|
|
93
|
+
private readonly recursiveFields: ReadonlySet<Field>,
|
|
94
|
+
/**
|
|
95
|
+
* All records declared in the module, reordered sp every record appears after its
|
|
96
|
+
* dependencies.
|
|
97
|
+
*/
|
|
98
|
+
readonly reorderedRecords: readonly RecordLocation[],
|
|
99
|
+
) {}
|
|
100
|
+
|
|
101
|
+
isRecursive(field: Field): boolean {
|
|
102
|
+
return this.recursiveFields.has(field);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class DepsCollector {
|
|
107
|
+
constructor(
|
|
108
|
+
readonly recordMap: ReadonlyMap<RecordKey, RecordLocation>,
|
|
109
|
+
readonly modulePath: string,
|
|
110
|
+
readonly recursiveFields: ReadonlySet<Field>,
|
|
111
|
+
) {}
|
|
112
|
+
|
|
113
|
+
readonly deps = new Set<RecordKey>();
|
|
114
|
+
|
|
115
|
+
collect(type: ResolvedType): void {
|
|
116
|
+
switch (type.kind) {
|
|
117
|
+
case "optional":
|
|
118
|
+
// absl::optional allocates its value on the stack.
|
|
119
|
+
this.collect(type.other);
|
|
120
|
+
break;
|
|
121
|
+
case "record": {
|
|
122
|
+
const recordKey = type.key;
|
|
123
|
+
const record = this.recordMap.get(recordKey)!;
|
|
124
|
+
// We only care about records declared in the module.
|
|
125
|
+
if (record.modulePath !== this.modulePath) break;
|
|
126
|
+
if (this.deps.has(recordKey)) break;
|
|
127
|
+
this.deps.add(recordKey);
|
|
128
|
+
// Wrapper variants of enums are allocated on the heap.
|
|
129
|
+
if (record.record.recordType === "enum") break;
|
|
130
|
+
for (const variant of record.record.fields) {
|
|
131
|
+
if (!variant.type) continue;
|
|
132
|
+
if (this.recursiveFields.has(variant)) continue;
|
|
133
|
+
this.collect(variant.type);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function reorderRecords(
|
|
141
|
+
inputRecords: readonly RecordLocation[],
|
|
142
|
+
recordToDeps: ReadonlyMap<RecordKey, Set<RecordKey>>,
|
|
143
|
+
recordMap: ReadonlyMap<RecordKey, RecordLocation>,
|
|
144
|
+
): readonly RecordLocation[] {
|
|
145
|
+
const result: RecordLocation[] = [];
|
|
146
|
+
const seenRecords = new Set<RecordKey>();
|
|
147
|
+
function addRecord(record: RecordLocation): void {
|
|
148
|
+
const { key } = record.record;
|
|
149
|
+
if (seenRecords.has(key)) return;
|
|
150
|
+
seenRecords.add(key);
|
|
151
|
+
const deps = recordToDeps.get(key)!;
|
|
152
|
+
for (const dep of deps) {
|
|
153
|
+
addRecord(recordMap.get(dep)!);
|
|
154
|
+
}
|
|
155
|
+
result.push(record);
|
|
156
|
+
}
|
|
157
|
+
for (const record of inputRecords) {
|
|
158
|
+
addRecord(record);
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Module,
|
|
3
|
+
RecordKey,
|
|
4
|
+
RecordLocation,
|
|
5
|
+
ResolvedType,
|
|
6
|
+
} from "skir-internal";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Transforms a type found in a `.skir` file into a C++ type.
|
|
10
|
+
*/
|
|
11
|
+
export class TypeSpeller {
|
|
12
|
+
constructor(
|
|
13
|
+
readonly recordMap: ReadonlyMap<RecordKey, RecordLocation>,
|
|
14
|
+
private readonly origin: Module,
|
|
15
|
+
private readonly includes: Set<string>,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
getCcType(
|
|
19
|
+
type: ResolvedType,
|
|
20
|
+
opts: {
|
|
21
|
+
fieldIsRecursive?: boolean;
|
|
22
|
+
forceNamespace?: boolean;
|
|
23
|
+
} = {},
|
|
24
|
+
): string {
|
|
25
|
+
switch (type.kind) {
|
|
26
|
+
case "record": {
|
|
27
|
+
const record = this.recordMap.get(type.key)!;
|
|
28
|
+
let qualifiedName = getClassName(record);
|
|
29
|
+
if (record.modulePath !== this.origin.path || opts.forceNamespace) {
|
|
30
|
+
// The record is located in an imported module.
|
|
31
|
+
const path = record.modulePath;
|
|
32
|
+
if (record.modulePath !== this.origin.path) {
|
|
33
|
+
this.includes.add('"skirout/' + path.replace(/\.skir$/, '.h"'));
|
|
34
|
+
}
|
|
35
|
+
const namespace = modulePathToNamespace(path);
|
|
36
|
+
qualifiedName = `::${namespace}::${qualifiedName}`;
|
|
37
|
+
}
|
|
38
|
+
if (opts.fieldIsRecursive) {
|
|
39
|
+
return `::skir::rec<${qualifiedName}>`;
|
|
40
|
+
}
|
|
41
|
+
return qualifiedName;
|
|
42
|
+
}
|
|
43
|
+
case "array": {
|
|
44
|
+
const itemType = this.getCcType(type.item, opts);
|
|
45
|
+
const { key } = type;
|
|
46
|
+
if (key) {
|
|
47
|
+
const { path } = key;
|
|
48
|
+
let keyType = "";
|
|
49
|
+
for (const pathItem of path) {
|
|
50
|
+
const fieldName = pathItem.name.text;
|
|
51
|
+
const isLastField = pathItem === path.at(-1);
|
|
52
|
+
if (isLastField && key.keyType.kind === "record") {
|
|
53
|
+
keyType = `::skir::get_kind<${keyType}>`;
|
|
54
|
+
} else {
|
|
55
|
+
keyType = `::skirout::get_${fieldName}<${keyType}>`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return `::skir::keyed_items<${itemType}, ${keyType}>`;
|
|
59
|
+
} else {
|
|
60
|
+
return `::std::vector<${itemType}>`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
case "optional": {
|
|
64
|
+
const valueType = this.getCcType(type.other, opts);
|
|
65
|
+
return `::absl::optional<${valueType}>`;
|
|
66
|
+
}
|
|
67
|
+
case "primitive": {
|
|
68
|
+
const { primitive } = type;
|
|
69
|
+
switch (primitive) {
|
|
70
|
+
case "bool":
|
|
71
|
+
return "bool";
|
|
72
|
+
case "int32":
|
|
73
|
+
return "::int32_t";
|
|
74
|
+
case "int64":
|
|
75
|
+
return "::int64_t";
|
|
76
|
+
case "uint64":
|
|
77
|
+
return "::uint64_t";
|
|
78
|
+
case "float32":
|
|
79
|
+
return "float";
|
|
80
|
+
case "float64":
|
|
81
|
+
return "double";
|
|
82
|
+
case "timestamp":
|
|
83
|
+
return "::absl::Time";
|
|
84
|
+
case "string":
|
|
85
|
+
return "::std::string";
|
|
86
|
+
case "bytes":
|
|
87
|
+
return "::skir::ByteString";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function modulePathToNamespace(path: string): string {
|
|
95
|
+
return "skirout_" + path.replace(/\.skir$/, "").replace("/", "_");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getClassName(record: RecordLocation): string {
|
|
99
|
+
return record.recordAncestors.map((r) => r.name.text).join("_");
|
|
100
|
+
}
|