@ugo-studio/jspp 0.1.3 → 0.1.5

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 (83) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/scope.js +33 -4
  3. package/dist/analysis/typeAnalyzer.js +260 -21
  4. package/dist/ast/symbols.js +29 -0
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -31
  10. package/dist/core/codegen/class-handlers.js +131 -0
  11. package/dist/core/codegen/control-flow-handlers.js +474 -0
  12. package/dist/core/codegen/declaration-handlers.js +36 -15
  13. package/dist/core/codegen/expression-handlers.js +579 -125
  14. package/dist/core/codegen/function-handlers.js +222 -37
  15. package/dist/core/codegen/helpers.js +158 -4
  16. package/dist/core/codegen/index.js +20 -8
  17. package/dist/core/codegen/literal-handlers.js +18 -6
  18. package/dist/core/codegen/statement-handlers.js +171 -228
  19. package/dist/core/codegen/visitor.js +31 -3
  20. package/package.json +3 -3
  21. package/src/prelude/any_value.hpp +510 -633
  22. package/src/prelude/any_value_access.hpp +151 -0
  23. package/src/prelude/any_value_defines.hpp +190 -0
  24. package/src/prelude/any_value_helpers.hpp +139 -225
  25. package/src/prelude/exception.hpp +32 -0
  26. package/src/prelude/exception_helpers.hpp +49 -0
  27. package/src/prelude/index.hpp +25 -9
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +14 -13
  30. package/src/prelude/library/error.hpp +113 -0
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +35 -4
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +2 -2
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +131 -0
  38. package/src/prelude/library/symbol.hpp +46 -59
  39. package/src/prelude/library/timer.hpp +92 -0
  40. package/src/prelude/scheduler.hpp +145 -0
  41. package/src/prelude/types.hpp +58 -1
  42. package/src/prelude/utils/access.hpp +345 -0
  43. package/src/prelude/utils/assignment_operators.hpp +99 -0
  44. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  45. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  46. package/src/prelude/utils/log_any_value/function.hpp +39 -0
  47. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  48. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  49. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  50. package/src/prelude/utils/log_any_value/object.hpp +136 -0
  51. package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
  52. package/src/prelude/utils/operators.hpp +751 -0
  53. package/src/prelude/utils/well_known_symbols.hpp +25 -0
  54. package/src/prelude/values/array.hpp +10 -7
  55. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  56. package/src/prelude/values/function.hpp +85 -51
  57. package/src/prelude/values/helpers/array.hpp +80 -35
  58. package/src/prelude/values/helpers/function.hpp +110 -77
  59. package/src/prelude/values/helpers/iterator.hpp +16 -10
  60. package/src/prelude/values/helpers/object.hpp +85 -10
  61. package/src/prelude/values/helpers/promise.hpp +181 -0
  62. package/src/prelude/values/helpers/string.hpp +3 -3
  63. package/src/prelude/values/helpers/symbol.hpp +2 -2
  64. package/src/prelude/values/iterator.hpp +14 -6
  65. package/src/prelude/values/object.hpp +14 -3
  66. package/src/prelude/values/promise.hpp +73 -0
  67. package/src/prelude/values/prototypes/array.hpp +855 -16
  68. package/src/prelude/values/prototypes/function.hpp +4 -4
  69. package/src/prelude/values/prototypes/iterator.hpp +11 -10
  70. package/src/prelude/values/prototypes/number.hpp +153 -0
  71. package/src/prelude/values/prototypes/object.hpp +26 -0
  72. package/src/prelude/values/prototypes/promise.hpp +134 -0
  73. package/src/prelude/values/prototypes/string.hpp +29 -29
  74. package/src/prelude/values/prototypes/symbol.hpp +22 -3
  75. package/src/prelude/values/shape.hpp +52 -0
  76. package/src/prelude/values/string.hpp +1 -1
  77. package/src/prelude/values/symbol.hpp +1 -1
  78. package/src/prelude/access.hpp +0 -91
  79. package/src/prelude/error.hpp +0 -31
  80. package/src/prelude/error_helpers.hpp +0 -59
  81. package/src/prelude/log_string.hpp +0 -407
  82. package/src/prelude/operators.hpp +0 -256
  83. package/src/prelude/well_known_symbols.hpp +0 -14
