Skip to content

Commit 1d7bf84

Browse files
committed
feat: compile schemas from source
1 parent f83dc5c commit 1d7bf84

27 files changed

+8646
-2378
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ This project is a rework<sup>1</sup> of [jdiaz5513/capnp-ts](https://github.com/
6868
- [x] Use native `TextEncoder` and `TextDecoder` for utf8 encoding
6969
- [x] Enums are typed plain JS objects (this way `.ts` files work with strip-only ts loaders without enum support.)
7070
- [x] Compiler CLI can directly accept a path to `.capnp` files and internally use `capnpc`
71+
- [x] Built-in schemas are compiled from source (compiler, compiles itself. so cool right?)
7172
- [ ] [WIP] Use reflection (getter setters) to access structs.
7273
- [ ] [TODO] Investigate runtime performance. Some language features make full traverse slow, especially on Node.js < 22, Bun is fast and all good.
7374
- [ ] [PLANNED] Investigate RPC level 1 (some progress [here](https://github.com/jdiaz5513/capnp-ts/pull/169))

src/compiler/ast-creators.ts

+25-24
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ export function createConcreteListProperty(
2121
ctx: CodeGeneratorFileContext,
2222
field: s.Field,
2323
): ts.PropertyDeclaration {
24-
const name = `_${util.c2t(field.getName())}`;
25-
const type = f.createTypeReferenceNode(
26-
getJsType(ctx, field.getSlot().getType(), true),
27-
);
24+
const name = `_${util.c2t(field.name)}`;
25+
const type = f.createTypeReferenceNode(getJsType(ctx, field.slot.type, true));
2826
let u: ts.Expression | undefined;
2927
return f.createPropertyDeclaration(
3028
[STATIC],
@@ -37,7 +35,7 @@ export function createConcreteListProperty(
3735

3836
export function createConstProperty(node: s.Node): ts.PropertyDeclaration {
3937
const name = util.c2s(getDisplayNamePrefix(node));
40-
const initializer = createValueExpression(node.getConst().getValue());
38+
const initializer = createValueExpression(node.const.value);
4139

4240
return f.createPropertyDeclaration(
4341
[STATIC, READONLY],
@@ -100,7 +98,7 @@ export function createUnionConstProperty(
10098
fullClassName: string,
10199
field: s.Field,
102100
): ts.PropertyDeclaration {
103-
const name = util.c2s(field.getName());
101+
const name = util.c2s(field.name);
104102
const initializer = f.createPropertyAccessExpression(
105103
f.createIdentifier(`${fullClassName}_Which`),
106104
name,
@@ -120,35 +118,35 @@ export function createValueExpression(value: s.Value): ts.Expression {
120118

121119
switch (value.which()) {
122120
case s.Value.BOOL: {
123-
return value.getBool() ? f.createTrue() : f.createFalse();
121+
return value.bool ? f.createTrue() : f.createFalse();
124122
}
125123

126124
case s.Value.ENUM: {
127-
return f.createNumericLiteral(value.getEnum().toString());
125+
return f.createNumericLiteral(value.enum.toString());
128126
}
129127

130128
case s.Value.FLOAT32: {
131-
return numericExpression(value.getFloat32());
129+
return numericExpression(value.float32);
132130
}
133131

134132
case s.Value.FLOAT64: {
135-
return numericExpression(value.getFloat64());
133+
return numericExpression(value.float64);
136134
}
137135

138136
case s.Value.INT8: {
139-
return numericExpression(value.getInt8());
137+
return numericExpression(value.int8);
140138
}
141139

142140
case s.Value.INT16: {
143-
return numericExpression(value.getInt16());
141+
return numericExpression(value.int16);
144142
}
145143

146144
case s.Value.INT32: {
147-
return numericExpression(value.getInt32());
145+
return numericExpression(value.int32);
148146
}
149147

150148
case s.Value.INT64: {
151-
let v = value.getInt64().toString(16);
149+
let v = value.int64.toString(16);
152150
let neg = "";
153151
if (v[0] === "-") {
154152
v = v.slice(1);
@@ -162,58 +160,61 @@ export function createValueExpression(value: s.Value): ts.Expression {
162160
}
163161

164162
case s.Value.TEXT: {
165-
return f.createStringLiteral(value.getText());
163+
return f.createStringLiteral(value.text);
166164
}
167165

168166
case s.Value.UINT16: {
169-
return f.createNumericLiteral(value.getUint16().toString());
167+
return f.createNumericLiteral(value.uint16.toString());
170168
}
171169

172170
case s.Value.UINT32: {
173-
return f.createNumericLiteral(value.getUint32().toString());
171+
return f.createNumericLiteral(value.uint32.toString());
174172
}
175173

176174
case s.Value.UINT64: {
177175
return f.createCallExpression(f.createIdentifier("BigInt"), undefined, [
178-
f.createStringLiteral(`0x${value.getUint64().toString(16)}`),
176+
f.createStringLiteral(`0x${value.uint64.toString(16)}`),
179177
]);
180178
}
181179
case s.Value.UINT8: {
182-
return f.createNumericLiteral(value.getUint8().toString());
180+
return f.createNumericLiteral(value.uint8.toString());
183181
}
184182

185183
case s.Value.VOID: {
186184
return f.createIdentifier("undefined");
187185
}
188186

189187
case s.Value.ANY_POINTER: {
190-
p = value.getAnyPointer();
188+
p = value.anyPointer;
191189

192190
break;
193191
}
194192

195193
case s.Value.DATA: {
196-
p = value.getData();
194+
p = value.data;
197195

198196
break;
199197
}
200198

201199
case s.Value.LIST: {
202-
p = value.getList();
200+
p = value.list;
203201

204202
break;
205203
}
206204

207205
case s.Value.STRUCT: {
208-
p = value.getStruct();
206+
p = value.struct;
209207

210208
break;
211209
}
212210

213211
// case s.Value.INTERFACE:
214212
default: {
215213
throw new Error(
216-
format(E.GEN_SERIALIZE_UNKNOWN_VALUE, s.Value_Which[value.which()]),
214+
format(
215+
E.GEN_SERIALIZE_UNKNOWN_VALUE,
216+
value.which() /* s.Value_Which[value.which()] */,
217+
), // TODO
217218
);
218219
}
219220
}

src/compiler/code-generator-file-context.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ export class CodeGeneratorFileContext {
1919
) {
2020
this.req = req;
2121
this.file = file;
22-
this.nodes = req.getNodes().toArray();
22+
this.nodes = req.nodes.toArray();
2323
this.concreteLists = [];
2424
this.generatedNodeIds = [];
2525
this.statements = [];
2626
this.tsPath = "";
27-
this.imports = file.getImports().toArray();
27+
this.imports = file.imports.toArray();
2828
}
2929

3030
toString(): string {
31-
return this.file ? this.file.getFilename() : "CodeGeneratorFileContext()";
31+
return this.file ? this.file.filename : "CodeGeneratorFileContext()";
3232
}
3333
}

src/compiler/compiler.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ export async function compileAll(
2828
// Load requested files into context
2929
const req = new Message(buff, false).getRoot(s.CodeGeneratorRequest);
3030
const ctx = new CodeGeneratorContext();
31-
ctx.files = req
32-
.getRequestedFiles()
33-
.map((file) => loadRequestedFile(req, file));
31+
ctx.files = req.requestedFiles.map((file) => loadRequestedFile(req, file));
3432

3533
// Compile files in memory
3634
const files = new Map<string, string>(
@@ -53,9 +51,9 @@ export function compileFile(ctx: CodeGeneratorFileContext): string {
5351
generateNestedImports(ctx);
5452
generateFileId(ctx);
5553

56-
for (const n of lookupNode(ctx, ctx.file)
57-
.getNestedNodes()
58-
.map((n) => lookupNode(ctx, n)))
54+
for (const n of lookupNode(ctx, ctx.file).nestedNodes.map((n) =>
55+
lookupNode(ctx, n),
56+
))
5957
generateNode(ctx, n);
6058

6159
for (const [fullClassName, field] of ctx.concreteLists)

src/compiler/file.ts

+24-27
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import * as E from "./errors";
99
import * as util from "./util";
1010

1111
export function compareCodeOrder(
12-
a: { getCodeOrder(): number },
13-
b: { getCodeOrder(): number },
12+
a: { readonly codeOrder: number },
13+
b: { readonly codeOrder: number },
1414
): number {
15-
return a.getCodeOrder() - b.getCodeOrder();
15+
return a.codeOrder - b.codeOrder;
1616
}
1717

1818
export function getConcreteListType(
@@ -21,17 +21,16 @@ export function getConcreteListType(
2121
): string {
2222
if (!type.isList()) return getJsType(ctx, type, false);
2323

24-
const elementType = type.getList().getElementType();
24+
const elementType = type.list.elementType;
2525
const elementTypeWhich = elementType.which();
2626

2727
if (elementTypeWhich === s.Type.LIST) {
2828
return `$.PointerList(${getConcreteListType(ctx, elementType)})`;
2929
} else if (elementTypeWhich === s.Type.STRUCT) {
30-
const structNode = lookupNode(ctx, elementType.getStruct().getTypeId());
30+
const structNode = lookupNode(ctx, elementType.struct.typeId);
3131

3232
if (
33-
structNode.getStruct().getPreferredListEncoding() !==
34-
s.ElementSize.INLINE_COMPOSITE
33+
structNode.struct.preferredListEncoding !== s.ElementSize.INLINE_COMPOSITE
3534
) {
3635
throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST);
3736
}
@@ -43,12 +42,11 @@ export function getConcreteListType(
4342
}
4443

4544
export function getDisplayNamePrefix(node: s.Node): string {
46-
return node.getDisplayName().slice(node.getDisplayNamePrefixLength());
45+
return node.displayName.slice(node.displayNamePrefixLength);
4746
}
4847

4948
export function getFullClassName(node: s.Node): string {
50-
return node
51-
.getDisplayName()
49+
return node.displayName
5250
.split(":")[1]
5351
.split(".")
5452
.map((s) => util.c2t(s))
@@ -76,7 +74,7 @@ export function getJsType(
7674
}
7775

7876
case s.Type.ENUM: {
79-
return getFullClassName(lookupNode(ctx, type.getEnum().getTypeId()));
77+
return getFullClassName(lookupNode(ctx, type.enum.typeId));
8078
}
8179

8280
case s.Type.FLOAT32:
@@ -100,11 +98,11 @@ export function getJsType(
10098
}
10199

102100
case s.Type.LIST: {
103-
return `$.List${constructor ? "Ctor" : ""}<${getJsType(ctx, type.getList().getElementType(), false)}>`;
101+
return `$.List${constructor ? "Ctor" : ""}<${getJsType(ctx, type.list.elementType, false)}>`;
104102
}
105103

106104
case s.Type.STRUCT: {
107-
const c = getFullClassName(lookupNode(ctx, type.getStruct().getTypeId()));
105+
const c = getFullClassName(lookupNode(ctx, type.struct.typeId));
108106

109107
return constructor ? `$.StructCtor<${c}>` : c;
110108
}
@@ -126,19 +124,18 @@ export function getJsType(
126124
export function getUnnamedUnionFields(node: s.Node): s.Field[] {
127125
if (!node.isStruct()) return [];
128126

129-
return node
130-
.getStruct()
131-
.getFields()
132-
.filter((f) => f.getDiscriminantValue() !== s.Field.NO_DISCRIMINANT);
127+
return node.struct.fields.filter(
128+
(f) => f.discriminantValue !== s.Field.NO_DISCRIMINANT,
129+
);
133130
}
134131

135132
export function hasNode(
136133
ctx: CodeGeneratorFileContext,
137-
lookup: { getId(): bigint } | bigint,
134+
lookup: { readonly id: bigint } | bigint,
138135
): boolean {
139-
const id = typeof lookup === "bigint" ? lookup : lookup.getId();
136+
const id = typeof lookup === "bigint" ? lookup : lookup.id;
140137

141-
return ctx.nodes.some((n) => n.getId() === id);
138+
return ctx.nodes.some((n) => n.id === id);
142139
}
143140

144141
export function loadRequestedFile(
@@ -147,19 +144,19 @@ export function loadRequestedFile(
147144
): CodeGeneratorFileContext {
148145
const ctx = new CodeGeneratorFileContext(req, file);
149146

150-
const schema = lookupNode(ctx, file.getId());
147+
const schema = lookupNode(ctx, file.id);
151148

152-
ctx.tsPath = schema.getDisplayName().replace(/\.capnp$/, "") + ".ts";
149+
ctx.tsPath = schema.displayName.replace(/\.capnp$/, "") + ".ts";
153150

154151
return ctx;
155152
}
156153

157154
export function lookupNode(
158155
ctx: CodeGeneratorFileContext,
159-
lookup: { getId(): bigint } | bigint,
156+
lookup: { readonly id: bigint } | bigint,
160157
): s.Node {
161-
const id = typeof lookup === "bigint" ? lookup : lookup.getId();
162-
const node = ctx.nodes.find((n) => n.getId() === id);
158+
const id = typeof lookup === "bigint" ? lookup : lookup.id;
159+
const node = ctx.nodes.find((n) => n.id === id);
163160

164161
if (node === undefined) throw new Error(format(E.GEN_NODE_LOOKUP_FAIL, id));
165162

@@ -177,11 +174,11 @@ export function lookupNode(
177174
export function needsConcreteListClass(field: s.Field): boolean {
178175
if (!field.isSlot()) return false;
179176

180-
const slotType = field.getSlot().getType();
177+
const slotType = field.slot.type;
181178

182179
if (!slotType.isList()) return false;
183180

184-
const elementType = slotType.getList().getElementType();
181+
const elementType = slotType.list.elementType;
185182

186183
return elementType.isStruct() || elementType.isList();
187184
}

0 commit comments

Comments
 (0)