Skip to content

Commit df6c557

Browse files
committed
chore: update
1 parent c2eaa5a commit df6c557

38 files changed

Lines changed: 372 additions & 103 deletions

‎.vscode/settings.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"prettier.enable": false,
88

99
"explorer.fileNesting.patterns": {
10-
"*.jsrs": "$(capture).mts, $(capture).js, $(capture).do.js",
10+
"*.jsrs": "$(capture).mts, $(capture).js, $(capture).do.js"
1111
},
1212

1313
// Silent the stylistic rules in you IDE, but still auto fix them

‎packages/language-server/src/codegen/context.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class CodegenContext {
44
needCaptureReturn = 0;
55
returnType: (Code[] | null)[] = [];
66
exportingTypes = new Set<string>();
7+
importedFrom = new Map<string, string>();
78
}
89

910
// TODO: Make this not singleton

‎packages/language-server/src/codegen/enum.ts‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ export function* generateEnum(node: SyntaxNode): Generator<Code> {
2727
yield ` { [${symbolName}]: typeof ${symbolName} }\n`;
2828

2929
const ctorName = escapeCtorName(name.text);
30-
yield `${exporting}interface ${ctorName}`;
31-
if (typeParameters)
32-
yield* generateTypeParameters(typeParameters);
33-
yield ` {\n`;
30+
const anyEnumType: Code[] = typeParameters
31+
? [name, `<${typeParameters.namedChildren.map(() => "any").join(", ")}>`]
32+
: [name];
33+
yield `${exporting}interface ${ctorName} {\n`;
3434
if (body) {
3535
for (const child of body.namedChildren) {
3636
if (child.type === "enum_variant") {
37-
yield* generateEnumVariant(child, enumType);
37+
yield* generateEnumVariant(child, enumType, anyEnumType, typeParameters);
3838
}
3939
}
4040
}
@@ -44,14 +44,15 @@ export function* generateEnum(node: SyntaxNode): Generator<Code> {
4444
yield `!: ${ctorName}\n`;
4545
}
4646

47-
function* generateEnumVariant(node: SyntaxNode, enumType: Code[]): Generator <Code> {
47+
function* generateEnumVariant(node: SyntaxNode, enumType: Code[], anyEnumType: Code[], typeParameters: SyntaxNode | null): Generator<Code> {
4848
if (node.type === "enum_variant") {
4949
const name = node.childForFieldName("name")!;
5050
const body = node.childForFieldName("body");
5151

5252
yield ` `;
5353
yield name;
5454
if (body) {
55+
yield* generateTypeParameters(typeParameters);
5556
yield "(";
5657
if (body.type === "ordered_field_declaration_list") {
5758
let index = 0;
@@ -66,9 +67,13 @@ function* generateEnumVariant(node: SyntaxNode, enumType: Code[]): Generator <Co
6667
yield* generateFieldDeclarationList(body);
6768
}
6869
yield `)`;
70+
yield `: `;
71+
yield* enumType;
72+
}
73+
else {
74+
yield `: `;
75+
yield* anyEnumType;
6976
}
70-
yield `: `;
71-
yield* enumType;
7277
yield `;\n`;
7378
}
7479
}

‎packages/language-server/src/codegen/escaping.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ export function escapeCtorName(name: string) {
22
return `__JSRS_${name}__Ctor`;
33
}
44

5+
export function escapeCtorImplName(name: string) {
6+
return `__JSRS_${name}__CtorImpl`;
7+
}
8+
59
export function escapeDataName(name: string) {
610
return `__JSRS_${name}__Data`;
711
}

‎packages/language-server/src/codegen/function.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export function* generateFunction(
2222
if (node.namedChildren[0]?.type === "visibility_modifier") {
2323
yield `export `;
2424
}
25+
const modifiers = node.namedChildren.find(c => c.type === "function_modifiers");
26+
if (modifiers?.text.includes("async")) {
27+
yield `async `;
28+
}
2529
yield `function `;
2630
}
2731

‎packages/language-server/src/codegen/impl.ts‎

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import type { SyntaxNode } from "tree-sitter";
22
import type { Code } from "../types";
33
import { context } from "./context";
4-
import { escapeCtorName } from "./escaping";
4+
import { escapeCtorImplName, escapeCtorName } from "./escaping";
55
import { FunctionKind, generateFunction } from "./function";
66
import { generateTypeParameters, getTypeParamPlaceholders } from "./type";
77

8+
function* generateInterfaceExtends(exporting: string, importedModule: string | undefined, name: string, extendsClause: string): Generator<Code> {
9+
if (importedModule) {
10+
yield `declare module "${importedModule}" {\n interface ${name} extends ${extendsClause} {}\n}\n`;
11+
}
12+
else {
13+
yield `${exporting}interface ${name} extends ${extendsClause} {}\n`;
14+
}
15+
}
16+
817
export function* generateImpl(node: SyntaxNode): Generator<
918
Code
1019
> {
@@ -28,8 +37,21 @@ export function* generateImpl(node: SyntaxNode): Generator<
2837
}
2938
}
3039

40+
const importedModule = context.importedFrom.get(name);
41+
const uid = node.startIndex;
42+
43+
if (trait) {
44+
const traitName = trait.text;
45+
const traitModule = context.importedFrom.get(traitName);
46+
if (traitModule) {
47+
yield `type ${escapeCtorImplName(traitName)}_${uid} = import("${traitModule}").${escapeCtorImplName(traitName)};\n`;
48+
yield `type ${escapeCtorName(traitName)}_${uid} = import("${traitModule}").${escapeCtorName(traitName)};\n`;
49+
yield `type ${traitName}_${uid} = import("${traitModule}").${traitName};\n`;
50+
}
51+
}
52+
3153
if (staticMethods.length > 0) {
32-
const implName = `__JSRS_impl_${node.startIndex}_static`;
54+
const implName = `__JSRS_impl_${uid}_static`;
3355
yield `function ${implName}`;
3456
yield* generateTypeParameters(typeParams);
3557
yield `() { return {\n`;
@@ -41,20 +63,22 @@ export function* generateImpl(node: SyntaxNode): Generator<
4163

4264
if (trait) {
4365
const traitName = trait.text;
44-
yield `satisfies ${escapeCtorName(traitName)}; }\n`;
45-
// TODO: declare module "..." {
46-
yield `${exporting}interface ${escapeCtorName(name)} extends ${escapeCtorName(traitName)} {}\n`;
66+
const traitModule = context.importedFrom.get(traitName);
67+
const ctorImplAlias = traitModule ? `${escapeCtorImplName(traitName)}_${uid}` : escapeCtorImplName(traitName);
68+
const ctorAlias = traitModule ? `${escapeCtorName(traitName)}_${uid}` : escapeCtorName(traitName);
69+
yield `satisfies ${ctorImplAlias}; }\n`;
70+
yield* generateInterfaceExtends(exporting, importedModule, escapeCtorName(name), ctorAlias);
4771
}
4872
else {
4973
yield "}\n";
5074
const typeParamPlaceholders = getTypeParamPlaceholders(typeParams);
5175
yield `type ${implName}_T${typeParamPlaceholders} = ReturnType<typeof ${implName}${typeParamPlaceholders}>;\n`;
52-
yield `${exporting}interface ${escapeCtorName(name)} extends ${implName}_T${typeParamPlaceholders} {}\n`;
76+
yield* generateInterfaceExtends(exporting, importedModule, escapeCtorName(name), `${implName}_T${typeParamPlaceholders}`);
5377
}
5478
}
5579

5680
if (instanceMethods.length > 0) {
57-
const implName = `__JSRS_impl_${node.startIndex}`;
81+
const implName = `__JSRS_impl_${uid}`;
5882
yield `function ${implName}`;
5983
yield* generateTypeParameters(typeParams);
6084
yield `() { return {\n`;
@@ -66,15 +90,16 @@ export function* generateImpl(node: SyntaxNode): Generator<
6690

6791
if (trait) {
6892
const traitName = trait.text;
69-
yield `satisfies ${traitName}; }\n`;
70-
// TODO: declare module "..." {
71-
yield `${exporting}interface ${name} extends ${traitName} {}\n`;
93+
const traitModule = context.importedFrom.get(traitName);
94+
const instanceAlias = traitModule ? `${traitName}_${uid}` : traitName;
95+
yield `satisfies ${instanceAlias}; }\n`;
96+
yield* generateInterfaceExtends(exporting, importedModule, name, instanceAlias);
7297
}
7398
else {
7499
yield "}\n";
75100
const typeParamPlaceholders = getTypeParamPlaceholders(typeParams);
76101
yield `type ${implName}_T${typeParamPlaceholders} = ReturnType<typeof ${implName}${typeParamPlaceholders}>;\n`;
77-
yield `${exporting}interface ${name} extends ${implName}_T${typeParamPlaceholders} {}\n`;
102+
yield* generateInterfaceExtends(exporting, importedModule, name, `${implName}_T${typeParamPlaceholders}`);
78103
}
79104
}
80105
}

