@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,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,9 @@
1
+ /**
2
+ * Class-related helper functions
3
+ */
4
+
5
+ /**
6
+ * Capitalize first letter of a string (for generating class names from property names)
7
+ */
8
+ export const capitalize = (str: string): string =>
9
+ str.charAt(0).toUpperCase() + str.slice(1);
@@ -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
+ };