@@ -0,0 +1,474 @@
1
+ import ts from "typescript";
2
+ import { DeclaredSymbols, DeclaredSymbolType } from "../../ast/symbols";
3
+ import { CodeGenerator } from "./";
4
+ export function visitForStatement(node, context) {
5
+ const forStmt = node;
6
+ let code = "";
7
+ if (context.currentLabel) {
8
+ code += `${this.indent()}${context.currentLabel}: {\n`;
9
+ this.indentationLevel++;
10
+ }
11
+ this.indentationLevel++; // Enter a new scope for the for loop
12
+ // Handle initializer
13
+ let initializerCode = "";
14
+ let conditionContext = {
15
+ ...context,
16
+ topLevelScopeSymbols: this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols),
17
+ };
18
+ if (forStmt.initializer) {
19
+ if (ts.isVariableDeclarationList(forStmt.initializer)) {
20
+ const varDeclList = forStmt.initializer;
21
+ const isLetOrConst = (varDeclList.flags &
22
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
23
+ 0;
24
+ if (isLetOrConst) {
25
+ const decl = varDeclList.declarations[0];
26
+ if (decl) {
27
+ const name = decl.name.getText();
28
+ const initValue = decl.initializer
29
+ ? this.visit(decl.initializer, context)
30
+ : "jspp::Constants::UNDEFINED";
31
+ const scope = this.getScopeForNode(decl);
32
+ const typeInfo = this.typeAnalyzer.scopeManager
33
+ .lookupFromScope(name, scope);
34
+ conditionContext.localScopeSymbols = new DeclaredSymbols();
35
+ conditionContext.localScopeSymbols.set(name, {
36
+ type: DeclaredSymbolType.letOrConst,
37
+ checkedIfUninitialized: true,
38
+ });
39
+ if (typeInfo.needsHeapAllocation) {
40
+ initializerCode =
41
+ `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
42
+ }
43
+ else {
44
+ initializerCode =
45
+ `jspp::AnyValue ${name} = ${initValue}`;
46
+ }
47
+ }
48
+ }
49
+ else {
50
+ initializerCode = this.visit(forStmt.initializer, {
51
+ ...context,
52
+ isAssignmentOnly: true,
53
+ });
54
+ }
55
+ }
56
+ else {
57
+ initializerCode = this.visit(forStmt.initializer, context);
58
+ }
59
+ }
60
+ code += `${this.indent()}for (${initializerCode}; `;
61
+ if (forStmt.condition) {
62
+ code += `is_truthy(${this.visit(forStmt.condition, conditionContext)})`;
63
+ }
64
+ code += "; ";
65
+ if (forStmt.incrementor) {
66
+ code += this.visit(forStmt.incrementor, context);
67
+ }
68
+ code += ") ";
69
+ const statementCode = this.visit(forStmt.statement, {
70
+ ...context,
71
+ currentLabel: undefined,
72
+ isFunctionBody: false,
73
+ });
74
+ if (ts.isBlock(node.statement)) {
75
+ let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
76
+ if (context.currentLabel) {
77
+ blockContent +=
78
+ `${this.indent()}${context.currentLabel}_continue:;\n`;
79
+ }
80
+ code += `{\n${blockContent}}\n`;
81
+ }
82
+ else {
83
+ code += `{\n`;
84
+ code += statementCode;
85
+ if (context.currentLabel) {
86
+ code += `${this.indent()}${context.currentLabel}_continue:;\n`;
87
+ }
88
+ code += `${this.indent()}}\n`;
89
+ }
90
+ this.indentationLevel--; // Exit the scope for the for loop
91
+ if (context.currentLabel) {
92
+ this.indentationLevel--;
93
+ code += `${this.indent()}}\n`;
94
+ code +=
95
+ `${this.indent()}${context.currentLabel}_break:; // break target\n`;
96
+ }
97
+ return code;
98
+ }
99
+ export function visitForInStatement(node, context) {
100
+ const forIn = node;
101
+ let code = "";
102
+ if (context.currentLabel) {
103
+ code += `${this.indent()}${context.currentLabel}: {\n`;
104
+ this.indentationLevel++;
105
+ }
106
+ code += `${this.indent()}{\n`;
107
+ this.indentationLevel++; // Enter a new scope for the for-in loop
108
+ let varName = "";
109
+ let assignmentTarget = "";
110
+ if (ts.isVariableDeclarationList(forIn.initializer)) {
111
+ const decl = forIn.initializer.declarations[0];
112
+ if (decl) {
113
+ varName = decl.name.getText();
114
+ const scope = this.getScopeForNode(decl);
115
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
116
+ if (typeInfo.needsHeapAllocation) {
117
+ code +=
118
+ `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
119
+ assignmentTarget = `*${varName}`;
120
+ }
121
+ else {
122
+ code +=
123
+ `${this.indent()}jspp::AnyValue ${varName} = jspp::Constants::UNDEFINED;\n`;
124
+ assignmentTarget = varName;
125
+ }
126
+ }
127
+ }
128
+ else if (ts.isIdentifier(forIn.initializer)) {
129
+ varName = forIn.initializer.getText();
130
+ const scope = this.getScopeForNode(forIn.initializer);
131
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
132
+ assignmentTarget = typeInfo.needsHeapAllocation
133
+ ? `*${varName}`
134
+ : varName;
135
+ }
136
+ const expr = forIn.expression;
137
+ const exprText = this.visit(expr, context);
138
+ let derefExpr = exprText;
139
+ if (ts.isIdentifier(expr)) {
140
+ const scope = this.getScopeForNode(expr);
141
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.getText(), scope);
142
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
143
+ }
144
+ const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
145
+ code +=
146
+ `${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
147
+ code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
148
+ this.indentationLevel++;
149
+ code +=
150
+ `${this.indent()}${assignmentTarget} = jspp::AnyValue::make_string(${varName}_str);\n`;
151
+ code += this.visit(forIn.statement, {
152
+ ...context,
153
+ currentLabel: undefined,
154
+ isFunctionBody: false,
155
+ });
156
+ this.indentationLevel--;
157
+ if (context.currentLabel) {
158
+ code += `${this.indent()}${context.currentLabel}_continue:;\n`;
159
+ }
160
+ code += `${this.indent()}}\n`;
161
+ this.indentationLevel--; // Exit the scope for the for-in loop
162
+ code += `${this.indent()}}\n`;
163
+ if (context.currentLabel) {
164
+ this.indentationLevel--;
165
+ code += `${this.indent()}}\n`;
166
+ code +=
167
+ `${this.indent()}${context.currentLabel}_break:; // break target\n`;
168
+ }
169
+ return code;
170
+ }
171
+ export function visitForOfStatement(node, context) {
172
+ const forOf = node;
173
+ let code = "";
174
+ if (context.currentLabel) {
175
+ code += `${this.indent()}${context.currentLabel}: {\n`;
176
+ this.indentationLevel++;
177
+ }
178
+ this.indentationLevel++; // Enter a new scope for the for-of loop
179
+ let elemName = "";
180
+ let assignmentTarget = "";
181
+ code += `${this.indent()}{\n`;
182
+ if (ts.isVariableDeclarationList(forOf.initializer)) {
183
+ const decl = forOf.initializer.declarations[0];
184
+ if (decl) {
185
+ elemName = decl.name.getText();
186
+ const scope = this.getScopeForNode(decl);
187
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
188
+ if (typeInfo.needsHeapAllocation) {
189
+ code +=
190
+ `${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
191
+ assignmentTarget = `*${elemName}`;
192
+ }
193
+ else {
194
+ code +=
195
+ `${this.indent()}jspp::AnyValue ${elemName} = jspp::Constants::UNDEFINED;\n`;
196
+ assignmentTarget = elemName;
197
+ }
198
+ }
199
+ }
200
+ else if (ts.isIdentifier(forOf.initializer)) {
201
+ elemName = forOf.initializer.getText();
202
+ const scope = this.getScopeForNode(forOf.initializer);
203
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
204
+ assignmentTarget = typeInfo.needsHeapAllocation
205
+ ? `*${elemName}`
206
+ : elemName;
207
+ }
208
+ const iterableExpr = this.visit(forOf.expression, context);
209
+ let derefIterable = iterableExpr;
210
+ if (ts.isIdentifier(forOf.expression)) {
211
+ const scope = this.getScopeForNode(forOf.expression);
212
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(forOf.expression.getText(), scope);
213
+ const varName = this.getJsVarName(forOf.expression);
214
+ derefIterable = this.getDerefCode(iterableExpr, varName, context, typeInfo);
215
+ }
216
+ const declaredSymbols = this.getDeclaredSymbols(forOf.statement);
217
+ const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
218
+ const iterator = this.generateUniqueName("__iter", declaredSymbols);
219
+ const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
220
+ const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
221
+ const varName = this.getJsVarName(forOf.expression);
222
+ code += `${this.indent()}auto ${iterableRef} = ${derefIterable};\n`;
223
+ code +=
224
+ `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
225
+ code +=
226
+ `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
227
+ code +=
228
+ `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
229
+ code +=
230
+ `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
231
+ this.indentationLevel++;
232
+ code +=
233
+ `${this.indent()}${assignmentTarget} = ${nextRes}.get_own_property("value");\n`;
234
+ code += this.visit(forOf.statement, {
235
+ ...context,
236
+ currentLabel: undefined,
237
+ isFunctionBody: false,
238
+ });
239
+ if (context.currentLabel) {
240
+ code += `${this.indent()}${context.currentLabel}_continue:;\n`;
241
+ }
242
+ code +=
243
+ `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
244
+ this.indentationLevel--;
245
+ code += `${this.indent()}}\n`;
246
+ this.indentationLevel--; // Exit the scope for the for-of loop
247
+ code += `${this.indent()}}\n`;
248
+ if (context.currentLabel) {
249
+ this.indentationLevel--;
250
+ code += `${this.indent()}}\n`;
251
+ code +=
252
+ `${this.indent()}${context.currentLabel}_break:; // break target\n`;
253
+ }
254
+ return code;
255
+ }
256
+ export function visitWhileStatement(node, context) {
257
+ const condition = node.expression;
258
+ const conditionText = condition.kind === ts.SyntaxKind.TrueKeyword ||
259
+ condition.kind === ts.SyntaxKind.FalseKeyword
260
+ ? condition.getText()
261
+ : `is_truthy(${this.visit(condition, context)})`;
262
+ let code = "";
263
+ if (context.currentLabel) {
264
+ code += `${this.indent()}${context.currentLabel}: {\n`;
265
+ this.indentationLevel++;
266
+ }
267
+ code += `${this.indent()}while (${conditionText}) `;
268
+ const statementCode = this.visit(node.statement, {
269
+ ...context,
270
+ currentLabel: undefined,
271
+ isFunctionBody: false,
272
+ });
273
+ if (ts.isBlock(node.statement)) {
274
+ let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
275
+ if (context.currentLabel) {
276
+ blockContent +=
277
+ `${this.indent()}${context.currentLabel}_continue:;\n`;
278
+ }
279
+ code += `{\n${blockContent}}\n`;
280
+ }
281
+ else {
282
+ code += `{\n`;
283
+ this.indentationLevel++;
284
+ code += statementCode;
285
+ if (context.currentLabel) {
286
+ code += `${this.indent()}${context.currentLabel}_continue:;\n`;
287
+ }
288
+ this.indentationLevel--;
289
+ code += `${this.indent()}}\n`;
290
+ }
291
+ if (context.currentLabel) {
292
+ this.indentationLevel--;
293
+ code += `${this.indent()}}\n`;
294
+ code +=
295
+ `${this.indent()}${context.currentLabel}_break:; // break target\n`;
296
+ }
297
+ return code;
298
+ }
299
+ export function visitDoStatement(node, context) {
300
+ const condition = node.expression;
301
+ const conditionText = `is_truthy(${this.visit(condition, context)})`;
302
+ let code = "";
303
+ if (context.currentLabel) {
304
+ code += `${this.indent()}${context.currentLabel}: {\n`;
305
+ this.indentationLevel++;
306
+ }
307
+ code += `${this.indent()}do `;
308
+ const statementCode = this.visit(node.statement, {
309
+ ...context,
310
+ currentLabel: undefined,
311
+ isFunctionBody: false,
312
+ });
313
+ if (ts.isBlock(node.statement)) {
314
+ let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
315
+ if (context.currentLabel) {
316
+ blockContent +=
317
+ `${this.indent()}${context.currentLabel}_continue:;\n`;
318
+ }
319
+ code += `{\n${blockContent}}`;
320
+ }
321
+ else {
322
+ code += `{\n`;
323
+ this.indentationLevel++;
324
+ code += statementCode;
325
+ if (context.currentLabel) {
326
+ code += `${this.indent()}${context.currentLabel}_continue:;\n`;
327
+ }
328
+ this.indentationLevel--;
329
+ code += `${this.indent()}}`;
330
+ }
331
+ code += ` while (${conditionText});\n`;
332
+ if (context.currentLabel) {
333
+ this.indentationLevel--;
334
+ code += `${this.indent()}}\n`;
335
+ code +=
336
+ `${this.indent()}${context.currentLabel}_break:; // break target\n`;
337
+ }
338
+ return code;
339
+ }
340
+ export function visitSwitchStatement(node, context) {
341
+ const switchStmt = node;
342
+ let code = "";
343
+ const declaredSymbols = this.getDeclaredSymbols(switchStmt.caseBlock);
344
+ const switchBreakLabel = this.generateUniqueName("__switch_break_", declaredSymbols);
345
+ const fallthroughVar = this.generateUniqueName("__switch_fallthrough_", declaredSymbols);
346
+ if (context.currentLabel) {
347
+ code += `${this.indent()}${context.currentLabel}: {\n`;
348
+ this.indentationLevel++;
349
+ }
350
+ code += `${this.indent()}{\n`; // Wrap the entire switch logic in a block
351
+ this.indentationLevel++;
352
+ // Evaluate the switch expression once
353
+ const expressionCode = this.visit(switchStmt.expression, context);
354
+ const switchValueVar = this.generateUniqueName("__switch_value_", declaredSymbols);
355
+ code +=
356
+ `${this.indent()}const jspp::AnyValue ${switchValueVar} = ${expressionCode};\n`;
357
+ code += `${this.indent()}bool ${fallthroughVar} = false;\n`;
358
+ // Hoist variable declarations
359
+ const hoistedSymbols = new DeclaredSymbols();
360
+ for (const clause of switchStmt.caseBlock.clauses) {
361
+ if (ts.isCaseClause(clause) || ts.isDefaultClause(clause)) {
362
+ for (const stmt of clause.statements) {
363
+ if (ts.isVariableStatement(stmt)) {
364
+ const varDecls = stmt.declarationList.declarations;
365
+ for (const decl of varDecls) {
366
+ code += this.hoistDeclaration(decl, hoistedSymbols);
367
+ }
368
+ }
369
+ else if (ts.isFunctionDeclaration(stmt)) {
370
+ code += this.hoistDeclaration(stmt, hoistedSymbols);
371
+ }
372
+ }
373
+ }
374
+ }
375
+ // Prepare scope symbols for the switch block
376
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
377
+ let firstIf = true;
378
+ for (const clause of switchStmt.caseBlock.clauses) {
379
+ if (ts.isCaseClause(clause)) {
380
+ const caseExprCode = this.visit(clause.expression, {
381
+ ...context,
382
+ currentLabel: undefined, // Clear currentLabel for nested visits
383
+ });
384
+ let condition = "";
385
+ if (firstIf) {
386
+ condition =
387
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
388
+ code += `${this.indent()}if ${condition} {\n`;
389
+ firstIf = false;
390
+ }
391
+ else {
392
+ condition =
393
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
394
+ code += `${this.indent()}if ${condition} {\n`;
395
+ }
396
+ this.indentationLevel++;
397
+ code += `${this.indent()}${fallthroughVar} = true;\n`;
398
+ for (const stmt of clause.statements) {
399
+ if (ts.isFunctionDeclaration(stmt)) {
400
+ const funcName = stmt.name?.getText();
401
+ if (funcName) {
402
+ const contextForFunction = {
403
+ ...context,
404
+ topLevelScopeSymbols,
405
+ localScopeSymbols: hoistedSymbols,
406
+ };
407
+ const lambda = this.generateLambda(stmt, contextForFunction, { isAssignment: true });
408
+ code += `${this.indent()}*${funcName} = ${lambda};\n`;
409
+ }
410
+ }
411
+ else {
412
+ code += this.visit(stmt, {
413
+ ...context,
414
+ switchBreakLabel,
415
+ currentLabel: undefined, // Clear currentLabel for nested visits
416
+ topLevelScopeSymbols,
417
+ localScopeSymbols: hoistedSymbols,
418
+ derefBeforeAssignment: true,
419
+ isAssignmentOnly: ts.isVariableStatement(stmt),
420
+ });
421
+ }
422
+ }
423
+ this.indentationLevel--;
424
+ code += `${this.indent()}}\n`;
425
+ }
426
+ else if (ts.isDefaultClause(clause)) {
427
+ // Default clause
428
+ code +=
429
+ `${this.indent()}if (!${fallthroughVar}) ${fallthroughVar} = true;\n`;
430
+ if (firstIf) {
431
+ code += `${this.indent()}if (true) {\n`; // Always execute if no prior cases match and it's the first clause
432
+ firstIf = false;
433
+ }
434
+ else {
435
+ code += `${this.indent()}if (${fallthroughVar}) {\n`; // Only execute if no prior case (or default) has matched and caused fallthrough
436
+ }
437
+ this.indentationLevel++;
438
+ for (const stmt of clause.statements) {
439
+ code += this.visit(stmt, {
440
+ ...context,
441
+ switchBreakLabel,
442
+ currentLabel: undefined, // Clear currentLabel for nested visits
443
+ topLevelScopeSymbols,
444
+ localScopeSymbols: hoistedSymbols,
445
+ derefBeforeAssignment: true,
446
+ isAssignmentOnly: ts.isVariableStatement(stmt),
447
+ });
448
+ }
449
+ this.indentationLevel--;
450
+ code += `${this.indent()}}\n`;
451
+ }
452
+ }
453
+ this.indentationLevel--;
454
+ code += `${this.indent()}}\n`; // End of the switch block
455
+ code +=
456
+ `${this.indent()}${switchBreakLabel}:; // break target for switch\n`;
457
+ if (context.currentLabel) {
458
+ this.indentationLevel--;
459
+ code += `${this.indent()}}\n`;
460
+ code +=
461
+ `${this.indent()}${context.currentLabel}_break:; // break target for labeled switch\n`;
462
+ }
463
+ return code;
464
+ }
465
+ export function visitCaseClause(node, context) {
466
+ // Case clauses are handled inline by visitSwitchStatement, not generated directly
467
+ // This function will likely not be called, or can return an empty string
468
+ return "";
469
+ }
470
+ export function visitDefaultClause(node, context) {
471
+ // Default clauses are handled inline by visitSwitchStatement, not generated directly
472
+ // This function will likely not be called, or can return an empty string
473
+ return "";
474
+ }
@@ -9,41 +9,62 @@ export function visitVariableDeclarationList(node, context) {
9
9
  export function visitVariableDeclaration(node, context) {
10
10
  const varDecl = node;
11
11
  const name = varDecl.name.getText();
12
+ const scope = this.getScopeForNode(varDecl);
13
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
14
+ // Mark the symbol as checked
15
+ this.markSymbolAsChecked(name, context.topLevelScopeSymbols, context.localScopeSymbols);
12
16
  let initializer = "";
13
17
  if (varDecl.initializer) {
14
18
  const initExpr = varDecl.initializer;
15
- let initText = this.visit(initExpr, context);
19
+ const initContext = {
20
+ ...context,
21
+ lambdaName: ts.isArrowFunction(initExpr) ? name : undefined, // Pass the variable name for arrow functions
22
+ };
23
+ let initText = ts.isNumericLiteral(initExpr)
24
+ ? initExpr.getText()
25
+ : this.visit(initExpr, initContext);
16
26
  if (ts.isIdentifier(initExpr)) {
17
- const scope = this.getScopeForNode(initExpr);
18
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initExpr.text, scope);
27
+ const initScope = this.getScopeForNode(initExpr);
28
+ const initTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initExpr.text, initScope);
19
29
  const varName = this.getJsVarName(initExpr);
20
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
21
- initText = `jspp::Access::deref(${initText}, ${varName})`;
30
+ if (initTypeInfo &&
31
+ !initTypeInfo.isParameter &&
32
+ !initTypeInfo.isBuiltin) {
33
+ initText = this.getDerefCode(initText, varName, initContext, initTypeInfo);
22
34
  }
23
35
  }
24
36
  initializer = " = " + initText;
25
37
  }
26
38
  const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
39
+ const shouldDeref = context.derefBeforeAssignment &&
40
+ (!context.localScopeSymbols.has(name));
41
+ const assignmentTarget = shouldDeref
42
+ ? this.getDerefCode(name, name, context, typeInfo)
43
+ : (typeInfo.needsHeapAllocation ? `*${name}` : name);
27
44
  if (isLetOrConst) {
28
45
  // If there's no initializer, it should be assigned undefined.
29
- if (!initializer)
30
- return `*${name} = jspp::AnyValue::make_undefined()`;
31
- return `*${name}${initializer}`;
46
+ if (!initializer) {
47
+ return `${assignmentTarget} = jspp::Constants::UNDEFINED`;
48
+ }
49
+ return `${assignmentTarget}${initializer}`;
32
50
  }
33
51
  // For 'var', it's a bit more complex.
34
- // If we are in a non-function-body block, 'var' is hoisted, so it's an assignment.
35
- // If we are at the top level or in a function body, it's a declaration if not already hoisted.
36
- // The current logic hoists at the function level, so we need to decide if this is the *hoisting* declaration or a later assignment.
37
- // The `isAssignmentOnly` flag helps here.
38
52
  if (context.isAssignmentOnly) {
39
53
  if (!initializer)
40
54
  return "";
41
- return `*${name}${initializer}`;
55
+ return `${assignmentTarget}${initializer}`;
42
56
  }
43
57
  else {
58
+ // This case should not be hit with the new hoisting logic,
59
+ // but is kept for safety.
44
60
  const initValue = initializer
45
61
  ? initializer.substring(3)
46
- : "jspp::AnyValue::make_undefined()";
47
- return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
62
+ : "jspp::Constants::UNDEFINED";
63
+ if (typeInfo.needsHeapAllocation) {
64
+ return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
65
+ }
66
+ else {
67
+ return `jspp::AnyValue ${name} = ${initValue}`;
68
+ }
48
69
  }
49
70
  }