‎packages/language-server/src/codegen/index.ts‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { generateMacroInvocation } from "./macro";
1010
import { generateMatch, getPatternBindings } from "./match";
1111
import { generatePrelude } from "./prelude";
1212
import { generateStruct, generateStructExpression } from "./struct";
13+
import { generateTrait } from "./trait";
1314
import { generateType } from "./type";
1415
import { generateUse } from "./use";
1516
import { between, generateChildren, wrapWith } from "./utils";
@@ -70,6 +71,9 @@ export function* generateStatement(node: SyntaxNode): Generator<Code> {
7071
case "static_item":
7172
yield* generateStaticItem(node);
7273
break;
74+
case "const_item":
75+
yield* generateConstItem(node);
76+
break;
7377
case "match_expression":
7478
yield* generateMatch(node);
7579
break;
@@ -82,6 +86,9 @@ export function* generateStatement(node: SyntaxNode): Generator<Code> {
8286
case "macro_invocation":
8387
yield* generateMacroInvocation(node);
8488
break;
89+
case "trait_item":
90+
yield* generateTrait(node);
91+
break;
8592
default:
8693
yield* generateExpression(node);
8794
}
@@ -116,6 +123,26 @@ function* generateLocal(node: SyntaxNode): Generator<Code> {
116123
}
117124
}
118125

