@tsonic/emitter 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/package.json +34 -0
- package/scripts/update-golden-tests.ts +119 -0
- package/src/adapter-generator.ts +112 -0
- package/src/array.test.ts +301 -0
- package/src/constants.ts +32 -0
- package/src/core/exports.ts +36 -0
- package/src/core/imports.test.ts +83 -0
- package/src/core/imports.ts +243 -0
- package/src/core/index.ts +9 -0
- package/src/core/module-emitter/assembly.ts +83 -0
- package/src/core/module-emitter/header.ts +19 -0
- package/src/core/module-emitter/index.ts +17 -0
- package/src/core/module-emitter/namespace.ts +39 -0
- package/src/core/module-emitter/orchestrator.ts +98 -0
- package/src/core/module-emitter/separation.ts +41 -0
- package/src/core/module-emitter/static-container.ts +75 -0
- package/src/core/module-emitter.test.ts +154 -0
- package/src/core/module-emitter.ts +6 -0
- package/src/core/module-map.ts +218 -0
- package/src/core/options.ts +16 -0
- package/src/core/type-params.ts +34 -0
- package/src/emitter-types/context.ts +91 -0
- package/src/emitter-types/core.ts +138 -0
- package/src/emitter-types/csharp-types.ts +42 -0
- package/src/emitter-types/formatting.ts +13 -0
- package/src/emitter-types/fqn.ts +81 -0
- package/src/emitter-types/index.ts +31 -0
- package/src/emitter.ts +107 -0
- package/src/expression-emitter.ts +112 -0
- package/src/expressions/access.ts +104 -0
- package/src/expressions/calls.ts +264 -0
- package/src/expressions/collections.ts +354 -0
- package/src/expressions/functions.ts +71 -0
- package/src/expressions/identifiers.ts +125 -0
- package/src/expressions/index.test.ts +515 -0
- package/src/expressions/index.ts +38 -0
- package/src/expressions/literals.ts +138 -0
- package/src/expressions/operators.ts +211 -0
- package/src/expressions/other.ts +63 -0
- package/src/generator-exchange.ts +120 -0
- package/src/generator.test.ts +193 -0
- package/src/golden-tests/config-parser.ts +67 -0
- package/src/golden-tests/discovery.ts +61 -0
- package/src/golden-tests/index.ts +10 -0
- package/src/golden-tests/registration.ts +26 -0
- package/src/golden-tests/runner.ts +131 -0
- package/src/golden-tests/tree-builder.ts +43 -0
- package/src/golden-tests/types.ts +21 -0
- package/src/golden.test.ts +40 -0
- package/src/hierarchical-bindings.test.ts +258 -0
- package/src/index.ts +14 -0
- package/src/integration.test.ts +303 -0
- package/src/specialization/call-site-rewriting.test.ts +99 -0
- package/src/specialization/collection/expressions.ts +184 -0
- package/src/specialization/collection/index.ts +7 -0
- package/src/specialization/collection/orchestrator.ts +25 -0
- package/src/specialization/collection/statements.ts +91 -0
- package/src/specialization/collection.ts +10 -0
- package/src/specialization/generation.ts +189 -0
- package/src/specialization/generic-classes.test.ts +59 -0
- package/src/specialization/generic-functions.test.ts +292 -0
- package/src/specialization/helpers.ts +39 -0
- package/src/specialization/index.ts +28 -0
- package/src/specialization/interfaces.test.ts +151 -0
- package/src/specialization/naming.ts +34 -0
- package/src/specialization/substitution.ts +186 -0
- package/src/specialization/type-aliases.test.ts +134 -0
- package/src/specialization/types.ts +19 -0
- package/src/specialization-generator.ts +23 -0
- package/src/statement-emitter.ts +117 -0
- package/src/statements/blocks.ts +115 -0
- package/src/statements/classes/helpers.ts +9 -0
- package/src/statements/classes/index.ts +13 -0
- package/src/statements/classes/inline-types.ts +79 -0
- package/src/statements/classes/members/constructors.ts +123 -0
- package/src/statements/classes/members/index.ts +8 -0
- package/src/statements/classes/members/methods.ts +137 -0
- package/src/statements/classes/members/orchestrator.ts +33 -0
- package/src/statements/classes/members/properties.ts +62 -0
- package/src/statements/classes/members.ts +6 -0
- package/src/statements/classes/parameters.ts +69 -0
- package/src/statements/classes/properties.ts +95 -0
- package/src/statements/classes.ts +14 -0
- package/src/statements/control/conditionals.ts +134 -0
- package/src/statements/control/exceptions.ts +59 -0
- package/src/statements/control/index.ts +11 -0
- package/src/statements/control/loops.ts +250 -0
- package/src/statements/control.ts +14 -0
- package/src/statements/declarations/classes.ts +89 -0
- package/src/statements/declarations/enums.ts +32 -0
- package/src/statements/declarations/functions.ts +147 -0
- package/src/statements/declarations/index.ts +10 -0
- package/src/statements/declarations/interfaces.ts +116 -0
- package/src/statements/declarations/structs.test.ts +182 -0
- package/src/statements/declarations/type-aliases.ts +104 -0
- package/src/statements/declarations/variables.ts +159 -0
- package/src/statements/declarations.ts +13 -0
- package/src/statements/index.test.ts +258 -0
- package/src/statements/index.ts +43 -0
- package/src/type-assertion.test.ts +143 -0
- package/src/type-emitter.ts +18 -0
- package/src/types/arrays.ts +21 -0
- package/src/types/dictionaries.ts +52 -0
- package/src/types/emitter.ts +76 -0
- package/src/types/functions.ts +45 -0
- package/src/types/index.test.ts +116 -0
- package/src/types/index.ts +14 -0
- package/src/types/intersections.ts +19 -0
- package/src/types/literals.ts +26 -0
- package/src/types/objects.ts +27 -0
- package/src/types/parameters.test.ts +146 -0
- package/src/types/parameters.ts +95 -0
- package/src/types/primitives.ts +24 -0
- package/src/types/references.ts +187 -0
- package/src/types/unions.test.ts +397 -0
- package/src/types/unions.ts +62 -0
- package/src/types.ts +33 -0
- package/testcases/README.md +213 -0
- package/testcases/arrays/basic/ArrayLiteral.ts +4 -0
- package/testcases/arrays/basic/config.yaml +1 -0
- package/testcases/arrays/destructuring/ArrayDestructure.ts +4 -0
- package/testcases/arrays/destructuring/config.yaml +1 -0
- package/testcases/arrays/methods/ArrayMethods.ts +6 -0
- package/testcases/arrays/methods/config.yaml +1 -0
- package/testcases/arrays/multidimensional/MultiDimensional.ts +10 -0
- package/testcases/arrays/multidimensional/config.yaml +1 -0
- package/testcases/arrays/spread/ArraySpread.ts +3 -0
- package/testcases/arrays/spread/config.yaml +1 -0
- package/testcases/async/basic/AsyncFunction.ts +5 -0
- package/testcases/async/basic/config.yaml +1 -0
- package/testcases/classes/abstract/AbstractClasses.ts +53 -0
- package/testcases/classes/abstract/config.yaml +1 -0
- package/testcases/classes/basic/Person.ts +12 -0
- package/testcases/classes/basic/config.yaml +1 -0
- package/testcases/classes/constructor/User.ts +11 -0
- package/testcases/classes/constructor/config.yaml +1 -0
- package/testcases/classes/field-inference/Counter.ts +11 -0
- package/testcases/classes/field-inference/config.yaml +1 -0
- package/testcases/classes/inheritance/Inheritance.ts +24 -0
- package/testcases/classes/inheritance/config.yaml +1 -0
- package/testcases/classes/static-members/MathHelper.ts +12 -0
- package/testcases/classes/static-members/config.yaml +1 -0
- package/testcases/control-flow/error-handling/ErrorHandling.ts +13 -0
- package/testcases/control-flow/error-handling/config.yaml +1 -0
- package/testcases/control-flow/loops/Loops.ts +21 -0
- package/testcases/control-flow/loops/config.yaml +1 -0
- package/testcases/control-flow/switch/SwitchStatement.ts +15 -0
- package/testcases/control-flow/switch/config.yaml +1 -0
- package/testcases/edge-cases/complex-expressions/ComplexExpressions.ts +10 -0
- package/testcases/edge-cases/complex-expressions/config.yaml +1 -0
- package/testcases/edge-cases/nested-scopes/NestedScopes.ts +10 -0
- package/testcases/edge-cases/nested-scopes/config.yaml +1 -0
- package/testcases/edge-cases/shadowing/Shadowing.ts +16 -0
- package/testcases/edge-cases/shadowing/config.yaml +1 -0
- package/testcases/functions/arrow/ArrowFunction.ts +5 -0
- package/testcases/functions/arrow/config.yaml +1 -0
- package/testcases/functions/basic/Greet.ts +3 -0
- package/testcases/functions/basic/config.yaml +1 -0
- package/testcases/functions/closures/Closures.ts +11 -0
- package/testcases/functions/closures/config.yaml +1 -0
- package/testcases/functions/default-params/DefaultParams.ts +7 -0
- package/testcases/functions/default-params/config.yaml +1 -0
- package/testcases/functions/rest-params/RestParams.ts +7 -0
- package/testcases/functions/rest-params/config.yaml +1 -0
- package/testcases/functions/type-guards/TypeGuards.ts +52 -0
- package/testcases/functions/type-guards/config.yaml +1 -0
- package/testcases/operators/logical/LogicalOperators.ts +11 -0
- package/testcases/operators/logical/config.yaml +1 -0
- package/testcases/operators/nullish-coalescing/NullishCoalescing.ts +7 -0
- package/testcases/operators/nullish-coalescing/config.yaml +1 -0
- package/testcases/operators/optional-chaining/OptionalChaining.ts +15 -0
- package/testcases/operators/optional-chaining/config.yaml +1 -0
- package/testcases/real-world/advanced-generics/advanced-generics.ts +116 -0
- package/testcases/real-world/advanced-generics/config.yaml +1 -0
- package/testcases/real-world/async-ops/async-ops.ts +67 -0
- package/testcases/real-world/async-ops/config.yaml +1 -0
- package/testcases/real-world/business-logic/business-logic.ts +215 -0
- package/testcases/real-world/business-logic/config.yaml +1 -0
- package/testcases/real-world/calculator/calculator.ts +29 -0
- package/testcases/real-world/calculator/config.yaml +1 -0
- package/testcases/real-world/data-structures/config.yaml +1 -0
- package/testcases/real-world/data-structures/data-structures.ts +133 -0
- package/testcases/real-world/functional/config.yaml +1 -0
- package/testcases/real-world/functional/functional.ts +116 -0
- package/testcases/real-world/shapes/config.yaml +1 -0
- package/testcases/real-world/shapes/shapes.ts +87 -0
- package/testcases/real-world/string-utils/config.yaml +1 -0
- package/testcases/real-world/string-utils/string-utils.ts +47 -0
- package/testcases/real-world/todo-list/config.yaml +1 -0
- package/testcases/real-world/todo-list/todo-list.ts +52 -0
- package/testcases/real-world/type-guards/config.yaml +1 -0
- package/testcases/real-world/type-guards/type-guards.ts +71 -0
- package/testcases/structs/basic/Point.ts +9 -0
- package/testcases/structs/basic/config.yaml +1 -0
- package/testcases/types/conditional/ConditionalTypes.ts +35 -0
- package/testcases/types/conditional/config.yaml +1 -0
- package/testcases/types/constants/ModuleConstants.ts +6 -0
- package/testcases/types/constants/config.yaml +1 -0
- package/testcases/types/generics/Generics.ts +15 -0
- package/testcases/types/generics/config.yaml +1 -0
- package/testcases/types/interfaces/Interfaces.ts +14 -0
- package/testcases/types/interfaces/config.yaml +1 -0
- package/testcases/types/mapped/MappedTypes.ts +27 -0
- package/testcases/types/mapped/config.yaml +1 -0
- package/testcases/types/tuples-intersections/TuplesAndIntersections.ts +46 -0
- package/testcases/types/tuples-intersections/config.yaml +1 -0
- package/testcases/types/unions/UnionTypes.ts +11 -0
- package/testcases/types/unions/config.yaml +1 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type parameter substitution for specialization
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { IrType, IrStatement, IrExpression } from "@tsonic/frontend";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Substitute type parameters in an IR type
|
|
9
|
+
*/
|
|
10
|
+
export const substituteType = (
|
|
11
|
+
type: IrType,
|
|
12
|
+
substitutions: Map<string, IrType>
|
|
13
|
+
): IrType => {
|
|
14
|
+
switch (type.kind) {
|
|
15
|
+
case "referenceType":
|
|
16
|
+
// Check if this is a type parameter that needs substitution
|
|
17
|
+
if (substitutions.has(type.name)) {
|
|
18
|
+
const substituted = substitutions.get(type.name);
|
|
19
|
+
if (substituted) {
|
|
20
|
+
return substituted;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Recursively substitute in type arguments
|
|
25
|
+
if (type.typeArguments && type.typeArguments.length > 0) {
|
|
26
|
+
return {
|
|
27
|
+
...type,
|
|
28
|
+
typeArguments: type.typeArguments.map((arg) =>
|
|
29
|
+
substituteType(arg, substitutions)
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return type;
|
|
35
|
+
|
|
36
|
+
case "arrayType":
|
|
37
|
+
return {
|
|
38
|
+
...type,
|
|
39
|
+
elementType: substituteType(type.elementType, substitutions),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
case "functionType":
|
|
43
|
+
return {
|
|
44
|
+
...type,
|
|
45
|
+
parameters: type.parameters.map((param) => {
|
|
46
|
+
const substitutedType = param.type
|
|
47
|
+
? substituteType(param.type, substitutions)
|
|
48
|
+
: param.type;
|
|
49
|
+
return {
|
|
50
|
+
...param,
|
|
51
|
+
type: substitutedType,
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
54
|
+
returnType: type.returnType
|
|
55
|
+
? substituteType(type.returnType, substitutions)
|
|
56
|
+
: type.returnType,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
case "unionType":
|
|
60
|
+
return {
|
|
61
|
+
...type,
|
|
62
|
+
types: type.types.map((t) => substituteType(t, substitutions)),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
case "intersectionType":
|
|
66
|
+
return {
|
|
67
|
+
...type,
|
|
68
|
+
types: type.types.map((t) => substituteType(t, substitutions)),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Primitive types, literal types, etc. don't need substitution
|
|
72
|
+
default:
|
|
73
|
+
return type;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Substitute type parameters in a statement
|
|
79
|
+
*/
|
|
80
|
+
export const substituteStatement = (
|
|
81
|
+
stmt: IrStatement,
|
|
82
|
+
substitutions: Map<string, IrType>
|
|
83
|
+
): IrStatement => {
|
|
84
|
+
switch (stmt.kind) {
|
|
85
|
+
case "blockStatement":
|
|
86
|
+
return {
|
|
87
|
+
...stmt,
|
|
88
|
+
statements: stmt.statements.map((s) =>
|
|
89
|
+
substituteStatement(s, substitutions)
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
case "returnStatement":
|
|
94
|
+
return {
|
|
95
|
+
...stmt,
|
|
96
|
+
expression: stmt.expression
|
|
97
|
+
? substituteExpression(stmt.expression, substitutions)
|
|
98
|
+
: undefined,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
case "expressionStatement":
|
|
102
|
+
return {
|
|
103
|
+
...stmt,
|
|
104
|
+
expression: substituteExpression(stmt.expression, substitutions),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
case "variableDeclaration":
|
|
108
|
+
return {
|
|
109
|
+
...stmt,
|
|
110
|
+
declarations: stmt.declarations.map((decl) => ({
|
|
111
|
+
...decl,
|
|
112
|
+
initializer: decl.initializer
|
|
113
|
+
? substituteExpression(decl.initializer, substitutions)
|
|
114
|
+
: undefined,
|
|
115
|
+
})),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
case "ifStatement":
|
|
119
|
+
return {
|
|
120
|
+
...stmt,
|
|
121
|
+
condition: substituteExpression(stmt.condition, substitutions),
|
|
122
|
+
thenStatement: substituteStatement(stmt.thenStatement, substitutions),
|
|
123
|
+
elseStatement: stmt.elseStatement
|
|
124
|
+
? substituteStatement(stmt.elseStatement, substitutions)
|
|
125
|
+
: undefined,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// For other statement types, return as-is for now
|
|
129
|
+
default:
|
|
130
|
+
return stmt;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Substitute type parameters in an expression
|
|
136
|
+
*/
|
|
137
|
+
export const substituteExpression = (
|
|
138
|
+
expr: IrExpression,
|
|
139
|
+
substitutions: Map<string, IrType>
|
|
140
|
+
): IrExpression => {
|
|
141
|
+
switch (expr.kind) {
|
|
142
|
+
case "call":
|
|
143
|
+
return {
|
|
144
|
+
...expr,
|
|
145
|
+
callee: substituteExpression(expr.callee, substitutions),
|
|
146
|
+
arguments: expr.arguments.map((arg) =>
|
|
147
|
+
arg.kind === "spread"
|
|
148
|
+
? {
|
|
149
|
+
...arg,
|
|
150
|
+
expression: substituteExpression(arg.expression, substitutions),
|
|
151
|
+
}
|
|
152
|
+
: substituteExpression(arg, substitutions)
|
|
153
|
+
),
|
|
154
|
+
typeArguments: expr.typeArguments
|
|
155
|
+
? expr.typeArguments.map((arg) => substituteType(arg, substitutions))
|
|
156
|
+
: undefined,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
case "memberAccess":
|
|
160
|
+
return {
|
|
161
|
+
...expr,
|
|
162
|
+
object: substituteExpression(expr.object, substitutions),
|
|
163
|
+
property:
|
|
164
|
+
typeof expr.property === "string"
|
|
165
|
+
? expr.property
|
|
166
|
+
: substituteExpression(expr.property, substitutions),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
case "binary":
|
|
170
|
+
return {
|
|
171
|
+
...expr,
|
|
172
|
+
left: substituteExpression(expr.left, substitutions),
|
|
173
|
+
right: substituteExpression(expr.right, substitutions),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
case "unary":
|
|
177
|
+
return {
|
|
178
|
+
...expr,
|
|
179
|
+
expression: substituteExpression(expr.expression, substitutions),
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// For other expressions, return as-is
|
|
183
|
+
default:
|
|
184
|
+
return expr;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Type Aliases
|
|
3
|
+
* Covers spec/16-types-and-interfaces.md §3 - Type Aliases
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it } from "mocha";
|
|
7
|
+
import { expect } from "chai";
|
|
8
|
+
import { emitModule } from "../emitter.js";
|
|
9
|
+
import { IrModule } from "@tsonic/frontend";
|
|
10
|
+
|
|
11
|
+
describe("Type Aliases (spec/16 §3)", () => {
|
|
12
|
+
it("should emit structural type alias as sealed class with __Alias suffix", () => {
|
|
13
|
+
const module: IrModule = {
|
|
14
|
+
kind: "module",
|
|
15
|
+
filePath: "/src/types.ts",
|
|
16
|
+
namespace: "MyApp",
|
|
17
|
+
className: "types",
|
|
18
|
+
isStaticContainer: true,
|
|
19
|
+
imports: [],
|
|
20
|
+
body: [
|
|
21
|
+
{
|
|
22
|
+
kind: "typeAliasDeclaration",
|
|
23
|
+
name: "Point",
|
|
24
|
+
typeParameters: undefined,
|
|
25
|
+
type: {
|
|
26
|
+
kind: "objectType",
|
|
27
|
+
members: [
|
|
28
|
+
{
|
|
29
|
+
kind: "propertySignature",
|
|
30
|
+
name: "x",
|
|
31
|
+
type: { kind: "primitiveType", name: "number" },
|
|
32
|
+
isOptional: false,
|
|
33
|
+
isReadonly: false,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
kind: "propertySignature",
|
|
37
|
+
name: "y",
|
|
38
|
+
type: { kind: "primitiveType", name: "number" },
|
|
39
|
+
isOptional: false,
|
|
40
|
+
isReadonly: false,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
isStruct: false,
|
|
45
|
+
isExported: true,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
exports: [],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const result = emitModule(module);
|
|
52
|
+
|
|
53
|
+
expect(result).to.include("public sealed class Point__Alias");
|
|
54
|
+
expect(result).to.include("public double x { get; set; }");
|
|
55
|
+
expect(result).to.include("public double y { get; set; }");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should emit non-structural type alias as comment", () => {
|
|
59
|
+
const module: IrModule = {
|
|
60
|
+
kind: "module",
|
|
61
|
+
filePath: "/src/types.ts",
|
|
62
|
+
namespace: "MyApp",
|
|
63
|
+
className: "types",
|
|
64
|
+
isStaticContainer: true,
|
|
65
|
+
imports: [],
|
|
66
|
+
body: [
|
|
67
|
+
{
|
|
68
|
+
kind: "typeAliasDeclaration",
|
|
69
|
+
name: "ID",
|
|
70
|
+
typeParameters: undefined,
|
|
71
|
+
type: { kind: "primitiveType", name: "number" },
|
|
72
|
+
isStruct: false,
|
|
73
|
+
isExported: true,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
exports: [],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const result = emitModule(module);
|
|
80
|
+
|
|
81
|
+
expect(result).to.include("// type ID = double");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should emit recursive type alias with self-reference", () => {
|
|
85
|
+
const module: IrModule = {
|
|
86
|
+
kind: "module",
|
|
87
|
+
filePath: "/src/types.ts",
|
|
88
|
+
namespace: "MyApp",
|
|
89
|
+
className: "types",
|
|
90
|
+
isStaticContainer: true,
|
|
91
|
+
imports: [],
|
|
92
|
+
body: [
|
|
93
|
+
{
|
|
94
|
+
kind: "typeAliasDeclaration",
|
|
95
|
+
name: "Node",
|
|
96
|
+
typeParameters: undefined,
|
|
97
|
+
type: {
|
|
98
|
+
kind: "objectType",
|
|
99
|
+
members: [
|
|
100
|
+
{
|
|
101
|
+
kind: "propertySignature",
|
|
102
|
+
name: "name",
|
|
103
|
+
type: { kind: "primitiveType", name: "string" },
|
|
104
|
+
isOptional: false,
|
|
105
|
+
isReadonly: false,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
kind: "propertySignature",
|
|
109
|
+
name: "next",
|
|
110
|
+
type: {
|
|
111
|
+
kind: "referenceType",
|
|
112
|
+
name: "Node",
|
|
113
|
+
typeArguments: [],
|
|
114
|
+
},
|
|
115
|
+
isOptional: true,
|
|
116
|
+
isReadonly: false,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
isStruct: false,
|
|
121
|
+
isExported: true,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
exports: [],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const result = emitModule(module);
|
|
128
|
+
|
|
129
|
+
expect(result).to.include("public sealed class Node__Alias");
|
|
130
|
+
expect(result).to.include("public string name { get; set; } = default!;");
|
|
131
|
+
// Self-reference should be nullable
|
|
132
|
+
expect(result).to.include("public Node? next { get; set; } = default!;");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for specialization system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
IrFunctionDeclaration,
|
|
7
|
+
IrClassDeclaration,
|
|
8
|
+
IrType,
|
|
9
|
+
} from "@tsonic/frontend";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Specialization request - tracks a function/class that needs a specialized version
|
|
13
|
+
*/
|
|
14
|
+
export type SpecializationRequest = {
|
|
15
|
+
readonly kind: "function" | "class";
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly typeArguments: readonly IrType[];
|
|
18
|
+
readonly declaration: IrFunctionDeclaration | IrClassDeclaration;
|
|
19
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specialization Generator - Generate monomorphized versions of generic declarations
|
|
3
|
+
* Per spec/15-generics.md §5-6 - Monomorphisation
|
|
4
|
+
* Main dispatcher - delegates to specialized modules
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Re-export everything from specialization modules for backward compatibility
|
|
8
|
+
export type { SpecializationRequest } from "./specialization/types.js";
|
|
9
|
+
export { collectSpecializations } from "./specialization/collection.js";
|
|
10
|
+
export { generateSpecializations } from "./specialization/generation.js";
|
|
11
|
+
export {
|
|
12
|
+
generateSpecializedFunctionName,
|
|
13
|
+
generateSpecializedClassName,
|
|
14
|
+
} from "./specialization/naming.js";
|
|
15
|
+
export {
|
|
16
|
+
createSpecializationKey,
|
|
17
|
+
serializeType,
|
|
18
|
+
} from "./specialization/helpers.js";
|
|
19
|
+
export {
|
|
20
|
+
substituteType,
|
|
21
|
+
substituteStatement,
|
|
22
|
+
substituteExpression,
|
|
23
|
+
} from "./specialization/substitution.js";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statement Emitter - IR statements to C# code
|
|
3
|
+
* Main dispatcher - delegates to specialized modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IrStatement } from "@tsonic/frontend";
|
|
7
|
+
import { EmitterContext, getIndent } from "./types.js";
|
|
8
|
+
|
|
9
|
+
// Import statement emitters from specialized modules
|
|
10
|
+
import {
|
|
11
|
+
emitVariableDeclaration,
|
|
12
|
+
emitFunctionDeclaration,
|
|
13
|
+
emitClassDeclaration,
|
|
14
|
+
emitInterfaceDeclaration,
|
|
15
|
+
emitEnumDeclaration,
|
|
16
|
+
emitTypeAliasDeclaration,
|
|
17
|
+
} from "./statements/declarations.js";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
emitBlockStatement,
|
|
21
|
+
emitReturnStatement,
|
|
22
|
+
emitExpressionStatement,
|
|
23
|
+
} from "./statements/blocks.js";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
emitIfStatement,
|
|
27
|
+
emitWhileStatement,
|
|
28
|
+
emitForStatement,
|
|
29
|
+
emitForOfStatement,
|
|
30
|
+
emitSwitchStatement,
|
|
31
|
+
emitTryStatement,
|
|
32
|
+
emitThrowStatement,
|
|
33
|
+
} from "./statements/control.js";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Emit a C# statement from an IR statement
|
|
37
|
+
*/
|
|
38
|
+
export const emitStatement = (
|
|
39
|
+
stmt: IrStatement,
|
|
40
|
+
context: EmitterContext
|
|
41
|
+
): [string, EmitterContext] => {
|
|
42
|
+
const ind = getIndent(context);
|
|
43
|
+
|
|
44
|
+
switch (stmt.kind) {
|
|
45
|
+
case "variableDeclaration":
|
|
46
|
+
return emitVariableDeclaration(stmt, context);
|
|
47
|
+
|
|
48
|
+
case "functionDeclaration":
|
|
49
|
+
return emitFunctionDeclaration(stmt, context);
|
|
50
|
+
|
|
51
|
+
case "classDeclaration":
|
|
52
|
+
return emitClassDeclaration(stmt, context);
|
|
53
|
+
|
|
54
|
+
case "interfaceDeclaration":
|
|
55
|
+
return emitInterfaceDeclaration(stmt, context);
|
|
56
|
+
|
|
57
|
+
case "enumDeclaration":
|
|
58
|
+
return emitEnumDeclaration(stmt, context);
|
|
59
|
+
|
|
60
|
+
case "typeAliasDeclaration":
|
|
61
|
+
return emitTypeAliasDeclaration(stmt, context);
|
|
62
|
+
|
|
63
|
+
case "blockStatement":
|
|
64
|
+
return emitBlockStatement(stmt, context);
|
|
65
|
+
|
|
66
|
+
case "ifStatement":
|
|
67
|
+
return emitIfStatement(stmt, context);
|
|
68
|
+
|
|
69
|
+
case "whileStatement":
|
|
70
|
+
return emitWhileStatement(stmt, context);
|
|
71
|
+
|
|
72
|
+
// Note: doWhileStatement not in current IR types
|
|
73
|
+
// case "doWhileStatement":
|
|
74
|
+
// return emitDoWhileStatement(stmt, context);
|
|
75
|
+
|
|
76
|
+
case "forStatement":
|
|
77
|
+
return emitForStatement(stmt, context);
|
|
78
|
+
|
|
79
|
+
case "forOfStatement":
|
|
80
|
+
return emitForOfStatement(stmt, context);
|
|
81
|
+
|
|
82
|
+
// Note: forInStatement not in current IR types
|
|
83
|
+
// case "forInStatement":
|
|
84
|
+
// return emitForInStatement(stmt, context);
|
|
85
|
+
|
|
86
|
+
case "switchStatement":
|
|
87
|
+
return emitSwitchStatement(stmt, context);
|
|
88
|
+
|
|
89
|
+
case "tryStatement":
|
|
90
|
+
return emitTryStatement(stmt, context);
|
|
91
|
+
|
|
92
|
+
case "throwStatement":
|
|
93
|
+
return emitThrowStatement(stmt, context);
|
|
94
|
+
|
|
95
|
+
case "returnStatement":
|
|
96
|
+
return emitReturnStatement(stmt, context);
|
|
97
|
+
|
|
98
|
+
case "breakStatement":
|
|
99
|
+
return [`${ind}break;`, context];
|
|
100
|
+
|
|
101
|
+
case "continueStatement":
|
|
102
|
+
return [`${ind}continue;`, context];
|
|
103
|
+
|
|
104
|
+
case "expressionStatement":
|
|
105
|
+
return emitExpressionStatement(stmt, context);
|
|
106
|
+
|
|
107
|
+
case "emptyStatement":
|
|
108
|
+
return [`${ind};`, context];
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
return [`${ind}// TODO: unhandled statement`, context];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Re-export commonly used functions for backward compatibility
|
|
116
|
+
export { emitBlockStatement } from "./statements/blocks.js";
|
|
117
|
+
export { emitParameters } from "./statements/classes.js";
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block and simple statement emitters
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { IrStatement, IrExpression } from "@tsonic/frontend";
|
|
6
|
+
import { EmitterContext, getIndent } from "../types.js";
|
|
7
|
+
import { emitExpression } from "../expression-emitter.js";
|
|
8
|
+
import { emitStatement } from "../statement-emitter.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Emit a block statement
|
|
12
|
+
*/
|
|
13
|
+
export const emitBlockStatement = (
|
|
14
|
+
stmt: Extract<IrStatement, { kind: "blockStatement" }>,
|
|
15
|
+
context: EmitterContext
|
|
16
|
+
): [string, EmitterContext] => {
|
|
17
|
+
const ind = getIndent(context);
|
|
18
|
+
let currentContext = context;
|
|
19
|
+
const statements: string[] = [];
|
|
20
|
+
|
|
21
|
+
for (const s of stmt.statements) {
|
|
22
|
+
const [code, newContext] = emitStatement(s, currentContext);
|
|
23
|
+
statements.push(code);
|
|
24
|
+
currentContext = newContext;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const bodyCode = statements.join("\n");
|
|
28
|
+
return [`${ind}{\n${bodyCode}\n${ind}}`, currentContext];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Emit a return statement
|
|
33
|
+
*/
|
|
34
|
+
export const emitReturnStatement = (
|
|
35
|
+
stmt: Extract<IrStatement, { kind: "returnStatement" }>,
|
|
36
|
+
context: EmitterContext
|
|
37
|
+
): [string, EmitterContext] => {
|
|
38
|
+
const ind = getIndent(context);
|
|
39
|
+
|
|
40
|
+
if (stmt.expression) {
|
|
41
|
+
const [exprFrag, newContext] = emitExpression(stmt.expression, context);
|
|
42
|
+
return [`${ind}return ${exprFrag.text};`, newContext];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [`${ind}return;`, context];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Emit yield expression as C# yield return with exchange object pattern
|
|
50
|
+
*
|
|
51
|
+
* TypeScript: yield value
|
|
52
|
+
* C#:
|
|
53
|
+
* exchange.Output = value;
|
|
54
|
+
* yield return exchange;
|
|
55
|
+
*
|
|
56
|
+
* TypeScript: yield* otherGenerator()
|
|
57
|
+
* C#:
|
|
58
|
+
* foreach (var item in OtherGenerator())
|
|
59
|
+
* yield return item;
|
|
60
|
+
*/
|
|
61
|
+
export const emitYieldStatement = (
|
|
62
|
+
expr: Extract<IrExpression, { kind: "yield" }>,
|
|
63
|
+
context: EmitterContext
|
|
64
|
+
): [string, EmitterContext] => {
|
|
65
|
+
const ind = getIndent(context);
|
|
66
|
+
let currentContext = context;
|
|
67
|
+
const parts: string[] = [];
|
|
68
|
+
|
|
69
|
+
if (expr.delegate) {
|
|
70
|
+
// yield* delegation
|
|
71
|
+
if (expr.expression) {
|
|
72
|
+
const [delegateFrag, newContext] = emitExpression(
|
|
73
|
+
expr.expression,
|
|
74
|
+
currentContext
|
|
75
|
+
);
|
|
76
|
+
currentContext = newContext;
|
|
77
|
+
parts.push(`${ind}foreach (var item in ${delegateFrag.text})`);
|
|
78
|
+
parts.push(`${ind} yield return item;`);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// Regular yield
|
|
82
|
+
if (expr.expression) {
|
|
83
|
+
const [valueFrag, newContext] = emitExpression(
|
|
84
|
+
expr.expression,
|
|
85
|
+
currentContext
|
|
86
|
+
);
|
|
87
|
+
currentContext = newContext;
|
|
88
|
+
parts.push(`${ind}exchange.Output = ${valueFrag.text};`);
|
|
89
|
+
parts.push(`${ind}yield return exchange;`);
|
|
90
|
+
} else {
|
|
91
|
+
// Bare yield (no value)
|
|
92
|
+
parts.push(`${ind}yield return exchange;`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return [parts.join("\n"), currentContext];
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Emit an expression statement
|
|
101
|
+
*/
|
|
102
|
+
export const emitExpressionStatement = (
|
|
103
|
+
stmt: Extract<IrStatement, { kind: "expressionStatement" }>,
|
|
104
|
+
context: EmitterContext
|
|
105
|
+
): [string, EmitterContext] => {
|
|
106
|
+
const ind = getIndent(context);
|
|
107
|
+
|
|
108
|
+
// Special handling for yield expressions in generators
|
|
109
|
+
if (stmt.expression.kind === "yield") {
|
|
110
|
+
return emitYieldStatement(stmt.expression, context);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const [exprFrag, newContext] = emitExpression(stmt.expression, context);
|
|
114
|
+
return [`${ind}${exprFrag.text};`, newContext];
|
|
115
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class-related helpers - Public API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { capitalize } from "./helpers.js";
|
|
6
|
+
export { emitParameters } from "./parameters.js";
|
|
7
|
+
export { emitClassMember } from "./members.js";
|
|
8
|
+
export { emitInterfaceMemberAsProperty } from "./properties.js";
|
|
9
|
+
export {
|
|
10
|
+
extractInlineObjectTypes,
|
|
11
|
+
emitExtractedType,
|
|
12
|
+
type ExtractedType,
|
|
13
|
+
} from "./inline-types.js";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline object type extraction and emission
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { IrInterfaceMember } from "@tsonic/frontend";
|
|
6
|
+
import { EmitterContext, getIndent, indent } from "../../types.js";
|
|
7
|
+
import { capitalize } from "./helpers.js";
|
|
8
|
+
import { emitInterfaceMemberAsProperty } from "./properties.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Extracted inline object type
|
|
12
|
+
*/
|
|
13
|
+
export type ExtractedType = {
|
|
14
|
+
readonly className: string;
|
|
15
|
+
readonly members: readonly IrInterfaceMember[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extract inline object types from interface members and generate class declarations
|
|
20
|
+
*/
|
|
21
|
+
export const extractInlineObjectTypes = (
|
|
22
|
+
members: readonly IrInterfaceMember[]
|
|
23
|
+
): readonly ExtractedType[] => {
|
|
24
|
+
const extracted: ExtractedType[] = [];
|
|
25
|
+
|
|
26
|
+
for (const member of members) {
|
|
27
|
+
if (
|
|
28
|
+
member.kind === "propertySignature" &&
|
|
29
|
+
member.type?.kind === "objectType"
|
|
30
|
+
) {
|
|
31
|
+
// Generate class name from property name (capitalize)
|
|
32
|
+
const className = capitalize(member.name);
|
|
33
|
+
extracted.push({
|
|
34
|
+
className,
|
|
35
|
+
members: member.type.members,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return extracted;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Emit an extracted inline object type as a class
|
|
45
|
+
*/
|
|
46
|
+
export const emitExtractedType = (
|
|
47
|
+
extracted: ExtractedType,
|
|
48
|
+
context: EmitterContext
|
|
49
|
+
): [string, EmitterContext] => {
|
|
50
|
+
const ind = getIndent(context);
|
|
51
|
+
let currentContext = context;
|
|
52
|
+
|
|
53
|
+
const parts: string[] = [];
|
|
54
|
+
parts.push(`${ind}public class ${extracted.className}`);
|
|
55
|
+
parts.push(`${ind}{`);
|
|
56
|
+
|
|
57
|
+
// Emit properties
|
|
58
|
+
const bodyContext = indent(currentContext);
|
|
59
|
+
const propertyParts: string[] = [];
|
|
60
|
+
let bodyCurrentContext = bodyContext;
|
|
61
|
+
|
|
62
|
+
for (const member of extracted.members) {
|
|
63
|
+
const [memberCode, newContext] = emitInterfaceMemberAsProperty(
|
|
64
|
+
member,
|
|
65
|
+
bodyCurrentContext
|
|
66
|
+
);
|
|
67
|
+
propertyParts.push(memberCode);
|
|
68
|
+
bodyCurrentContext = newContext;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (propertyParts.length > 0) {
|
|
72
|
+
parts.push(propertyParts.join("\n"));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
parts.push(`${ind}}`);
|
|
76
|
+
|
|
77
|
+
// Return context at original indent level
|
|
78
|
+
return [parts.join("\n"), context];
|
|
79
|
+
};
|