@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.
Files changed (209) hide show
  1. package/package.json +34 -0
  2. package/scripts/update-golden-tests.ts +119 -0
  3. package/src/adapter-generator.ts +112 -0
  4. package/src/array.test.ts +301 -0
  5. package/src/constants.ts +32 -0
  6. package/src/core/exports.ts +36 -0
  7. package/src/core/imports.test.ts +83 -0
  8. package/src/core/imports.ts +243 -0
  9. package/src/core/index.ts +9 -0
  10. package/src/core/module-emitter/assembly.ts +83 -0
  11. package/src/core/module-emitter/header.ts +19 -0
  12. package/src/core/module-emitter/index.ts +17 -0
  13. package/src/core/module-emitter/namespace.ts +39 -0
  14. package/src/core/module-emitter/orchestrator.ts +98 -0
  15. package/src/core/module-emitter/separation.ts +41 -0
  16. package/src/core/module-emitter/static-container.ts +75 -0
  17. package/src/core/module-emitter.test.ts +154 -0
  18. package/src/core/module-emitter.ts +6 -0
  19. package/src/core/module-map.ts +218 -0
  20. package/src/core/options.ts +16 -0
  21. package/src/core/type-params.ts +34 -0
  22. package/src/emitter-types/context.ts +91 -0
  23. package/src/emitter-types/core.ts +138 -0
  24. package/src/emitter-types/csharp-types.ts +42 -0
  25. package/src/emitter-types/formatting.ts +13 -0
  26. package/src/emitter-types/fqn.ts +81 -0
  27. package/src/emitter-types/index.ts +31 -0
  28. package/src/emitter.ts +107 -0
  29. package/src/expression-emitter.ts +112 -0
  30. package/src/expressions/access.ts +104 -0
  31. package/src/expressions/calls.ts +264 -0
  32. package/src/expressions/collections.ts +354 -0
  33. package/src/expressions/functions.ts +71 -0
  34. package/src/expressions/identifiers.ts +125 -0
  35. package/src/expressions/index.test.ts +515 -0
  36. package/src/expressions/index.ts +38 -0
  37. package/src/expressions/literals.ts +138 -0
  38. package/src/expressions/operators.ts +211 -0
  39. package/src/expressions/other.ts +63 -0
  40. package/src/generator-exchange.ts +120 -0
  41. package/src/generator.test.ts +193 -0
  42. package/src/golden-tests/config-parser.ts +67 -0
  43. package/src/golden-tests/discovery.ts +61 -0
  44. package/src/golden-tests/index.ts +10 -0
  45. package/src/golden-tests/registration.ts +26 -0
  46. package/src/golden-tests/runner.ts +131 -0
  47. package/src/golden-tests/tree-builder.ts +43 -0
  48. package/src/golden-tests/types.ts +21 -0
  49. package/src/golden.test.ts +40 -0
  50. package/src/hierarchical-bindings.test.ts +258 -0
  51. package/src/index.ts +14 -0
  52. package/src/integration.test.ts +303 -0
  53. package/src/specialization/call-site-rewriting.test.ts +99 -0
  54. package/src/specialization/collection/expressions.ts +184 -0
  55. package/src/specialization/collection/index.ts +7 -0
  56. package/src/specialization/collection/orchestrator.ts +25 -0
  57. package/src/specialization/collection/statements.ts +91 -0
  58. package/src/specialization/collection.ts +10 -0
  59. package/src/specialization/generation.ts +189 -0
  60. package/src/specialization/generic-classes.test.ts +59 -0
  61. package/src/specialization/generic-functions.test.ts +292 -0
  62. package/src/specialization/helpers.ts +39 -0
  63. package/src/specialization/index.ts +28 -0
  64. package/src/specialization/interfaces.test.ts +151 -0
  65. package/src/specialization/naming.ts +34 -0
  66. package/src/specialization/substitution.ts +186 -0
  67. package/src/specialization/type-aliases.test.ts +134 -0
  68. package/src/specialization/types.ts +19 -0
  69. package/src/specialization-generator.ts +23 -0
  70. package/src/statement-emitter.ts +117 -0
  71. package/src/statements/blocks.ts +115 -0
  72. package/src/statements/classes/helpers.ts +9 -0
  73. package/src/statements/classes/index.ts +13 -0
  74. package/src/statements/classes/inline-types.ts +79 -0
  75. package/src/statements/classes/members/constructors.ts +123 -0
  76. package/src/statements/classes/members/index.ts +8 -0
  77. package/src/statements/classes/members/methods.ts +137 -0
  78. package/src/statements/classes/members/orchestrator.ts +33 -0
  79. package/src/statements/classes/members/properties.ts +62 -0
  80. package/src/statements/classes/members.ts +6 -0
  81. package/src/statements/classes/parameters.ts +69 -0
  82. package/src/statements/classes/properties.ts +95 -0
  83. package/src/statements/classes.ts +14 -0
  84. package/src/statements/control/conditionals.ts +134 -0
  85. package/src/statements/control/exceptions.ts +59 -0
  86. package/src/statements/control/index.ts +11 -0
  87. package/src/statements/control/loops.ts +250 -0
  88. package/src/statements/control.ts +14 -0
  89. package/src/statements/declarations/classes.ts +89 -0
  90. package/src/statements/declarations/enums.ts +32 -0
  91. package/src/statements/declarations/functions.ts +147 -0
  92. package/src/statements/declarations/index.ts +10 -0
  93. package/src/statements/declarations/interfaces.ts +116 -0
  94. package/src/statements/declarations/structs.test.ts +182 -0
  95. package/src/statements/declarations/type-aliases.ts +104 -0
  96. package/src/statements/declarations/variables.ts +159 -0
  97. package/src/statements/declarations.ts +13 -0
  98. package/src/statements/index.test.ts +258 -0
  99. package/src/statements/index.ts +43 -0
  100. package/src/type-assertion.test.ts +143 -0
  101. package/src/type-emitter.ts +18 -0
  102. package/src/types/arrays.ts +21 -0
  103. package/src/types/dictionaries.ts +52 -0
  104. package/src/types/emitter.ts +76 -0
  105. package/src/types/functions.ts +45 -0
  106. package/src/types/index.test.ts +116 -0
  107. package/src/types/index.ts +14 -0
  108. package/src/types/intersections.ts +19 -0
  109. package/src/types/literals.ts +26 -0
  110. package/src/types/objects.ts +27 -0
  111. package/src/types/parameters.test.ts +146 -0
  112. package/src/types/parameters.ts +95 -0
  113. package/src/types/primitives.ts +24 -0
  114. package/src/types/references.ts +187 -0
  115. package/src/types/unions.test.ts +397 -0
  116. package/src/types/unions.ts +62 -0
  117. package/src/types.ts +33 -0
  118. package/testcases/README.md +213 -0
  119. package/testcases/arrays/basic/ArrayLiteral.ts +4 -0
  120. package/testcases/arrays/basic/config.yaml +1 -0
  121. package/testcases/arrays/destructuring/ArrayDestructure.ts +4 -0
  122. package/testcases/arrays/destructuring/config.yaml +1 -0
  123. package/testcases/arrays/methods/ArrayMethods.ts +6 -0
  124. package/testcases/arrays/methods/config.yaml +1 -0
  125. package/testcases/arrays/multidimensional/MultiDimensional.ts +10 -0
  126. package/testcases/arrays/multidimensional/config.yaml +1 -0
  127. package/testcases/arrays/spread/ArraySpread.ts +3 -0
  128. package/testcases/arrays/spread/config.yaml +1 -0
  129. package/testcases/async/basic/AsyncFunction.ts +5 -0
  130. package/testcases/async/basic/config.yaml +1 -0
  131. package/testcases/classes/abstract/AbstractClasses.ts +53 -0
  132. package/testcases/classes/abstract/config.yaml +1 -0
  133. package/testcases/classes/basic/Person.ts +12 -0
  134. package/testcases/classes/basic/config.yaml +1 -0
  135. package/testcases/classes/constructor/User.ts +11 -0
  136. package/testcases/classes/constructor/config.yaml +1 -0
  137. package/testcases/classes/field-inference/Counter.ts +11 -0
  138. package/testcases/classes/field-inference/config.yaml +1 -0
  139. package/testcases/classes/inheritance/Inheritance.ts +24 -0
  140. package/testcases/classes/inheritance/config.yaml +1 -0
  141. package/testcases/classes/static-members/MathHelper.ts +12 -0
  142. package/testcases/classes/static-members/config.yaml +1 -0
  143. package/testcases/control-flow/error-handling/ErrorHandling.ts +13 -0
  144. package/testcases/control-flow/error-handling/config.yaml +1 -0
  145. package/testcases/control-flow/loops/Loops.ts +21 -0
  146. package/testcases/control-flow/loops/config.yaml +1 -0
  147. package/testcases/control-flow/switch/SwitchStatement.ts +15 -0
  148. package/testcases/control-flow/switch/config.yaml +1 -0
  149. package/testcases/edge-cases/complex-expressions/ComplexExpressions.ts +10 -0
  150. package/testcases/edge-cases/complex-expressions/config.yaml +1 -0
  151. package/testcases/edge-cases/nested-scopes/NestedScopes.ts +10 -0
  152. package/testcases/edge-cases/nested-scopes/config.yaml +1 -0
  153. package/testcases/edge-cases/shadowing/Shadowing.ts +16 -0
  154. package/testcases/edge-cases/shadowing/config.yaml +1 -0
  155. package/testcases/functions/arrow/ArrowFunction.ts +5 -0
  156. package/testcases/functions/arrow/config.yaml +1 -0
  157. package/testcases/functions/basic/Greet.ts +3 -0
  158. package/testcases/functions/basic/config.yaml +1 -0
  159. package/testcases/functions/closures/Closures.ts +11 -0
  160. package/testcases/functions/closures/config.yaml +1 -0
  161. package/testcases/functions/default-params/DefaultParams.ts +7 -0
  162. package/testcases/functions/default-params/config.yaml +1 -0
  163. package/testcases/functions/rest-params/RestParams.ts +7 -0
  164. package/testcases/functions/rest-params/config.yaml +1 -0
  165. package/testcases/functions/type-guards/TypeGuards.ts +52 -0
  166. package/testcases/functions/type-guards/config.yaml +1 -0
  167. package/testcases/operators/logical/LogicalOperators.ts +11 -0
  168. package/testcases/operators/logical/config.yaml +1 -0
  169. package/testcases/operators/nullish-coalescing/NullishCoalescing.ts +7 -0
  170. package/testcases/operators/nullish-coalescing/config.yaml +1 -0
  171. package/testcases/operators/optional-chaining/OptionalChaining.ts +15 -0
  172. package/testcases/operators/optional-chaining/config.yaml +1 -0
  173. package/testcases/real-world/advanced-generics/advanced-generics.ts +116 -0
  174. package/testcases/real-world/advanced-generics/config.yaml +1 -0
  175. package/testcases/real-world/async-ops/async-ops.ts +67 -0
  176. package/testcases/real-world/async-ops/config.yaml +1 -0
  177. package/testcases/real-world/business-logic/business-logic.ts +215 -0
  178. package/testcases/real-world/business-logic/config.yaml +1 -0
  179. package/testcases/real-world/calculator/calculator.ts +29 -0
  180. package/testcases/real-world/calculator/config.yaml +1 -0
  181. package/testcases/real-world/data-structures/config.yaml +1 -0
  182. package/testcases/real-world/data-structures/data-structures.ts +133 -0
  183. package/testcases/real-world/functional/config.yaml +1 -0
  184. package/testcases/real-world/functional/functional.ts +116 -0
  185. package/testcases/real-world/shapes/config.yaml +1 -0
  186. package/testcases/real-world/shapes/shapes.ts +87 -0
  187. package/testcases/real-world/string-utils/config.yaml +1 -0
  188. package/testcases/real-world/string-utils/string-utils.ts +47 -0
  189. package/testcases/real-world/todo-list/config.yaml +1 -0
  190. package/testcases/real-world/todo-list/todo-list.ts +52 -0
  191. package/testcases/real-world/type-guards/config.yaml +1 -0
  192. package/testcases/real-world/type-guards/type-guards.ts +71 -0
  193. package/testcases/structs/basic/Point.ts +9 -0
  194. package/testcases/structs/basic/config.yaml +1 -0
  195. package/testcases/types/conditional/ConditionalTypes.ts +35 -0
  196. package/testcases/types/conditional/config.yaml +1 -0
  197. package/testcases/types/constants/ModuleConstants.ts +6 -0
  198. package/testcases/types/constants/config.yaml +1 -0
  199. package/testcases/types/generics/Generics.ts +15 -0
  200. package/testcases/types/generics/config.yaml +1 -0
  201. package/testcases/types/interfaces/Interfaces.ts +14 -0
  202. package/testcases/types/interfaces/config.yaml +1 -0
  203. package/testcases/types/mapped/MappedTypes.ts +27 -0
  204. package/testcases/types/mapped/config.yaml +1 -0
  205. package/testcases/types/tuples-intersections/TuplesAndIntersections.ts +46 -0
  206. package/testcases/types/tuples-intersections/config.yaml +1 -0
  207. package/testcases/types/unions/UnionTypes.ts +11 -0
  208. package/testcases/types/unions/config.yaml +1 -0
  209. package/tsconfig.json +14 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Specialization collection from expressions
