@tsonic/emitter 0.0.72 → 0.0.74

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 (207) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-generator.d.ts.map +1 -1
  3. package/dist/adapter-generator.js +2 -1
  4. package/dist/adapter-generator.js.map +1 -1
  5. package/dist/constants.d.ts +3 -2
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +15 -6
  8. package/dist/constants.js.map +1 -1
  9. package/dist/core/format/attributes.d.ts.map +1 -1
  10. package/dist/core/format/attributes.js +5 -56
  11. package/dist/core/format/attributes.js.map +1 -1
  12. package/dist/core/format/attributes.test.js +1 -1
  13. package/dist/core/format/attributes.test.js.map +1 -1
  14. package/dist/core/format/backend-ast/builders.d.ts +13 -0
  15. package/dist/core/format/backend-ast/builders.d.ts.map +1 -0
  16. package/dist/core/format/backend-ast/builders.js +169 -0
  17. package/dist/core/format/backend-ast/builders.js.map +1 -0
  18. package/dist/core/format/backend-ast/builders.test.d.ts +2 -0
  19. package/dist/core/format/backend-ast/builders.test.d.ts.map +1 -0
  20. package/dist/core/format/backend-ast/builders.test.js +258 -0
  21. package/dist/core/format/backend-ast/builders.test.js.map +1 -0
  22. package/dist/core/format/backend-ast/index.d.ts +3 -2
  23. package/dist/core/format/backend-ast/index.d.ts.map +1 -1
  24. package/dist/core/format/backend-ast/index.js +2 -1
  25. package/dist/core/format/backend-ast/index.js.map +1 -1
  26. package/dist/core/format/backend-ast/invariants.test.d.ts +2 -0
  27. package/dist/core/format/backend-ast/invariants.test.d.ts.map +1 -0
  28. package/dist/core/format/backend-ast/invariants.test.js +72 -0
  29. package/dist/core/format/backend-ast/invariants.test.js.map +1 -0
  30. package/dist/core/format/backend-ast/printer.d.ts +1 -1
  31. package/dist/core/format/backend-ast/printer.d.ts.map +1 -1
  32. package/dist/core/format/backend-ast/printer.js +362 -119
  33. package/dist/core/format/backend-ast/printer.js.map +1 -1
  34. package/dist/core/format/backend-ast/printer.test.d.ts +2 -0
  35. package/dist/core/format/backend-ast/printer.test.d.ts.map +1 -0
  36. package/dist/core/format/backend-ast/printer.test.js +796 -0
  37. package/dist/core/format/backend-ast/printer.test.js.map +1 -0
  38. package/dist/core/format/backend-ast/types.d.ts +66 -14
  39. package/dist/core/format/backend-ast/types.d.ts.map +1 -1
  40. package/dist/core/format/backend-ast/utils.d.ts +12 -8
  41. package/dist/core/format/backend-ast/utils.d.ts.map +1 -1
  42. package/dist/core/format/backend-ast/utils.js +222 -19
  43. package/dist/core/format/backend-ast/utils.js.map +1 -1
  44. package/dist/core/format/backend-ast/utils.test.d.ts +2 -0
  45. package/dist/core/format/backend-ast/utils.test.d.ts.map +1 -0
  46. package/dist/core/format/backend-ast/utils.test.js +142 -0
  47. package/dist/core/format/backend-ast/utils.test.js.map +1 -0
  48. package/dist/core/format/module-emitter/assembly.d.ts +2 -2
  49. package/dist/core/format/module-emitter/assembly.d.ts.map +1 -1
  50. package/dist/core/format/module-emitter/assembly.js +7 -3
  51. package/dist/core/format/module-emitter/assembly.js.map +1 -1
  52. package/dist/core/format/module-emitter/header.d.ts +2 -1
  53. package/dist/core/format/module-emitter/header.d.ts.map +1 -1
  54. package/dist/core/format/module-emitter/header.js +2 -2
  55. package/dist/core/format/module-emitter/header.js.map +1 -1
  56. package/dist/core/format/module-emitter/orchestrator.js +1 -1
  57. package/dist/core/format/module-emitter/orchestrator.js.map +1 -1
  58. package/dist/core/format/module-emitter/static-container.d.ts.map +1 -1
  59. package/dist/core/format/module-emitter/static-container.js +3 -5
  60. package/dist/core/format/module-emitter/static-container.js.map +1 -1
  61. package/dist/core/module-emitter.test.js +1 -0
  62. package/dist/core/module-emitter.test.js.map +1 -1
  63. package/dist/core/semantic/boolean-context.d.ts.map +1 -1
  64. package/dist/core/semantic/boolean-context.js +45 -49
  65. package/dist/core/semantic/boolean-context.js.map +1 -1
  66. package/dist/core/semantic/imports.d.ts.map +1 -1
  67. package/dist/core/semantic/imports.js +25 -15
  68. package/dist/core/semantic/imports.js.map +1 -1
  69. package/dist/core/semantic/imports.test.js +43 -0
  70. package/dist/core/semantic/imports.test.js.map +1 -1
  71. package/dist/core/semantic/type-resolution.d.ts +1 -0
  72. package/dist/core/semantic/type-resolution.d.ts.map +1 -1
  73. package/dist/core/semantic/type-resolution.js +166 -5
  74. package/dist/core/semantic/type-resolution.js.map +1 -1
  75. package/dist/core/semantic/type-resolution.test.js +35 -0
  76. package/dist/core/semantic/type-resolution.test.js.map +1 -1
  77. package/dist/emitter-types/core.d.ts +24 -10
  78. package/dist/emitter-types/core.d.ts.map +1 -1
  79. package/dist/emitter.d.ts.map +1 -1
  80. package/dist/emitter.js +124 -44
  81. package/dist/emitter.js.map +1 -1
  82. package/dist/expression-emitter.d.ts.map +1 -1
  83. package/dist/expression-emitter.js +797 -17
  84. package/dist/expression-emitter.js.map +1 -1
  85. package/dist/expressions/access.d.ts.map +1 -1
  86. package/dist/expressions/access.js +65 -25
  87. package/dist/expressions/access.js.map +1 -1
  88. package/dist/expressions/calls/call-analysis.d.ts +0 -10
  89. package/dist/expressions/calls/call-analysis.d.ts.map +1 -1
  90. package/dist/expressions/calls/call-analysis.js +3 -62
  91. package/dist/expressions/calls/call-analysis.js.map +1 -1
  92. package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
  93. package/dist/expressions/calls/call-emitter.js +97 -196
  94. package/dist/expressions/calls/call-emitter.js.map +1 -1
  95. package/dist/expressions/calls/new-emitter.d.ts.map +1 -1
  96. package/dist/expressions/calls/new-emitter.js +36 -69
  97. package/dist/expressions/calls/new-emitter.js.map +1 -1
  98. package/dist/expressions/collections.d.ts +3 -0
  99. package/dist/expressions/collections.d.ts.map +1 -1
  100. package/dist/expressions/collections.js +238 -57
  101. package/dist/expressions/collections.js.map +1 -1
  102. package/dist/expressions/functions.d.ts.map +1 -1
  103. package/dist/expressions/functions.js +1 -7
  104. package/dist/expressions/functions.js.map +1 -1
  105. package/dist/expressions/identifiers.d.ts.map +1 -1
  106. package/dist/expressions/identifiers.js +24 -38
  107. package/dist/expressions/identifiers.js.map +1 -1
  108. package/dist/expressions/index.test.js +245 -0
  109. package/dist/expressions/index.test.js.map +1 -1
  110. package/dist/expressions/literals.d.ts.map +1 -1
  111. package/dist/expressions/literals.js +9 -41
  112. package/dist/expressions/literals.js.map +1 -1
  113. package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -1
  114. package/dist/expressions/operators/assignment-emitter.js +2 -6
  115. package/dist/expressions/operators/assignment-emitter.js.map +1 -1
  116. package/dist/expressions/operators/binary-emitter.d.ts.map +1 -1
  117. package/dist/expressions/operators/binary-emitter.js +102 -77
  118. package/dist/expressions/operators/binary-emitter.js.map +1 -1
  119. package/dist/expressions/operators/logical-emitter.d.ts.map +1 -1
  120. package/dist/expressions/operators/logical-emitter.js +1 -3
  121. package/dist/expressions/operators/logical-emitter.js.map +1 -1
  122. package/dist/expressions/operators/unary-emitter.d.ts.map +1 -1
  123. package/dist/expressions/operators/unary-emitter.js +9 -20
  124. package/dist/expressions/operators/unary-emitter.js.map +1 -1
  125. package/dist/expressions/other.d.ts.map +1 -1
  126. package/dist/expressions/other.js +57 -4
  127. package/dist/expressions/other.js.map +1 -1
  128. package/dist/generator-exchange.d.ts.map +1 -1
  129. package/dist/generator-exchange.js +3 -2
  130. package/dist/generator-exchange.js.map +1 -1
  131. package/dist/generator-wrapper.d.ts.map +1 -1
  132. package/dist/generator-wrapper.js +27 -56
  133. package/dist/generator-wrapper.js.map +1 -1
  134. package/dist/integration.test.js +393 -5
  135. package/dist/integration.test.js.map +1 -1
  136. package/dist/json-aot-generic.test.js +3 -0
  137. package/dist/json-aot-generic.test.js.map +1 -1
  138. package/dist/patterns.d.ts.map +1 -1
  139. package/dist/patterns.js +19 -40
  140. package/dist/patterns.js.map +1 -1
  141. package/dist/specialization/type-aliases.test.js +8 -0
  142. package/dist/specialization/type-aliases.test.js.map +1 -1
  143. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  144. package/dist/statements/classes/members/methods.js +5 -22
  145. package/dist/statements/classes/members/methods.js.map +1 -1
  146. package/dist/statements/classes/parameters.d.ts.map +1 -1
  147. package/dist/statements/classes/parameters.js +2 -1
  148. package/dist/statements/classes/parameters.js.map +1 -1
  149. package/dist/statements/classes/properties.d.ts.map +1 -1
  150. package/dist/statements/classes/properties.js +5 -16
  151. package/dist/statements/classes/properties.js.map +1 -1
  152. package/dist/statements/control/conditionals/guard-analysis.d.ts +44 -1
  153. package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -1
  154. package/dist/statements/control/conditionals/guard-analysis.js +301 -125
  155. package/dist/statements/control/conditionals/guard-analysis.js.map +1 -1
  156. package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
  157. package/dist/statements/control/conditionals/if-emitter.js +182 -53
  158. package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
  159. package/dist/statements/control/exceptions.d.ts.map +1 -1
  160. package/dist/statements/control/exceptions.js +2 -4
  161. package/dist/statements/control/exceptions.js.map +1 -1
  162. package/dist/statements/control/loops.d.ts.map +1 -1
  163. package/dist/statements/control/loops.js +3 -5
  164. package/dist/statements/control/loops.js.map +1 -1
  165. package/dist/statements/declarations/classes.d.ts.map +1 -1
  166. package/dist/statements/declarations/classes.js +2 -4
  167. package/dist/statements/declarations/classes.js.map +1 -1
  168. package/dist/statements/declarations/functions.d.ts.map +1 -1
  169. package/dist/statements/declarations/functions.js +38 -79
  170. package/dist/statements/declarations/functions.js.map +1 -1
  171. package/dist/statements/declarations/interfaces-mutable-storage.test.js +10 -2
  172. package/dist/statements/declarations/interfaces-mutable-storage.test.js.map +1 -1
  173. package/dist/statements/declarations/interfaces.d.ts.map +1 -1
  174. package/dist/statements/declarations/interfaces.js +4 -12
  175. package/dist/statements/declarations/interfaces.js.map +1 -1
  176. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  177. package/dist/statements/declarations/type-aliases.js +5 -9
  178. package/dist/statements/declarations/type-aliases.js.map +1 -1
  179. package/dist/statements/declarations/variables.d.ts.map +1 -1
  180. package/dist/statements/declarations/variables.js +55 -51
  181. package/dist/statements/declarations/variables.js.map +1 -1
  182. package/dist/statements/index.test.js +1026 -0
  183. package/dist/statements/index.test.js.map +1 -1
  184. package/dist/types/dictionaries.d.ts.map +1 -1
  185. package/dist/types/dictionaries.js +5 -5
  186. package/dist/types/dictionaries.js.map +1 -1
  187. package/dist/types/functions.d.ts.map +1 -1
  188. package/dist/types/functions.js +5 -25
  189. package/dist/types/functions.js.map +1 -1
  190. package/dist/types/primitives.d.ts.map +1 -1
  191. package/dist/types/primitives.js.map +1 -1
  192. package/dist/types/references.d.ts.map +1 -1
  193. package/dist/types/references.js +65 -128
  194. package/dist/types/references.js.map +1 -1
  195. package/dist/types/references.test.js +170 -46
  196. package/dist/types/references.test.js.map +1 -1
  197. package/dist/types/tuples.d.ts.map +1 -1
  198. package/dist/types/tuples.js +7 -17
  199. package/dist/types/tuples.js.map +1 -1
  200. package/dist/types/unions.d.ts.map +1 -1
  201. package/dist/types/unions.js +2 -5
  202. package/dist/types/unions.js.map +1 -1
  203. package/package.json +2 -2
  204. package/dist/expressions/parentheses.d.ts +0 -4
  205. package/dist/expressions/parentheses.d.ts.map +0 -1
  206. package/dist/expressions/parentheses.js +0 -91
  207. package/dist/expressions/parentheses.js.map +0 -1