126+
function* generateConstItem(node: SyntaxNode): Generator<Code> {
127+
const mutable = node.namedChildren[0]?.type === "visibility_modifier";
128+
yield mutable ? "export const " : "const ";
129+
130+
const name = node.childForFieldName("name")!;
131+
yield* generateIdentifier(name);
132+
133+
const type = node.childForFieldName("type");
134+
if (type) {
135+
yield ": ";
136+
yield* generateType(type);
137+
}
138+
139+
const value = node.childForFieldName("value");
140+
if (value) {
141+
yield " = ";
142+
yield* generateExpression(value);
143+
}
144+
}
145+
119146
function* generateStaticItem(node: SyntaxNode): Generator<Code> {
120147
const mutable = node.namedChildren.some(c => c.type === "mutable_specifier");
121148
yield mutable ? "let " : "const ";
@@ -675,7 +702,7 @@ function* generateReturnExpression(node: SyntaxNode): Generator<Code> {
675702
const [keyword, value] = node.children;
676703

677704
if (context.needCaptureReturn) {
678-
const typeCode = context.returnType[context.returnType.length - 1];
705+
const typeCode = context.returnType.at(-1);
679706
if (typeCode) {
680707
yield `(`;
681708
if (value) {

‎packages/language-server/src/codegen/macro.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import type { SyntaxNode } from "tree-sitter";
22
import type { Code } from "../types";
3+
import { context } from "./context";
34

45
export function* generateMacroInvocation(node: SyntaxNode): Generator<Code> {
56
const macro = node.childForFieldName("macro")!;
67
if (macro.text === "import") {
8+
const text = node.text;
9+
const fromMatch = text.match(/from\s+"([^"]+)"/);
10+
if (fromMatch) {
11+
const path = fromMatch[1];
12+
const namedMatch = text.match(/\{([^}]*)\}/);
13+
if (namedMatch) {
14+
for (const part of namedMatch[1].split(",")) {
15+
const name = part.trim().split(/\s+as\s+/).pop()!.trim();
16+
if (name)
17+
context.importedFrom.set(name, path);
18+
}
19+
}
20+
}
721
yield [
822
node.text.replaceAll(/[!()]/g, " "),
923
node.startIndex,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import type { SyntaxNode } from "tree-sitter";
2+
import type { Code } from "../types";
3+
import { generateIdentifier, generatePattern } from ".";
4+
import { escapeCtorImplName, escapeCtorName } from "./escaping";
5+
import { generateType, generateTypeParameters } from "./type";
6+
7+
export function* generateTrait(node: SyntaxNode): Generator<Code> {
8+
const nameNode = node.childForFieldName("name")!;
9+
const name = nameNode.text;
10+
const typeParams = node.childForFieldName("type_parameters");
11+
const body = node.childForFieldName("body")!;
12+
const exporting = node.namedChildren[0]?.type === "visibility_modifier" ? "export " : "";
13+
const typeParamSuffix = typeParams ? `<${typeParams.namedChildren.map(c => c.text).join(", ")}>` : "";
14+
15+
const staticMembers: SyntaxNode[] = [];
16+
const instanceMembers: SyntaxNode[] = [];
17+
18+
for (const child of body.namedChildren) {
19+
if (child.type === "function_item" || child.type === "function_signature_item") {
20+
const params = child.childForFieldName("parameters");
21+
const isInstance = params?.namedChildren[0]?.type === "self_parameter";
22+
if (isInstance)
23+
instanceMembers.push(child);
24+
else
25+
staticMembers.push(child);
26+
}
27+
else if (child.type === "const_item") {
28+
staticMembers.push(child);
29+
}
30+
}
31+
32+
// type __JSRS_Trait__CtorImpl = { optional?(): void; required(): void; }
33+
const ctorImplName = escapeCtorImplName(name);
34+
yield `${exporting}type ${ctorImplName}`;
35+
yield* generateTypeParameters(typeParams);
36+
yield ` = {\n`;
37+
for (const member of staticMembers) {
38+
if (member.type === "function_item" || member.type === "function_signature_item") {
39+
const hasDefault = member.type === "function_item";
40+
yield* generateMethodSignature(member, false, hasDefault);
41+
}
42+
else if (member.type === "const_item") {
43+
const hasDefault = !!member.childForFieldName("value");
44+
yield ` `;
45+
yield* generateIdentifier(member.childForFieldName("name")!);
46+
if (hasDefault)
47+
yield `?`;
48+
const type = member.childForFieldName("type");
49+
if (type) {
50+
yield `: `;
51+
yield* generateType(type);
52+
}
53+
yield `;\n`;
54+
}
55+
}
56+
yield `};\n`;
57+
58+
// type __JSRS_Trait__Ctor = Required<__JSRS_Trait__CtorImpl>
59+
yield `${exporting}type ${escapeCtorName(name)}${typeParamSuffix} = Required<${ctorImplName}${typeParamSuffix}>;\n`;
60+
61+
// interface Trait { method(this: unknown): void; }
62+
yield `${exporting}interface ${name}`;
63+
yield* generateTypeParameters(typeParams);
64+
yield ` {\n`;
65+
for (const member of instanceMembers) {
66+
const hasDefault = member.type === "function_item";
67+
yield* generateMethodSignature(member, true, hasDefault);
68+
}
69+
yield `}\n`;
70+
}
71+
72+
function* generateMethodSignature(node: SyntaxNode, isInstance: boolean, optional: boolean): Generator<Code> {
73+
const name = node.childForFieldName("name")!;
74+
const typeParams = node.childForFieldName("type_parameters");
75+
const parameters = node.childForFieldName("parameters")!;
76+
const returnType = node.childForFieldName("return_type");
77+
78+
yield ` `;
79+
yield* generateIdentifier(name);
80+
if (optional)
81+
yield `?`;
82+
yield* generateTypeParameters(typeParams);
83+
yield `(`;
84+
85+
let first = true;
86+
if (isInstance) {
87+
yield `this: unknown`;
88+
first = false;
89+
}
90+
91+
for (const param of parameters.namedChildren) {
92+
if (param.type === "self_parameter")
93+
continue;
94+
if (!first)
95+
yield `, `;
96+
first = false;
97+
if (param.type === "parameter") {
98+
yield* generatePattern(param.childForFieldName("pattern")!);
99+
const type = param.childForFieldName("type");
100+
if (type) {
101+
yield `: `;
102+
yield* generateType(type);
103+
}
104+
}
105+
else if (param.type === "identifier" || param.type === "type_identifier") {
106+
yield* generateIdentifier(param);
107+
}
108+
}
109+
110+
yield `)`;
111+
if (returnType) {
112+
yield `: `;
113+
yield* generateType(returnType);
114+
}
115+
else {
116+
yield `: void`;
117+
}
118+
yield `;\n`;
119+
}

0 commit comments

Comments
 (0)