@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,59 @@
1
+ /**
2
+ * Exception handling emitters (try, throw)
3
+ */
4
+
5
+ import { IrStatement } from "@tsonic/frontend";
6
+ import { EmitterContext, getIndent } from "../../types.js";
7
+ import { emitExpression } from "../../expression-emitter.js";
8
+ import { emitBlockStatement } from "../blocks.js";
9
+
10
+ /**
11
+ * Emit a try statement
12
+ */
13
+ export const emitTryStatement = (
14
+ stmt: Extract<IrStatement, { kind: "tryStatement" }>,
15
+ context: EmitterContext
16
+ ): [string, EmitterContext] => {
17
+ const ind = getIndent(context);
18
+ const [tryBlock, tryContext] = emitBlockStatement(stmt.tryBlock, context);
19
+
20
+ let code = `${ind}try\n${tryBlock}`;
21
+ let currentContext = tryContext;
22
+
23
+ if (stmt.catchClause) {
24
+ const param =
25
+ stmt.catchClause.parameter?.kind === "identifierPattern"
26
+ ? stmt.catchClause.parameter.name
27
+ : "ex";
28
+
29
+ const [catchBlock, catchContext] = emitBlockStatement(
30
+ stmt.catchClause.body,
31
+ currentContext
32
+ );
33
+ code += `\n${ind}catch (Exception ${param})\n${catchBlock}`;
34
+ currentContext = catchContext;
35
+ }
36
+
37
+ if (stmt.finallyBlock) {
38
+ const [finallyBlock, finallyContext] = emitBlockStatement(
39
+ stmt.finallyBlock,
40
+ currentContext
41
+ );
42
+ code += `\n${ind}finally\n${finallyBlock}`;
43
+ currentContext = finallyContext;
44
+ }
45
+
46
+ return [code, currentContext];
47
+ };
48
+
49
+ /**
50
+ * Emit a throw statement
51
+ */
52
+ export const emitThrowStatement = (
53
+ stmt: Extract<IrStatement, { kind: "throwStatement" }>,
54
+ context: EmitterContext
55
+ ): [string, EmitterContext] => {
56
+ const ind = getIndent(context);
57
+ const [exprFrag, newContext] = emitExpression(stmt.expression, context);
58
+ return [`${ind}throw ${exprFrag.text};`, newContext];
59
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Control flow statement emitters - Public API
3
+ */
4
+
5
+ export {
6
+ emitWhileStatement,
7
+ emitForStatement,
8
+ emitForOfStatement,
9
+ } from "./loops.js";
10
+ export { emitIfStatement, emitSwitchStatement } from "./conditionals.js";
11
+ export { emitTryStatement, emitThrowStatement } from "./exceptions.js";
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Loop statement emitters (while, for, for-of)
3
+ */
4
+
5
+ import { IrStatement, IrExpression } from "@tsonic/frontend";
6
+ import { EmitterContext, getIndent, indent, dedent } from "../../types.js";
7
+ import { emitExpression } from "../../expression-emitter.js";
8
+ import { emitStatement } from "../../statement-emitter.js";
9
+
10
+ /**
11
+ * Information about a canonical integer loop counter.
12
+ * Canonical form: `for (let i = 0; i < n; i++)`
13
+ */
14
+ type CanonicalIntLoop = {
15
+ readonly varName: string;
16
+ readonly initialValue: number;
17
+ };
18
+
19
+ /**
20
+ * Detect if a for-loop has a canonical integer loop counter pattern.
21
+ *
22
+ * Canonical patterns:
23
+ * - Initializer: `let i = 0` (or any integer literal)
24
+ * - Update: `i++`, `++i`, `i += 1`, `i = i + 1`
25
+ *
26
+ * Returns the variable name and initial value if canonical, undefined otherwise.
27
+ */
28
+ const detectCanonicalIntLoop = (
29
+ stmt: Extract<IrStatement, { kind: "forStatement" }>
30
+ ): CanonicalIntLoop | undefined => {
31
+ const { initializer, update } = stmt;
32
+
33
+ // Check initializer: must be `let varName = <integer literal>`
34
+ if (!initializer || initializer.kind !== "variableDeclaration") {
35
+ return undefined;
36
+ }
37
+
38
+ if (initializer.declarationKind !== "let") {
39
+ return undefined;
40
+ }
41
+
42
+ if (initializer.declarations.length !== 1) {
43
+ return undefined;
44
+ }
45
+
46
+ const decl = initializer.declarations[0];
47
+ if (!decl || decl.name.kind !== "identifierPattern") {
48
+ return undefined;
49
+ }
50
+
51
+ const varName = decl.name.name;
52
+
53
+ // Check initializer value: must be an integer literal
54
+ const declInit = decl.initializer;
55
+ if (!declInit || declInit.kind !== "literal") {
56
+ return undefined;
57
+ }
58
+
59
+ const initValue = declInit.value;
60
+ if (typeof initValue !== "number" || !Number.isInteger(initValue)) {
61
+ return undefined;
62
+ }
63
+
64
+ // Check update: must be i++, ++i, i += 1, or i = i + 1
65
+ if (!update) {
66
+ return undefined;
67
+ }
68
+
69
+ if (!isIntegerIncrement(update, varName)) {
70
+ return undefined;
71
+ }
72
+
73
+ return { varName, initialValue: initValue };
74
+ };
75
+
76
+ /**
77
+ * Check if an expression is an integer increment of a variable.
78
+ * Matches: i++, ++i, i += 1, i = i + 1
79
+ */
80
+ const isIntegerIncrement = (expr: IrExpression, varName: string): boolean => {
81
+ // i++ or ++i
82
+ if (expr.kind === "update") {
83
+ if (expr.operator !== "++") {
84
+ return false;
85
+ }
86
+ if (expr.expression.kind !== "identifier") {
87
+ return false;
88
+ }
89
+ return expr.expression.name === varName;
90
+ }
91
+
92
+ // i += 1 or i = i + 1
93
+ if (expr.kind === "assignment") {
94
+ if (expr.left.kind !== "identifier" || expr.left.name !== varName) {
95
+ return false;
96
+ }
97
+
98
+ // i += 1
99
+ if (expr.operator === "+=") {
100
+ if (expr.right.kind !== "literal") {
101
+ return false;
102
+ }
103
+ return expr.right.value === 1;
104
+ }
105
+
106
+ // i = i + 1
107
+ if (expr.operator === "=") {
108
+ if (expr.right.kind !== "binary" || expr.right.operator !== "+") {
109
+ return false;
110
+ }
111
+ const binExpr = expr.right;
112
+ // Check i + 1 or 1 + i
113
+ const isVarPlusOne =
114
+ binExpr.left.kind === "identifier" &&
115
+ binExpr.left.name === varName &&
116
+ binExpr.right.kind === "literal" &&
117
+ binExpr.right.value === 1;
118
+ const isOnePlusVar =
119
+ binExpr.left.kind === "literal" &&
120
+ binExpr.left.value === 1 &&
121
+ binExpr.right.kind === "identifier" &&
122
+ binExpr.right.name === varName;
123
+ return isVarPlusOne || isOnePlusVar;
124
+ }
125
+ }
126
+
127
+ return false;
128
+ };
129
+
130
+ /**
131
+ * Emit a while statement
132
+ */
133
+ export const emitWhileStatement = (
134
+ stmt: Extract<IrStatement, { kind: "whileStatement" }>,
135
+ context: EmitterContext
136
+ ): [string, EmitterContext] => {
137
+ const ind = getIndent(context);
138
+ const [condFrag, condContext] = emitExpression(stmt.condition, context);
139
+
140
+ const [bodyCode, bodyContext] = emitStatement(stmt.body, indent(condContext));
141
+
142
+ const code = `${ind}while (${condFrag.text})\n${bodyCode}`;
143
+ return [code, dedent(bodyContext)];
144
+ };
145
+
146
+ /**
147
+ * Emit a for statement
148
+ *
149
+ * Special handling for canonical integer loop counters:
150
+ * `for (let i = 0; i < n; i++)` emits as `for (int i = 0; ...)` in C#.
151
+ * This avoids the double→int conversion cost when using loop variables
152
+ * as CLR indexers (e.g., list[i]).
153
+ */
154
+ export const emitForStatement = (
155
+ stmt: Extract<IrStatement, { kind: "forStatement" }>,
156
+ context: EmitterContext
157
+ ): [string, EmitterContext] => {
158
+ const ind = getIndent(context);
159
+ let currentContext = context;
160
+
161
+ // Check for canonical integer loop pattern
162
+ const canonicalLoop = detectCanonicalIntLoop(stmt);
163
+
164
+ // Initializer
165
+ let init = "";
166
+ if (stmt.initializer) {
167
+ if (canonicalLoop) {
168
+ // Canonical integer loop: emit `int varName = value` directly
169
+ init = `int ${canonicalLoop.varName} = ${canonicalLoop.initialValue}`;
170
+ } else if (stmt.initializer.kind === "variableDeclaration") {
171
+ const [initCode, newContext] = emitStatement(
172
+ stmt.initializer,
173
+ currentContext
174
+ );
175
+ currentContext = newContext;
176
+ // Strip trailing semicolon from variable declaration
177
+ init = initCode.trim().replace(/;$/, "");
178
+ } else {
179
+ const [initFrag, newContext] = emitExpression(
180
+ stmt.initializer,
181
+ currentContext
182
+ );
183
+ currentContext = newContext;
184
+ init = initFrag.text;
185
+ }
186
+ }
187
+
188
+ // Condition
189
+ let cond = "";
190
+ if (stmt.condition) {
191
+ const [condFrag, newContext] = emitExpression(
192
+ stmt.condition,
193
+ currentContext
194
+ );
195
+ currentContext = newContext;
196
+ cond = condFrag.text;
197
+ }
198
+
199
+ // Update
200
+ let update = "";
201
+ if (stmt.update) {
202
+ const [updateFrag, newContext] = emitExpression(
203
+ stmt.update,
204
+ currentContext
205
+ );
206
+ currentContext = newContext;
207
+ update = updateFrag.text;
208
+ }
209
+
210
+ // Body - if canonical loop, add the var to intLoopVars so indexers don't cast
211
+ let bodyContext: EmitterContext;
212
+ if (canonicalLoop) {
213
+ const existingIntVars = currentContext.intLoopVars ?? new Set<string>();
214
+ const newIntVars = new Set([...existingIntVars, canonicalLoop.varName]);
215
+ const contextWithIntVar = {
216
+ ...indent(currentContext),
217
+ intLoopVars: newIntVars,
218
+ };
219
+ const [code, ctx] = emitStatement(stmt.body, contextWithIntVar);
220
+ // Remove the var from intLoopVars after body (restore previous scope)
221
+ bodyContext = { ...ctx, intLoopVars: existingIntVars };
222
+ const finalCode = `${ind}for (${init}; ${cond}; ${update})\n${code}`;
223
+ return [finalCode, dedent(bodyContext)];
224
+ }
225
+
226
+ const [bodyCode, ctx] = emitStatement(stmt.body, indent(currentContext));
227
+ bodyContext = ctx;
228
+
229
+ const code = `${ind}for (${init}; ${cond}; ${update})\n${bodyCode}`;
230
+ return [code, dedent(bodyContext)];
231
+ };
232
+
233
+ /**
234
+ * Emit a for-of statement
235
+ */
236
+ export const emitForOfStatement = (
237
+ stmt: Extract<IrStatement, { kind: "forOfStatement" }>,
238
+ context: EmitterContext
239
+ ): [string, EmitterContext] => {
240
+ const ind = getIndent(context);
241
+ const [exprFrag, exprContext] = emitExpression(stmt.expression, context);
242
+
243
+ const [bodyCode, bodyContext] = emitStatement(stmt.body, indent(exprContext));
244
+
245
+ // Use foreach in C#
246
+ const varName =
247
+ stmt.variable.kind === "identifierPattern" ? stmt.variable.name : "item";
248
+ const code = `${ind}foreach (var ${varName} in ${exprFrag.text})\n${bodyCode}`;
249
+ return [code, dedent(bodyContext)];
250
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Control flow statement emitters (if, while, for, switch, try, throw)
3
+ * Main dispatcher - re-exports from control/ subdirectory
4
+ */
5
+
6
+ export {
7
+ emitIfStatement,
8
+ emitWhileStatement,
9
+ emitForStatement,
10
+ emitForOfStatement,
11
+ emitSwitchStatement,
12
+ emitTryStatement,
13
+ emitThrowStatement,
14
+ } from "./control/index.js";
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Class declaration emission
3
+ */
4
+
5
+ import { IrStatement } from "@tsonic/frontend";
6
+ import {
7
+ EmitterContext,
8
+ getIndent,
9
+ indent,
10
+ withClassName,
11
+ } from "../../types.js";
12
+ import { emitExpression } from "../../expression-emitter.js";
13
+ import { emitType, emitTypeParameters } from "../../type-emitter.js";
14
+ import { emitClassMember } from "../classes.js";
15
+
16
+ /**
17
+ * Emit a class declaration
18
+ */
19
+ export const emitClassDeclaration = (
20
+ stmt: Extract<IrStatement, { kind: "classDeclaration" }>,
21
+ context: EmitterContext
22
+ ): [string, EmitterContext] => {
23
+ const ind = getIndent(context);
24
+ let currentContext = context;
25
+ const parts: string[] = [];
26
+
27
+ // Access modifiers
28
+ const accessibility = stmt.isExported ? "public" : "internal";
29
+ parts.push(accessibility);
30
+
31
+ // Emit struct or class based on isStruct flag
32
+ parts.push(stmt.isStruct ? "struct" : "class");
33
+ parts.push(stmt.name);
34
+
35
+ // Type parameters
36
+ const [typeParamsStr, whereClauses, typeParamContext] = emitTypeParameters(
37
+ stmt.typeParameters,
38
+ currentContext
39
+ );
40
+ currentContext = typeParamContext;
41
+
42
+ // Base class and interfaces
43
+ const heritage: string[] = [];
44
+
45
+ // Handle superclass (extends clause)
46
+ if (stmt.superClass) {
47
+ const [superClassFrag, newContext] = emitExpression(
48
+ stmt.superClass,
49
+ currentContext
50
+ );
51
+ currentContext = newContext;
52
+ heritage.push(superClassFrag.text);
53
+ }
54
+
55
+ // Handle interfaces (implements clause)
56
+ if (stmt.implements && stmt.implements.length > 0) {
57
+ for (const iface of stmt.implements) {
58
+ const [ifaceType, newContext] = emitType(iface, currentContext);
59
+ currentContext = newContext;
60
+ heritage.push(ifaceType);
61
+ }
62
+ }
63
+
64
+ const heritageStr = heritage.length > 0 ? ` : ${heritage.join(", ")}` : "";
65
+ const whereClause =
66
+ whereClauses.length > 0
67
+ ? `\n${ind} ${whereClauses.join(`\n${ind} `)}`
68
+ : "";
69
+
70
+ // Class body
71
+ const baseContext = withClassName(indent(currentContext), stmt.name);
72
+ // Only set hasSuperClass flag if there's actually a superclass (for inheritance)
73
+ const bodyContext = stmt.superClass
74
+ ? { ...baseContext, hasSuperClass: true }
75
+ : baseContext;
76
+ const members: string[] = [];
77
+
78
+ for (const member of stmt.members) {
79
+ const [memberCode, newContext] = emitClassMember(member, bodyContext);
80
+ members.push(memberCode);
81
+ currentContext = newContext;
82
+ }
83
+
84
+ const signature = parts.join(" ");
85
+ const memberCode = members.join("\n\n");
86
+ const code = `${ind}${signature}${typeParamsStr}${heritageStr}${whereClause}\n${ind}{\n${memberCode}\n${ind}}`;
87
+
88
+ return [code, currentContext];
89
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Enum declaration emission
3
+ */
4
+
5
+ import { IrStatement } from "@tsonic/frontend";
6
+ import { EmitterContext, getIndent, indent } from "../../types.js";
7
+ import { emitExpression } from "../../expression-emitter.js";
8
+
9
+ /**
10
+ * Emit an enum declaration
11
+ */
12
+ export const emitEnumDeclaration = (
13
+ stmt: Extract<IrStatement, { kind: "enumDeclaration" }>,
14
+ context: EmitterContext
15
+ ): [string, EmitterContext] => {
16
+ const ind = getIndent(context);
17
+ const memberInd = getIndent(indent(context));
18
+
19
+ const accessibility = stmt.isExported ? "public" : "internal";
20
+ const members = stmt.members
21
+ .map((member) => {
22
+ if (member.initializer) {
23
+ const [initFrag] = emitExpression(member.initializer, context);
24
+ return `${memberInd}${member.name} = ${initFrag.text}`;
25
+ }
26
+ return `${memberInd}${member.name}`;
27
+ })
28
+ .join(",\n");
29
+
30
+ const code = `${ind}${accessibility} enum ${stmt.name}\n${ind}{\n${members}\n${ind}}`;
31
+ return [code, context];
32
+ };
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Function declaration emission
3
+ */
4
+
5
+ import { IrStatement } from "@tsonic/frontend";
6
+ import {
7
+ EmitterContext,
8
+ getIndent,
9
+ indent,
10
+ withAsync,
11
+ withStatic,
12
+ } from "../../types.js";
13
+ import { emitType, emitTypeParameters } from "../../type-emitter.js";
14
+ import { emitBlockStatement } from "../blocks.js";
15
+ import { emitParameters } from "../classes.js";
16
+
17
+ /**
18
+ * Emit a function declaration
19
+ */
20
+ export const emitFunctionDeclaration = (
21
+ stmt: Extract<IrStatement, { kind: "functionDeclaration" }>,
22
+ context: EmitterContext
23
+ ): [string, EmitterContext] => {
24
+ const ind = getIndent(context);
25
+ let currentContext = context;
26
+ const parts: string[] = [];
27
+
28
+ // Access modifiers
29
+ const accessibility = stmt.isExported ? "public" : "private";
30
+ parts.push(accessibility);
31
+
32
+ if (context.isStatic) {
33
+ parts.push("static");
34
+ }
35
+
36
+ if (stmt.isAsync && !stmt.isGenerator) {
37
+ parts.push("async");
38
+ }
39
+
40
+ // Return type
41
+ if (stmt.isGenerator) {
42
+ // Generator functions return IEnumerable<exchange> or IAsyncEnumerable<exchange>
43
+ const exchangeName = `${stmt.name}_exchange`;
44
+ if (stmt.isAsync) {
45
+ parts.push(
46
+ `async global::System.Collections.Generic.IAsyncEnumerable<${exchangeName}>`
47
+ );
48
+ } else {
49
+ parts.push(
50
+ `global::System.Collections.Generic.IEnumerable<${exchangeName}>`
51
+ );
52
+ }
53
+ } else if (stmt.returnType) {
54
+ const [returnType, newContext] = emitType(stmt.returnType, currentContext);
55
+ currentContext = newContext;
56
+ // If async and return type is Promise, it's already converted to Task
57
+ // Don't wrap it again
58
+ if (
59
+ stmt.isAsync &&
60
+ stmt.returnType.kind === "referenceType" &&
61
+ stmt.returnType.name === "Promise"
62
+ ) {
63
+ parts.push(returnType); // Already Task<T> from emitType
64
+ } else {
65
+ parts.push(
66
+ stmt.isAsync
67
+ ? `global::System.Threading.Tasks.Task<${returnType}>`
68
+ : returnType
69
+ );
70
+ }
71
+ } else {
72
+ parts.push(stmt.isAsync ? "global::System.Threading.Tasks.Task" : "void");
73
+ }
74
+
75
+ // Function name
76
+ parts.push(stmt.name);
77
+
78
+ // Type parameters
79
+ const [typeParamsStr, whereClauses, typeParamContext] = emitTypeParameters(
80
+ stmt.typeParameters,
81
+ currentContext
82
+ );
83
+ currentContext = typeParamContext;
84
+
85
+ // Parameters
86
+ const params = emitParameters(stmt.parameters, currentContext);
87
+ currentContext = params[1];
88
+
89
+ // Function body (not a static context - local variables)
90
+ const bodyContext = withAsync(
91
+ withStatic(indent(currentContext), false),
92
+ stmt.isAsync
93
+ );
94
+ const [bodyCode] = emitBlockStatement(stmt.body, bodyContext);
95
+
96
+ // Collect out parameters that need initialization
97
+ const outParams: Array<{ name: string; type: string }> = [];
98
+ for (const param of stmt.parameters) {
99
+ // Use param.passing to detect out parameters (type is already unwrapped by frontend)
100
+ if (param.passing === "out" && param.pattern.kind === "identifierPattern") {
101
+ // Get the type for default value
102
+ let typeName = "object";
103
+ if (param.type) {
104
+ const [typeStr] = emitType(param.type, currentContext);
105
+ typeName = typeStr;
106
+ }
107
+ outParams.push({ name: param.pattern.name, type: typeName });
108
+ }
109
+ }
110
+
111
+ // Inject initialization code for generators and out parameters
112
+ let finalBodyCode = bodyCode;
113
+ const bodyInd = getIndent(bodyContext);
114
+ const injectLines: string[] = [];
115
+
116
+ // Add generator exchange initialization
117
+ if (stmt.isGenerator) {
118
+ const exchangeName = `${stmt.name}_exchange`;
119
+ injectLines.push(`${bodyInd}var exchange = new ${exchangeName}();`);
120
+ }
121
+
122
+ // Add out parameter initializations
123
+ if (outParams.length > 0) {
124
+ for (const outParam of outParams) {
125
+ injectLines.push(`${bodyInd}${outParam.name} = default;`);
126
+ }
127
+ }
128
+
129
+ // Inject lines after opening brace
130
+ if (injectLines.length > 0) {
131
+ const lines = bodyCode.split("\n");
132
+ if (lines.length > 1) {
133
+ lines.splice(1, 0, ...injectLines, "");
134
+ finalBodyCode = lines.join("\n");
135
+ }
136
+ }
137
+
138
+ const signature = parts.join(" ");
139
+ const whereClause =
140
+ whereClauses.length > 0
141
+ ? `\n${ind} ${whereClauses.join(`\n${ind} `)}`
142
+ : "";
143
+ const code = `${ind}${signature}${typeParamsStr}(${params[0]})${whereClause}\n${finalBodyCode}`;
144
+
145
+ // Return context - no usings tracking needed with global:: FQN approach
146
+ return [code, currentContext];
147
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Declaration emitters - Public API
3
+ */
4
+
5
+ export { emitVariableDeclaration } from "./variables.js";
6
+ export { emitFunctionDeclaration } from "./functions.js";
7
+ export { emitClassDeclaration } from "./classes.js";
8
+ export { emitInterfaceDeclaration } from "./interfaces.js";
9
+ export { emitEnumDeclaration } from "./enums.js";
10
+ export { emitTypeAliasDeclaration } from "./type-aliases.js";