@@ -0,0 +1,796 @@
1
+ import { describe, it } from "mocha";
2
+ import { expect } from "chai";
3
+ import { decimalIntegerLiteral, identifierType, stringLiteral, } from "./builders.js";
4
+ import { printCompilationUnit, printExpression, printPattern, printStatement, printType, } from "./printer.js";
5
+ describe("backend-ast printer", () => {
6
+ it("prints the full typed literal set without raw text escape hatches", () => {
7
+ const cases = [
8
+ [{ kind: "nullLiteralExpression" }, "null"],
9
+ [{ kind: "booleanLiteralExpression", value: true }, "true"],
10
+ [{ kind: "booleanLiteralExpression", value: false }, "false"],
11
+ [
12
+ { kind: "stringLiteralExpression", value: "hello\nworld" },
13
+ '"hello\\nworld"',
14
+ ],
15
+ [
16
+ {
17
+ kind: "stringLiteralExpression",
18
+ value: '"\\\r\t',
19
+ },
20
+ '"\\"\\\\\\r\\t"',
21
+ ],
22
+ [{ kind: "charLiteralExpression", value: "\0" }, "'\\0'"],
23
+ [{ kind: "charLiteralExpression", value: "'" }, "'\\''"],
24
+ [{ kind: "charLiteralExpression", value: "\\" }, "'\\\\'"],
25
+ [{ kind: "charLiteralExpression", value: "\n" }, "'\\n'"],
26
+ [{ kind: "charLiteralExpression", value: "\r" }, "'\\r'"],
27
+ [{ kind: "charLiteralExpression", value: "\t" }, "'\\t'"],
28
+ [
29
+ {
30
+ kind: "numericLiteralExpression",
31
+ base: "decimal",
32
+ wholePart: "42",
33
+ },
34
+ "42",
35
+ ],
36
+ [
37
+ {
38
+ kind: "numericLiteralExpression",
39
+ base: "decimal",
40
+ wholePart: "0",
41
+ suffix: "d",
42
+ },
43
+ "0d",
44
+ ],
45
+ [
46
+ {
47
+ kind: "numericLiteralExpression",
48
+ base: "decimal",
49
+ wholePart: "1",
50
+ suffix: "m",
51
+ },
52
+ "1m",
53
+ ],
54
+ [
55
+ {
56
+ kind: "numericLiteralExpression",
57
+ base: "decimal",
58
+ wholePart: "0",
59
+ suffix: "U",
60
+ },
61
+ "0U",
62
+ ],
63
+ [
64
+ {
65
+ kind: "numericLiteralExpression",
66
+ base: "decimal",
67
+ wholePart: "0",
68
+ suffix: "UL",
69
+ },
70
+ "0UL",
71
+ ],
72
+ [
73
+ {
74
+ kind: "numericLiteralExpression",
75
+ base: "decimal",
76
+ wholePart: "1",
77
+ fractionalPart: "5",
78
+ suffix: "f",
79
+ },
80
+ "1.5f",
81
+ ],
82
+ [
83
+ {
84
+ kind: "numericLiteralExpression",
85
+ base: "decimal",
86
+ wholePart: "6",
87
+ fractionalPart: "02",
88
+ exponentSign: "+",
89
+ exponentDigits: "23",
90
+ suffix: "d",
91
+ },
92
+ "6.02e+23d",
93
+ ],
94
+ [
95
+ {
96
+ kind: "numericLiteralExpression",
97
+ base: "hexadecimal",
98
+ wholePart: "FF",
99
+ },
100
+ "0xFF",
101
+ ],
102
+ [
103
+ {
104
+ kind: "numericLiteralExpression",
105
+ base: "binary",
106
+ wholePart: "1010",
107
+ },
108
+ "0b1010",
109
+ ],
110
+ ];
111
+ for (const [ast, expected] of cases) {
112
+ expect(printExpression(ast)).to.equal(expected);
113
+ }
114
+ });
115
+ it("prints implicit element access expressions for collection initializers", () => {
116
+ expect(printExpression({
117
+ kind: "implicitElementAccessExpression",
118
+ arguments: [{ kind: "stringLiteralExpression", value: "count" }],
119
+ })).to.equal('["count"]');
120
+ });
121
+ it("prints type reference expressions structurally", () => {
122
+ expect(printExpression({
123
+ kind: "typeReferenceExpression",
124
+ type: identifierType("global::System.Collections.Generic.Dictionary", [
125
+ { kind: "predefinedType", keyword: "string" },
126
+ { kind: "predefinedType", keyword: "int" },
127
+ ]),
128
+ })).to.equal("global::System.Collections.Generic.Dictionary<string, int>");
129
+ });
130
+ it("prints all supported predefined type keywords", () => {
131
+ const keywords = [
132
+ "bool",
133
+ "byte",
134
+ "sbyte",
135
+ "short",
136
+ "ushort",
137
+ "int",
138
+ "uint",
139
+ "long",
140
+ "ulong",
141
+ "nint",
142
+ "nuint",
143
+ "float",
144
+ "double",
145
+ "decimal",
146
+ "char",
147
+ "string",
148
+ "object",
149
+ "void",
150
+ ];
151
+ for (const keyword of keywords) {
152
+ expect(printType({ kind: "predefinedType", keyword })).to.equal(keyword);
153
+ }
154
+ });
155
+ it("prints exact BCL numeric value types as identifier types, not imaginary keywords", () => {
156
+ expect(printType(identifierType("global::System.Int128"))).to.equal("global::System.Int128");
157
+ expect(printType(identifierType("global::System.UInt128"))).to.equal("global::System.UInt128");
158
+ expect(printType(identifierType("global::System.Half"))).to.equal("global::System.Half");
159
+ });
160
+ it("rejects illegally qualified simple identifier nodes", () => {
161
+ expect(() => printType({
162
+ kind: "identifierType",
163
+ name: "global::System.String",
164
+ })).to.throw("ICE: Simple identifierType 'global::System.String' contains qualification. Use qualifiedIdentifierType AST instead.");
165
+ expect(() => printExpression({
166
+ kind: "identifierExpression",
167
+ identifier: "global::System.String",
168
+ })).to.throw("ICE: Simple identifierExpression 'global::System.String' contains qualification. Use qualifiedIdentifierExpression AST instead.");
169
+ });
170
+ it("prints compilation-unit leading trivia structurally", () => {
171
+ expect(printCompilationUnit({
172
+ kind: "compilationUnit",
173
+ leadingTrivia: [
174
+ { kind: "singleLineCommentTrivia", text: "<auto-generated/>" },
175
+ { kind: "singleLineCommentTrivia", text: "Generated by Tsonic" },
176
+ { kind: "blankLineTrivia" },
177
+ ],
178
+ usings: [],
179
+ members: [],
180
+ })).to.equal("// <auto-generated/>\n// Generated by Tsonic\n");
181
+ });
182
+ it("uses structural unary analysis instead of operand text lookups", () => {
183
+ expect(printExpression({
184
+ kind: "prefixUnaryExpression",
185
+ operatorToken: "-",
186
+ operand: {
187
+ kind: "prefixUnaryExpression",
188
+ operatorToken: "-",
189
+ operand: { kind: "identifierExpression", identifier: "value" },
190
+ },
191
+ })).to.equal("- -value");
192
+ expect(printExpression({
193
+ kind: "prefixUnaryExpression",
194
+ operatorToken: "+",
195
+ operand: {
196
+ kind: "prefixUnaryExpression",
197
+ operatorToken: "+",
198
+ operand: { kind: "identifierExpression", identifier: "count" },
199
+ },
200
+ })).to.equal("+ +count");
201
+ });
202
+ it("parenthesizes interpolations structurally when the AST can print colons", () => {
203
+ expect(printExpression({
204
+ kind: "interpolatedStringExpression",
205
+ parts: [
206
+ {
207
+ kind: "interpolation",
208
+ expression: {
209
+ kind: "conditionalExpression",
210
+ condition: { kind: "identifierExpression", identifier: "flag" },
211
+ whenTrue: { kind: "identifierExpression", identifier: "left" },
212
+ whenFalse: { kind: "identifierExpression", identifier: "right" },
213
+ },
214
+ },
215
+ ],
216
+ })).to.equal('$"{(flag ? left : right)}"');
217
+ expect(printExpression({
218
+ kind: "interpolatedStringExpression",
219
+ parts: [
220
+ {
221
+ kind: "interpolation",
222
+ expression: {
223
+ kind: "memberAccessExpression",
224
+ expression: {
225
+ kind: "typeReferenceExpression",
226
+ type: identifierType("global::System.String"),
227
+ },
228
+ memberName: "Empty",
229
+ },
230
+ },
231
+ ],
232
+ })).to.equal('$"{(global::System.String.Empty)}"');
233
+ });
234
+ it("prints block-bodied lambdas from printer indentation context, not emitter string hints", () => {
235
+ expect(printExpression({
236
+ kind: "lambdaExpression",
237
+ isAsync: false,
238
+ parameters: [],
239
+ body: {
240
+ kind: "blockStatement",
241
+ statements: [
242
+ {
243
+ kind: "returnStatement",
244
+ expression: {
245
+ kind: "stringLiteralExpression",
246
+ value: "ok",
247
+ },
248
+ },
249
+ ],
250
+ },
251
+ }, " ")).to.equal('() =>\n {\n return "ok";\n }');
252
+ });
253
+ it("prints block-bodied lambdas consistently inside nested expression contexts", () => {
254
+ expect(printExpression({
255
+ kind: "invocationExpression",
256
+ expression: {
257
+ kind: "parenthesizedExpression",
258
+ expression: {
259
+ kind: "castExpression",
260
+ type: identifierType("global::System.Func", [
261
+ { kind: "predefinedType", keyword: "object" },
262
+ ]),
263
+ expression: {
264
+ kind: "lambdaExpression",
265
+ isAsync: false,
266
+ parameters: [],
267
+ body: {
268
+ kind: "blockStatement",
269
+ statements: [
270
+ {
271
+ kind: "expressionStatement",
272
+ expression: {
273
+ kind: "invocationExpression",
274
+ expression: {
275
+ kind: "identifierExpression",
276
+ identifier: "sideEffect",
277
+ },
278
+ arguments: [],
279
+ },
280
+ },
281
+ {
282
+ kind: "returnStatement",
283
+ expression: {
284
+ kind: "defaultExpression",
285
+ type: {
286
+ kind: "nullableType",
287
+ underlyingType: {
288
+ kind: "predefinedType",
289
+ keyword: "object",
290
+ },
291
+ },
292
+ },
293
+ },
294
+ ],
295
+ },
296
+ },
297
+ },
298
+ },
299
+ arguments: [],
300
+ }, " ")).to.equal("((global::System.Func<object>)(() =>\n" +
301
+ " {\n" +
302
+ " sideEffect();\n" +
303
+ " return default(object?);\n" +
304
+ " }))()");
305
+ });
306
+ it("prints interpolated string text and format clauses structurally", () => {
307
+ expect(printExpression({
308
+ kind: "interpolatedStringExpression",
309
+ parts: [
310
+ { kind: "text", text: "count = " },
311
+ {
312
+ kind: "interpolation",
313
+ expression: { kind: "identifierExpression", identifier: "value" },
314
+ formatClause: "D4",
315
+ },
316
+ { kind: "text", text: ", raw = " },
317
+ {
318
+ kind: "interpolation",
319
+ expression: {
320
+ kind: "numericLiteralExpression",
321
+ base: "hexadecimal",
322
+ wholePart: "FF",
323
+ },
324
+ },
325
+ ],
326
+ })).to.equal('$"count = {value:D4}, raw = {0xFF}"');
327
+ });
328
+ it("prints compound structured types without falling back to textual reconstruction", () => {
329
+ expect(printType({
330
+ kind: "nullableType",
331
+ underlyingType: {
332
+ kind: "arrayType",
333
+ elementType: {
334
+ kind: "tupleType",
335
+ elements: [
336
+ {
337
+ type: { kind: "predefinedType", keyword: "int" },
338
+ name: "count",
339
+ },
340
+ {
341
+ type: identifierType("global::System.Collections.Generic.List", [{ kind: "predefinedType", keyword: "string" }]),
342
+ name: "values",
343
+ },
344
+ ],
345
+ },
346
+ rank: 1,
347
+ },
348
+ })).to.equal("(int count, global::System.Collections.Generic.List<string> values)[]?");
349
+ });
350
+ it("prints structured control-flow statements without text fallbacks", () => {
351
+ expect(printStatement({
352
+ kind: "ifStatement",
353
+ condition: { kind: "identifierExpression", identifier: "ready" },
354
+ thenStatement: {
355
+ kind: "blockStatement",
356
+ statements: [
357
+ {
358
+ kind: "returnStatement",
359
+ expression: {
360
+ kind: "numericLiteralExpression",
361
+ base: "decimal",
362
+ wholePart: "1",
363
+ },
364
+ },
365
+ ],
366
+ },
367
+ elseStatement: {
368
+ kind: "ifStatement",
369
+ condition: { kind: "identifierExpression", identifier: "retry" },
370
+ thenStatement: {
371
+ kind: "blockStatement",
372
+ statements: [{ kind: "continueStatement" }],
373
+ },
374
+ elseStatement: {
375
+ kind: "blockStatement",
376
+ statements: [
377
+ {
378
+ kind: "throwStatement",
379
+ expression: {
380
+ kind: "identifierExpression",
381
+ identifier: "ex",
382
+ },
383
+ },
384
+ ],
385
+ },
386
+ },
387
+ }, "")).to.equal("if (ready)\n" +
388
+ "{\n" +
389
+ " return 1;\n" +
390
+ "}\n" +
391
+ "else if (retry)\n" +
392
+ "{\n" +
393
+ " continue;\n" +
394
+ "}\n" +
395
+ "else\n" +
396
+ "{\n" +
397
+ " throw ex;\n" +
398
+ "}");
399
+ expect(printStatement({
400
+ kind: "forStatement",
401
+ declaration: {
402
+ kind: "localDeclarationStatement",
403
+ modifiers: [],
404
+ type: { kind: "predefinedType", keyword: "int" },
405
+ declarators: [
406
+ {
407
+ name: "i",
408
+ initializer: {
409
+ kind: "numericLiteralExpression",
410
+ base: "decimal",
411
+ wholePart: "0",
412
+ },
413
+ },
414
+ ],
415
+ },
416
+ condition: {
417
+ kind: "binaryExpression",
418
+ operatorToken: "<",
419
+ left: { kind: "identifierExpression", identifier: "i" },
420
+ right: {
421
+ kind: "numericLiteralExpression",
422
+ base: "decimal",
423
+ wholePart: "10",
424
+ },
425
+ },
426
+ incrementors: [
427
+ {
428
+ kind: "postfixUnaryExpression",
429
+ operatorToken: "++",
430
+ operand: { kind: "identifierExpression", identifier: "i" },
431
+ },
432
+ ],
433
+ body: {
434
+ kind: "blockStatement",
435
+ statements: [
436
+ {
437
+ kind: "expressionStatement",
438
+ expression: {
439
+ kind: "invocationExpression",
440
+ expression: {
441
+ kind: "identifierExpression",
442
+ identifier: "tick",
443
+ },
444
+ arguments: [
445
+ { kind: "identifierExpression", identifier: "i" },
446
+ ],
447
+ },
448
+ },
449
+ ],
450
+ },
451
+ }, "")).to.equal("for (int i = 0; i < 10; i++)\n" + "{\n" + " tick(i);\n" + "}");
452
+ expect(printStatement({
453
+ kind: "foreachStatement",
454
+ isAwait: true,
455
+ type: { kind: "predefinedType", keyword: "string" },
456
+ identifier: "item",
457
+ expression: { kind: "identifierExpression", identifier: "items" },
458
+ body: {
459
+ kind: "blockStatement",
460
+ statements: [
461
+ {
462
+ kind: "yieldStatement",
463
+ isBreak: false,
464
+ expression: {
465
+ kind: "identifierExpression",
466
+ identifier: "item",
467
+ },
468
+ },
469
+ ],
470
+ },
471
+ }, "")).to.equal("await foreach (string item in items)\n" +
472
+ "{\n" +
473
+ " yield return item;\n" +
474
+ "}");
475
+ expect(printStatement({
476
+ kind: "tryStatement",
477
+ body: {
478
+ kind: "blockStatement",
479
+ statements: [
480
+ {
481
+ kind: "expressionStatement",
482
+ expression: {
483
+ kind: "invocationExpression",
484
+ expression: {
485
+ kind: "identifierExpression",
486
+ identifier: "work",
487
+ },
488
+ arguments: [],
489
+ },
490
+ },
491
+ ],
492
+ },
493
+ catches: [
494
+ {
495
+ type: identifierType("global::System.Exception"),
496
+ identifier: "ex",
497
+ filter: {
498
+ kind: "identifierExpression",
499
+ identifier: "shouldHandle",
500
+ },
501
+ body: {
502
+ kind: "blockStatement",
503
+ statements: [{ kind: "throwStatement" }],
504
+ },
505
+ },
506
+ ],
507
+ finallyBody: {
508
+ kind: "blockStatement",
509
+ statements: [{ kind: "yieldStatement", isBreak: true }],
510
+ },
511
+ }, "")).to.equal("try\n" +
512
+ "{\n" +
513
+ " work();\n" +
514
+ "}\n" +
515
+ "catch (global::System.Exception ex) when (shouldHandle)\n" +
516
+ "{\n" +
517
+ " throw;\n" +
518
+ "}\n" +
519
+ "finally\n" +
520
+ "{\n" +
521
+ " yield break;\n" +
522
+ "}");
523
+ });
524
+ it("prints structured switch expressions, patterns, and switch statements", () => {
525
+ expect(printPattern({
526
+ kind: "declarationPattern",
527
+ type: identifierType("global::System.Exception"),
528
+ designation: "ex",
529
+ })).to.equal("global::System.Exception ex");
530
+ expect(printExpression({
531
+ kind: "switchExpression",
532
+ governingExpression: {
533
+ kind: "identifierExpression",
534
+ identifier: "value",
535
+ },
536
+ arms: [
537
+ {
538
+ pattern: {
539
+ kind: "constantPattern",
540
+ expression: decimalIntegerLiteral(0),
541
+ },
542
+ expression: stringLiteral("zero"),
543
+ },
544
+ {
545
+ pattern: {
546
+ kind: "declarationPattern",
547
+ type: identifierType("global::System.Exception"),
548
+ designation: "ex",
549
+ },
550
+ whenClause: { kind: "identifierExpression", identifier: "flag" },
551
+ expression: {
552
+ kind: "memberAccessExpression",
553
+ expression: { kind: "identifierExpression", identifier: "ex" },
554
+ memberName: "Message",
555
+ },
556
+ },
557
+ {
558
+ pattern: { kind: "discardPattern" },
559
+ expression: stringLiteral("other"),
560
+ },
561
+ ],
562
+ })).to.equal('value switch { 0 => "zero", global::System.Exception ex when flag => ex.Message, _ => "other" }');
563
+ expect(printStatement({
564
+ kind: "switchStatement",
565
+ expression: { kind: "identifierExpression", identifier: "value" },
566
+ sections: [
567
+ {
568
+ labels: [
569
+ {
570
+ kind: "caseSwitchLabel",
571
+ value: decimalIntegerLiteral(0),
572
+ },
573
+ ],
574
+ statements: [
575
+ {
576
+ kind: "returnStatement",
577
+ expression: stringLiteral("zero"),
578
+ },
579
+ ],
580
+ },
581
+ {
582
+ labels: [
583
+ {
584
+ kind: "casePatternSwitchLabel",
585
+ pattern: {
586
+ kind: "declarationPattern",
587
+ type: identifierType("global::System.Exception"),
588
+ designation: "ex",
589
+ },
590
+ whenClause: {
591
+ kind: "identifierExpression",
592
+ identifier: "flag",
593
+ },
594
+ },
595
+ ],
596
+ statements: [{ kind: "breakStatement" }],
597
+ },
598
+ {
599
+ labels: [{ kind: "defaultSwitchLabel" }],
600
+ statements: [{ kind: "returnStatement" }],
601
+ },
602
+ ],
603
+ }, "")).to.equal("switch (value)\n" +
604
+ "{\n" +
605
+ " case 0:\n" +
606
+ ' return "zero";\n' +
607
+ " case global::System.Exception ex when flag:\n" +
608
+ " break;\n" +
609
+ " default:\n" +
610
+ " return;\n" +
611
+ "}");
612
+ });
613
+ it("prints remaining expression and statement node kinds structurally", () => {
614
+ const expressionCases = [
615
+ [
616
+ {
617
+ kind: "conditionalMemberAccessExpression",
618
+ expression: { kind: "identifierExpression", identifier: "value" },
619
+ memberName: "Length",
620
+ },
621
+ "value?.Length",
622
+ ],
623
+ [
624
+ {
625
+ kind: "conditionalElementAccessExpression",
626
+ expression: { kind: "identifierExpression", identifier: "value" },
627
+ arguments: [decimalIntegerLiteral(0)],
628
+ },
629
+ "value?[0]",
630
+ ],
631
+ [
632
+ {
633
+ kind: "arrayCreationExpression",
634
+ elementType: { kind: "predefinedType", keyword: "int" },
635
+ sizeExpression: decimalIntegerLiteral(3),
636
+ initializer: [decimalIntegerLiteral(1), decimalIntegerLiteral(2)],
637
+ },
638
+ "new int[3] { 1, 2 }",
639
+ ],
640
+ [
641
+ {
642
+ kind: "stackAllocArrayCreationExpression",
643
+ elementType: { kind: "predefinedType", keyword: "byte" },
644
+ sizeExpression: decimalIntegerLiteral(16),
645
+ },
646
+ "stackalloc byte[16]",
647
+ ],
648
+ [
649
+ {
650
+ kind: "castExpression",
651
+ type: { kind: "predefinedType", keyword: "int" },
652
+ expression: {
653
+ kind: "binaryExpression",
654
+ operatorToken: "+",
655
+ left: { kind: "identifierExpression", identifier: "left" },
656
+ right: { kind: "identifierExpression", identifier: "right" },
657
+ },
658
+ },
659
+ "(int)(left + right)",
660
+ ],
661
+ [
662
+ {
663
+ kind: "asExpression",
664
+ expression: { kind: "identifierExpression", identifier: "value" },
665
+ type: identifierType("global::System.IDisposable"),
666
+ },
667
+ "value as global::System.IDisposable",
668
+ ],
669
+ [
670
+ {
671
+ kind: "isExpression",
672
+ expression: { kind: "identifierExpression", identifier: "value" },
673
+ pattern: { kind: "varPattern", designation: "captured" },
674
+ },
675
+ "value is var captured",
676
+ ],
677
+ [
678
+ {
679
+ kind: "defaultExpression",
680
+ type: {
681
+ kind: "nullableType",
682
+ underlyingType: { kind: "predefinedType", keyword: "int" },
683
+ },
684
+ },
685
+ "default(int?)",
686
+ ],
687
+ [
688
+ {
689
+ kind: "sizeOfExpression",
690
+ type: { kind: "predefinedType", keyword: "decimal" },
691
+ },
692
+ "sizeof(decimal)",
693
+ ],
694
+ [
695
+ {
696
+ kind: "awaitExpression",
697
+ expression: { kind: "identifierExpression", identifier: "task" },
698
+ },
699
+ "await task",
700
+ ],
701
+ [
702
+ {
703
+ kind: "suppressNullableWarningExpression",
704
+ expression: { kind: "identifierExpression", identifier: "maybe" },
705
+ },
706
+ "maybe!",
707
+ ],
708
+ [
709
+ {
710
+ kind: "typeofExpression",
711
+ type: identifierType("global::System.IntPtr"),
712
+ },
713
+ "typeof(global::System.IntPtr)",
714
+ ],
715
+ [
716
+ {
717
+ kind: "argumentModifierExpression",
718
+ modifier: "ref",
719
+ expression: { kind: "identifierExpression", identifier: "value" },
720
+ },
721
+ "ref value",
722
+ ],
723
+ [
724
+ {
725
+ kind: "tupleExpression",
726
+ elements: [
727
+ decimalIntegerLiteral(1),
728
+ { kind: "identifierExpression", identifier: "name" },
729
+ ],
730
+ },
731
+ "(1, name)",
732
+ ],
733
+ [
734
+ {
735
+ kind: "throwExpression",
736
+ expression: { kind: "identifierExpression", identifier: "ex" },
737
+ },
738
+ "throw ex",
739
+ ],
740
+ ];
741
+ for (const [ast, expected] of expressionCases) {
742
+ expect(printExpression(ast)).to.equal(expected);
743
+ }
744
+ expect(printPattern({
745
+ kind: "typePattern",
746
+ type: identifierType("global::System.Exception"),
747
+ })).to.equal("global::System.Exception");
748
+ expect(printPattern({
749
+ kind: "varPattern",
750
+ designation: "captured",
751
+ })).to.equal("var captured");
752
+ expect(printPattern({
753
+ kind: "negatedPattern",
754
+ pattern: { kind: "discardPattern" },
755
+ })).to.equal("not _");
756
+ expect(printStatement({
757
+ kind: "whileStatement",
758
+ condition: { kind: "identifierExpression", identifier: "keepGoing" },
759
+ body: {
760
+ kind: "blockStatement",
761
+ statements: [{ kind: "emptyStatement" }],
762
+ },
763
+ }, "")).to.equal("while (keepGoing)\n{\n ;\n}");
764
+ expect(printStatement({
765
+ kind: "localFunctionStatement",
766
+ modifiers: ["static"],
767
+ returnType: { kind: "predefinedType", keyword: "void" },
768
+ name: "consume",
769
+ parameters: [
770
+ {
771
+ name: "item",
772
+ type: { kind: "predefinedType", keyword: "string" },
773
+ },
774
+ ],
775
+ body: {
776
+ kind: "blockStatement",
777
+ statements: [
778
+ {
779
+ kind: "expressionStatement",
780
+ expression: {
781
+ kind: "invocationExpression",
782
+ expression: {
783
+ kind: "identifierExpression",
784
+ identifier: "sink",
785
+ },
786
+ arguments: [
787
+ { kind: "identifierExpression", identifier: "item" },
788
+ ],
789
+ },
790
+ },
791
+ ],
792
+ },
793
+ }, "")).to.equal("static void consume(string item)\n{\n sink(item);\n}");
794
+ });
795
+ });
796
+ //# sourceMappingURL=printer.test.js.map