3
+ */
4
+
5
+ import {
6
+ IrModule,
7
+ IrExpression,
8
+ IrFunctionDeclaration,
9
+ IrClassDeclaration,
10
+ } from "@tsonic/frontend";
11
+ import { SpecializationRequest } from "../types.js";
12
+ import { createSpecializationKey } from "../helpers.js";
13
+ import { collectFromStatement } from "./statements.js";
14
+
15
+ /**
16
+ * Collect specializations from an expression
17
+ */
18
+ export const collectFromExpression = (
19
+ expr: IrExpression,
20
+ requests: SpecializationRequest[],
21
+ seen: Set<string>,
22
+ module: IrModule
23
+ ): void => {
24
+ switch (expr.kind) {
25
+ case "call":
26
+ // Check if this call requires specialization
27
+ if (
28
+ expr.requiresSpecialization &&
29
+ expr.typeArguments &&
30
+ expr.typeArguments.length > 0
31
+ ) {
32
+ // Get function name from callee
33
+ if (expr.callee.kind === "identifier") {
34
+ const funcName = expr.callee.name;
35
+ const key = createSpecializationKey(funcName, expr.typeArguments);
36
+
37
+ if (!seen.has(key)) {
38
+ seen.add(key);
39
+
40
+ // Find the function declaration in the module
41
+ const funcDecl = module.body.find(
42
+ (stmt) =>
43
+ stmt.kind === "functionDeclaration" && stmt.name === funcName
44
+ ) as IrFunctionDeclaration | undefined;
45
+
46
+ if (funcDecl) {
47
+ requests.push({
48
+ kind: "function",
49
+ name: funcName,
50
+ typeArguments: expr.typeArguments,
51
+ declaration: funcDecl,
52
+ });
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ // Recurse into callee and arguments
59
+ collectFromExpression(expr.callee, requests, seen, module);
60
+ for (const arg of expr.arguments) {
61
+ if (arg.kind !== "spread") {
62
+ collectFromExpression(arg, requests, seen, module);
63
+ }
64
+ }
65
+ break;
66
+
67
+ case "new":
68
+ // Check if this constructor call requires specialization
69
+ if (
70
+ expr.requiresSpecialization &&
71
+ expr.typeArguments &&
72
+ expr.typeArguments.length > 0
73
+ ) {
74
+ if (expr.callee.kind === "identifier") {
75
+ const className = expr.callee.name;
76
+ const key = createSpecializationKey(className, expr.typeArguments);
77
+
78
+ if (!seen.has(key)) {
79
+ seen.add(key);
80
+
81
+ // Find the class declaration in the module
82
+ const classDecl = module.body.find(
83
+ (stmt) =>
84
+ stmt.kind === "classDeclaration" && stmt.name === className
85
+ ) as IrClassDeclaration | undefined;
86
+
87
+ if (classDecl) {
88
+ requests.push({
89
+ kind: "class",
90
+ name: className,
91
+ typeArguments: expr.typeArguments,
92
+ declaration: classDecl,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ // Recurse into callee and arguments
100
+ collectFromExpression(expr.callee, requests, seen, module);
101
+ for (const arg of expr.arguments) {
102
+ if (arg.kind !== "spread") {
103
+ collectFromExpression(arg, requests, seen, module);
104
+ }
105
+ }
106
+ break;
107
+
108
+ case "binary":
109
+ case "logical":
110
+ collectFromExpression(expr.left, requests, seen, module);
111
+ collectFromExpression(expr.right, requests, seen, module);
112
+ break;
113
+
114
+ case "unary":
115
+ case "update":
116
+ case "await":
117
+ collectFromExpression(expr.expression, requests, seen, module);
118
+ break;
119
+
120
+ case "assignment":
121
+ if ("kind" in expr.left) {
122
+ collectFromExpression(
123
+ expr.left as IrExpression,
124
+ requests,
125
+ seen,
126
+ module
127
+ );
128
+ }
129
+ collectFromExpression(expr.right, requests, seen, module);
130
+ break;
131
+
132
+ case "conditional":
133
+ collectFromExpression(expr.condition, requests, seen, module);
134
+ collectFromExpression(expr.whenTrue, requests, seen, module);
135
+ collectFromExpression(expr.whenFalse, requests, seen, module);
136
+ break;
137
+
138
+ case "memberAccess":
139
+ collectFromExpression(expr.object, requests, seen, module);
140
+ if (typeof expr.property !== "string") {
141
+ collectFromExpression(expr.property, requests, seen, module);
142
+ }
143
+ break;
144
+
145
+ case "array":
146
+ for (const elem of expr.elements) {
147
+ if (elem === undefined) continue;
148
+ if ("kind" in elem && elem.kind === "spread") {
149
+ collectFromExpression(elem.expression, requests, seen, module);
150
+ } else {
151
+ collectFromExpression(elem, requests, seen, module);
152
+ }
153
+ }
154
+ break;
155
+
156
+ case "object":
157
+ for (const prop of expr.properties) {
158
+ if (prop.kind === "property") {
159
+ if (typeof prop.key !== "string") {
160
+ collectFromExpression(prop.key, requests, seen, module);
161
+ }
162
+ collectFromExpression(prop.value, requests, seen, module);
163
+ } else if (prop.kind === "spread") {
164
+ collectFromExpression(prop.expression, requests, seen, module);
165
+ }
166
+ }
167
+ break;
168
+
169
+ case "arrowFunction":
170
+ case "functionExpression":
171
+ if (typeof expr.body === "object" && "kind" in expr.body) {
172
+ if (expr.body.kind === "blockStatement") {
173
+ collectFromStatement(expr.body, requests, seen, module);
174
+ } else {
175
+ collectFromExpression(expr.body, requests, seen, module);
176
+ }
177
+ }
178
+ break;
179
+
180
+ // Literals, identifiers, etc. don't contain expressions
181
+ default:
182
+ break;
183
+ }
184
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Specialization collection - Public API
3
+ */
4
+
5
+ export { collectSpecializations } from "./orchestrator.js";
6
+ export { collectFromStatement } from "./statements.js";
7
+ export { collectFromExpression } from "./expressions.js";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Main specialization collection orchestration
3
+ */
4
+
5
+ import { IrModule } from "@tsonic/frontend";
6
+ import { SpecializationRequest } from "../types.js";
7
+ import { collectFromStatement } from "./statements.js";
8
+
9
+ /**
10
+ * Collect all specialization requests from a module
11
+ * Walks the IR tree looking for calls/news with requiresSpecialization flag
12
+ */
13
+ export const collectSpecializations = (
14
+ module: IrModule
15
+ ): readonly SpecializationRequest[] => {
16
+ const requests: SpecializationRequest[] = [];
17
+ const seen = new Set<string>(); // Avoid duplicates
18
+
19
+ // Walk through all statements and expressions to find specialization needs
20
+ for (const stmt of module.body) {
21
+ collectFromStatement(stmt, requests, seen, module);
22
+ }
23
+
24
+ return requests;
25
+ };
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Specialization collection from statements
3
+ */
4
+
5
+ import { IrModule, IrStatement } from "@tsonic/frontend";
6
+ import { SpecializationRequest } from "../types.js";
7
+ import { collectFromExpression } from "./expressions.js";
8
+
9
+ /**
10
+ * Collect specializations from a statement
11
+ */
12
+ export const collectFromStatement = (
13
+ stmt: IrStatement,
14
+ requests: SpecializationRequest[],
15
+ seen: Set<string>,
16
+ module: IrModule
17
+ ): void => {
18
+ switch (stmt.kind) {
19
+ case "expressionStatement":
20
+ collectFromExpression(stmt.expression, requests, seen, module);
21
+ break;
22
+
23
+ case "variableDeclaration":
24
+ for (const decl of stmt.declarations) {
25
+ if (decl.initializer) {
26
+ collectFromExpression(decl.initializer, requests, seen, module);
27
+ }
28
+ }
29
+ break;
30
+
31
+ case "returnStatement":
32
+ if (stmt.expression) {
33
+ collectFromExpression(stmt.expression, requests, seen, module);
34
+ }
35
+ break;
36
+
37
+ case "ifStatement":
38
+ collectFromExpression(stmt.condition, requests, seen, module);
39
+ collectFromStatement(stmt.thenStatement, requests, seen, module);
40
+ if (stmt.elseStatement) {
41
+ collectFromStatement(stmt.elseStatement, requests, seen, module);
42
+ }
43
+ break;
44
+
45
+ case "blockStatement":
46
+ for (const s of stmt.statements) {
47
+ collectFromStatement(s, requests, seen, module);
48
+ }
49
+ break;
50
+
51
+ case "whileStatement":
52
+ collectFromExpression(stmt.condition, requests, seen, module);
53
+ collectFromStatement(stmt.body, requests, seen, module);
54
+ break;
55
+
56
+ case "forStatement":
57
+ if (stmt.initializer) {
58
+ if (stmt.initializer.kind === "variableDeclaration") {
59
+ collectFromStatement(stmt.initializer, requests, seen, module);
60
+ } else {
61
+ collectFromExpression(stmt.initializer, requests, seen, module);
62
+ }
63
+ }
64
+ if (stmt.condition) {
65
+ collectFromExpression(stmt.condition, requests, seen, module);
66
+ }
67
+ if (stmt.update) {
68
+ collectFromExpression(stmt.update, requests, seen, module);
69
+ }
70
+ collectFromStatement(stmt.body, requests, seen, module);
71
+ break;
72
+
73
+ case "functionDeclaration":
74
+ if (stmt.body) {
75
+ collectFromStatement(stmt.body, requests, seen, module);
76
+ }
77
+ break;
78
+
79
+ case "classDeclaration":
80
+ for (const member of stmt.members) {
81
+ if (member.kind === "methodDeclaration" && member.body) {
82
+ collectFromStatement(member.body, requests, seen, module);
83
+ }
84
+ }
85
+ break;
86
+
87
+ // Other statement types don't contain expressions
88
+ default:
89
+ break;
90
+ }
91
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Collect specialization requests from IR
3
+ * Main dispatcher - re-exports from collection/ subdirectory
4
+ */
5
+
6
+ export {
7
+ collectSpecializations,
8
+ collectFromStatement,
9
+ collectFromExpression,
10
+ } from "./collection/index.js";
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Generate specialized declarations from requests
3
+ */
4
+
5
+ import {
6
+ IrFunctionDeclaration,
7
+ IrClassDeclaration,
8
+ IrBlockStatement,
9
+ IrType,
10
+ } from "@tsonic/frontend";
11
+ import { EmitterContext } from "../types.js";
12
+ import { emitStatement } from "../statement-emitter.js";
13
+ import { SpecializationRequest } from "./types.js";
14
+ import {
15
+ generateSpecializedFunctionName,
16
+ generateSpecializedClassName,
17
+ } from "./naming.js";
18
+ import {
19
+ substituteType,
20
+ substituteStatement,
21
+ substituteExpression,
22
+ } from "./substitution.js";
23
+
24
+ /**
25
+ * Generate specialized declarations from requests
26
+ * Returns C# code for the specialized declarations
27
+ */
28
+ export const generateSpecializations = (
29
+ requests: readonly SpecializationRequest[],
30
+ context: EmitterContext
31
+ ): [string, EmitterContext] => {
32
+ if (requests.length === 0) {
33
+ return ["", context];
34
+ }
35
+
36
+ const parts: string[] = [];
37
+ let currentContext = context;
38
+
39
+ for (const request of requests) {
40
+ if (request.kind === "function") {
41
+ const [code, newContext] = generateSpecializedFunction(
42
+ request,
43
+ currentContext
44
+ );
45
+ parts.push(code);
46
+ currentContext = newContext;
47
+ } else if (request.kind === "class") {
48
+ const [code, newContext] = generateSpecializedClass(
49
+ request,
50
+ currentContext
51
+ );
52
+ parts.push(code);
53
+ currentContext = newContext;
54
+ }
55
+ }
56
+
57
+ return [parts.join("\n\n"), currentContext];
58
+ };
59
+
60
+ /**
61
+ * Generate a specialized function by substituting type parameters
62
+ */
63
+ const generateSpecializedFunction = (
64
+ request: SpecializationRequest,
65
+ context: EmitterContext
66
+ ): [string, EmitterContext] => {
67
+ const funcDecl = request.declaration as IrFunctionDeclaration;
68
+
69
+ // Create type substitution map
70
+ const substitutions = new Map<string, IrType>();
71
+ if (funcDecl.typeParameters) {
72
+ funcDecl.typeParameters.forEach((tp, index) => {
73
+ const typeArg = request.typeArguments[index];
74
+ if (typeArg) {
75
+ substitutions.set(tp.name, typeArg);
76
+ }
77
+ });
78
+ }
79
+
80
+ // Substitute types in the function declaration
81
+ const specializedDecl: IrFunctionDeclaration = {
82
+ ...funcDecl,
83
+ name: generateSpecializedFunctionName(funcDecl.name, request.typeArguments),
84
+ typeParameters: undefined, // Remove type parameters
85
+ parameters: funcDecl.parameters.map((param) => ({
86
+ ...param,
87
+ type: param.type ? substituteType(param.type, substitutions) : undefined,
88
+ })),
89
+ returnType: funcDecl.returnType
90
+ ? substituteType(funcDecl.returnType, substitutions)
91
+ : undefined,
92
+ body: substituteStatement(funcDecl.body, substitutions) as IrBlockStatement,
93
+ };
94
+
95
+ // Emit the specialized function using the statement emitter
96
+ return emitStatement(specializedDecl, context);
97
+ };
98
+
99
+ /**
100
+ * Generate a specialized class by substituting type parameters
101
+ */
102
+ const generateSpecializedClass = (
103
+ request: SpecializationRequest,
104
+ context: EmitterContext
105
+ ): [string, EmitterContext] => {
106
+ const classDecl = request.declaration as IrClassDeclaration;
107
+
108
+ // Create type substitution map
109
+ const substitutions = new Map<string, IrType>();
110
+ if (classDecl.typeParameters) {
111
+ classDecl.typeParameters.forEach((tp, index) => {
112
+ const typeArg = request.typeArguments[index];
113
+ if (typeArg) {
114
+ substitutions.set(tp.name, typeArg);
115
+ }
116
+ });
117
+ }
118
+
119
+ // Generate specialized class name
120
+ const specializedName = generateSpecializedClassName(
121
+ classDecl.name,
122
+ request.typeArguments
123
+ );
124
+
125
+ // Substitute types in class members
126
+ const specializedMembers = classDecl.members.map((member) => {
127
+ if (member.kind === "propertyDeclaration") {
128
+ return {
129
+ ...member,
130
+ type: member.type
131
+ ? substituteType(member.type, substitutions)
132
+ : undefined,
133
+ };
134
+ } else if (member.kind === "methodDeclaration") {
135
+ return {
136
+ ...member,
137
+ parameters: member.parameters.map((param) => ({
138
+ ...param,
139
+ type: param.type
140
+ ? substituteType(param.type, substitutions)
141
+ : undefined,
142
+ })),
143
+ returnType: member.returnType
144
+ ? substituteType(member.returnType, substitutions)
145
+ : undefined,
146
+ body: member.body
147
+ ? (substituteStatement(
148
+ member.body,
149
+ substitutions
150
+ ) as IrBlockStatement)
151
+ : undefined,
152
+ };
153
+ } else if (member.kind === "constructorDeclaration") {
154
+ return {
155
+ ...member,
156
+ parameters: member.parameters.map((param) => ({
157
+ ...param,
158
+ type: param.type
159
+ ? substituteType(param.type, substitutions)
160
+ : undefined,
161
+ })),
162
+ body: member.body
163
+ ? (substituteStatement(
164
+ member.body,
165
+ substitutions
166
+ ) as IrBlockStatement)
167
+ : undefined,
168
+ };
169
+ }
170
+ return member;
171
+ });
172
+
173
+ // Create specialized class declaration
174
+ const specializedDecl: IrClassDeclaration = {
175
+ ...classDecl,
176
+ name: specializedName,
177
+ typeParameters: undefined, // Remove type parameters
178
+ members: specializedMembers,
179
+ superClass: classDecl.superClass
180
+ ? substituteExpression(classDecl.superClass, substitutions)
181
+ : undefined,
182
+ implements: classDecl.implements.map((iface) =>
183
+ substituteType(iface, substitutions)
184
+ ),
185
+ };
186
+
187
+ // Emit the specialized class using the statement emitter
188
+ return emitStatement(specializedDecl, context);
189
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Tests for Generic Classes
3
+ * Covers spec/15-generics.md §3-5 - Generic Classes
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("Generic Classes (spec/15 §3-5)", () => {
12
+ it("should emit generic class with type parameter", () => {
13
+ const module: IrModule = {
14
+ kind: "module",
15
+ filePath: "/src/Box.ts",
16
+ namespace: "MyApp",
17
+ className: "Box",
18
+ isStaticContainer: false,
19
+ imports: [],
20
+ body: [
21
+ {
22
+ kind: "classDeclaration",
23
+ name: "Box",
24
+ typeParameters: [
25
+ {
26
+ kind: "typeParameter",
27
+ name: "T",
28
+ constraint: undefined,
29
+ default: undefined,
30
+ variance: undefined,
31
+ isStructuralConstraint: false,
32
+ },
33
+ ],
34
+ isStruct: false,
35
+ members: [
36
+ {
37
+ kind: "propertyDeclaration",
38
+ name: "value",
39
+ type: { kind: "referenceType", name: "T", typeArguments: [] },
40
+ initializer: undefined,
41
+ accessibility: "public",
42
+ isStatic: false,
43
+ isReadonly: false,
44
+ },
45
+ ],
46
+ superClass: undefined,
47
+ implements: [],
48
+ isExported: true,
49
+ },
50
+ ],
51
+ exports: [],
52
+ };
53
+
54
+ const result = emitModule(module);
55
+
56
+ expect(result).to.include("public class Box<T>");
57
+ expect(result).to.include("public T value");
58
+ });
59
+ });