@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
@@ -1,42 +1,48 @@
1
1
  import ts from "typescript";
2
+ import { DeclaredSymbols } from "../../ast/symbols";
2
3
  import { CodeGenerator } from "./";
4
+ import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers";
3
5
  export function visitSourceFile(node, context) {
4
6
  const sourceFile = node;
5
7
  let code = "";
6
- const varDecls = sourceFile.statements
7
- .filter(ts.isVariableStatement)
8
- .flatMap((stmt) => stmt.declarationList.declarations);
8
+ // 1. Collect all var declarations (recursively) + top-level let/const
9
+ const varDecls = collectFunctionScopedDeclarations(sourceFile);
10
+ const topLevelLetConst = collectBlockScopedDeclarations(sourceFile.statements);
9
11
  const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
10
- const hoistedSymbols = new Set();
11
- // 1. Hoist function declarations
12
+ const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
13
+ const hoistedSymbols = new DeclaredSymbols();
14
+ // Hoist function declarations
12
15
  funcDecls.forEach((func) => {
13
- const funcName = func.name?.getText();
14
- if (funcName && !hoistedSymbols.has(funcName)) {
15
- hoistedSymbols.add(funcName);
16
- code +=
17
- `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
18
- }
16
+ code += this.hoistDeclaration(func, hoistedSymbols);
17
+ });
18
+ // Hoist class declarations
19
+ classDecls.forEach((cls) => {
20
+ code += this.hoistDeclaration(cls, hoistedSymbols);
19
21
  });
20
- // Hoist variable declarations
22
+ // Hoist variable declarations (var)
21
23
  varDecls.forEach((decl) => {
22
- const name = decl.name.getText();
23
- if (hoistedSymbols.has(name)) {
24
- return;
25
- }
26
- hoistedSymbols.add(name);
27
- const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
28
- 0;
29
- const initializer = isLetOrConst
30
- ? "jspp::AnyValue::make_uninitialized()"
31
- : "jspp::AnyValue::make_undefined()";
32
- code +=
33
- `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
24
+ code += this.hoistDeclaration(decl, hoistedSymbols);
25
+ });
26
+ // Hoist top-level let/const
27
+ topLevelLetConst.forEach((decl) => {
28
+ code += this.hoistDeclaration(decl, hoistedSymbols);
34
29
  });
30
+ // Compile symbols for other statements (excluding function)
31
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
32
+ const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
35
33
  // 2. Assign all hoisted functions first
34
+ const contextForFunctions = {
35
+ ...context,
36
+ localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
37
+ };
36
38
  funcDecls.forEach((stmt) => {
37
39
  const funcName = stmt.name?.getText();
38
40
  if (funcName) {
39
- const lambda = this.generateLambda(stmt, true);
41
+ this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
42
+ this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
43
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
44
+ isAssignment: true,
45
+ });
40
46
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
41
47
  }
42
48
  });
@@ -51,6 +57,8 @@ export function visitSourceFile(node, context) {
51
57
  0;
52
58
  const contextForVisit = {
53
59
  ...context,
60
+ topLevelScopeSymbols,
61
+ localScopeSymbols,
54
62
  isAssignmentOnly: !isLetOrConst,
55
63
  };
56
64
  const assignments = this.visit(stmt.declarationList, contextForVisit);
@@ -59,7 +67,12 @@ export function visitSourceFile(node, context) {
59
67
  }
60
68
  }
61
69
  else {
62
- code += this.visit(stmt, context);
70
+ code += this.visit(stmt, {
71
+ ...context,
72
+ isFunctionBody: false,
73
+ topLevelScopeSymbols,
74
+ localScopeSymbols,
75
+ });
63
76
  }
64
77
  });
65
78
  return code;
@@ -68,40 +81,39 @@ export function visitBlock(node, context) {
68
81
  let code = "{\n";
69
82
  this.indentationLevel++;
70
83
  const block = node;
71
- const varDecls = block.statements
72
- .filter(ts.isVariableStatement)
73
- .flatMap((stmt) => stmt.declarationList.declarations);
84
+ // Collect ONLY block-scoped declarations (let/const)
85
+ const blockScopedDecls = collectBlockScopedDeclarations(block.statements);
74
86
  const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
75
- const hoistedSymbols = new Set();
87
+ const classDecls = block.statements.filter(ts.isClassDeclaration);
88
+ const hoistedSymbols = new DeclaredSymbols();
76
89
  // 1. Hoist all function declarations
77
90
  funcDecls.forEach((func) => {
78
- const funcName = func.name?.getText();
79
- if (funcName && !hoistedSymbols.has(funcName)) {
80
- hoistedSymbols.add(funcName);
81
- code +=
82
- `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
83
- }
91
+ code += this.hoistDeclaration(func, hoistedSymbols);
84
92
  });
85
- // Hoist variable declarations
86
- varDecls.forEach((decl) => {
87
- const name = decl.name.getText();
88
- if (hoistedSymbols.has(name)) {
89
- return;
90
- }
91
- hoistedSymbols.add(name);
92
- const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
93
- 0;
94
- const initializer = isLetOrConst
95
- ? "jspp::AnyValue::make_uninitialized()"
96
- : "jspp::AnyValue::make_undefined()";
97
- code +=
98
- `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
93
+ // Hoist class declarations
94
+ classDecls.forEach((cls) => {
95
+ code += this.hoistDeclaration(cls, hoistedSymbols);
99
96
  });
97
+ // Hoist variable declarations (let/const only)
98
+ blockScopedDecls.forEach((decl) => {
99
+ code += this.hoistDeclaration(decl, hoistedSymbols);
100
+ });
101
+ // Compile symbols for other statements (excluding function)
102
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
103
+ const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
100
104
  // 2. Assign all hoisted functions first
105
+ const contextForFunctions = {
106
+ ...context,
107
+ localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
108
+ };
101
109
  funcDecls.forEach((stmt) => {
102
110
  const funcName = stmt.name?.getText();
103
111
  if (funcName) {
104
- const lambda = this.generateLambda(stmt, true);
112
+ this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
113
+ this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
114
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
115
+ isAssignment: true,
116
+ });
105
117
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
106
118
  }
107
119
  });
@@ -116,6 +128,8 @@ export function visitBlock(node, context) {
116
128
  0;
117
129
  const contextForVisit = {
118
130
  ...context,
131
+ topLevelScopeSymbols,
132
+ localScopeSymbols,
119
133
  isAssignmentOnly: !isLetOrConst,
120
134
  };
121
135
  const assignments = this.visit(stmt.declarationList, contextForVisit);
@@ -124,13 +138,18 @@ export function visitBlock(node, context) {
124
138
  }
125
139
  }
126
140
  else {
127
- code += this.visit(stmt, context);
141
+ code += this.visit(stmt, {
142
+ ...context,
143
+ isFunctionBody: false,
144
+ topLevelScopeSymbols,
145
+ localScopeSymbols,
146
+ });
128
147
  }
129
148
  });
130
149
  if (context.isFunctionBody) {
131
150
  const lastStatement = block.statements[block.statements.length - 1];
132
151
  if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
133
- code += `${this.indent()}${this.getReturnCmd(context)} jspp::AnyValue::make_undefined();\n`;
152
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
134
153
  }
135
154
  }
136
155
  this.indentationLevel--;
@@ -142,156 +161,47 @@ export function visitVariableStatement(node, context) {
142
161
  this.visit(node.declarationList, context) +
143
162
  ";\n");
144
163
  }
145
- export function visitForStatement(node, context) {
146
- const forStmt = node;
147
- let code = "";
148
- this.indentationLevel++; // Enter a new scope for the for loop
149
- // Handle initializer
150
- let initializerCode = "";
151
- if (forStmt.initializer) {
152
- if (ts.isVariableDeclarationList(forStmt.initializer)) {
153
- const varDeclList = forStmt.initializer;
154
- const isLetOrConst = (varDeclList.flags &
155
- (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
156
- 0;
157
- if (isLetOrConst) {
158
- // For `let` or `const` in for loop, they are block-scoped to the loop.
159
- // Declare the variable within the loop's scope.
160
- // The C++ for loop initializer can contain a declaration.
161
- const decl = varDeclList.declarations[0]; // Assuming single declaration for simplicity
162
- if (decl) {
163
- const name = decl.name.getText();
164
- const initValue = decl.initializer
165
- ? this.visit(decl.initializer, context)
166
- : "jspp::AnyValue::make_undefined()";
167
- initializerCode =
168
- `auto ${name} = std::make_unique<jspp::AnyValue>(${initValue})`;
169
- }
170
- }
171
- else {
172
- // For 'var', it's already hoisted, so this is an assignment.
173
- initializerCode = this.visit(forStmt.initializer, {
174
- ...context,
175
- isAssignmentOnly: true,
176
- });
177
- }
178
- }
179
- else {
180
- // If it's an expression (e.g., `i = 0`)
181
- initializerCode = this.visit(forStmt.initializer, context);
182
- }
183
- }
184
- code += `${this.indent()}for (${initializerCode}; `;
185
- if (forStmt.condition) {
186
- code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
164
+ export function visitBreakStatement(node, context) {
165
+ if (node.label) {
166
+ return `${this.indent()}goto ${node.label.text}_break;\n`;
187
167
  }
188
- code += "; ";
189
- if (forStmt.incrementor) {
190
- code += this.visit(forStmt.incrementor, context);
168
+ if (context.switchBreakLabel) {
169
+ return `${this.indent()}goto ${context.switchBreakLabel};\n`;
191
170
  }
192
- code += ") ";
193
- code += this.visit(forStmt.statement, {
194
- ...context,
195
- isFunctionBody: false,
196
- });
197
- this.indentationLevel--; // Exit the scope for the for loop
198
- return code;
171
+ return `${this.indent()}break;\n`;
199
172
  }
200
- export function visitForInStatement(node, context) {
201
- const forIn = node;
202
- let code = `${this.indent()}{\n`;
203
- this.indentationLevel++; // Enter a new scope for the for-in loop
204
- let varName = "";
205
- if (ts.isVariableDeclarationList(forIn.initializer)) {
206
- const decl = forIn.initializer.declarations[0];
207
- if (decl) {
208
- varName = decl.name.getText();
209
- // Declare the shared_ptr before the loop
210
- code +=
211
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
212
- }
213
- }
214
- else if (ts.isIdentifier(forIn.initializer)) {
215
- varName = forIn.initializer.getText();
216
- // Assume it's already declared in an outer scope, just assign to it.
217
- // No explicit declaration here.
218
- }
219
- const expr = forIn.expression;
220
- const exprText = this.visit(expr, context);
221
- let derefExpr = exprText;
222
- if (ts.isIdentifier(expr)) {
223
- derefExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
173
+ export function visitContinueStatement(node, context) {
174
+ if (node.label) {
175
+ return `${this.indent()}goto ${node.label.text}_continue;\n`;
224
176
  }
225
- const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
226
- code +=
227
- `${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
228
- code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
229
- this.indentationLevel++;
230
- code +=
231
- `${this.indent()}*${varName} = jspp::AnyValue::make_string(${varName}_str);\n`;
232
- code += this.visit(forIn.statement, {
233
- ...context,
234
- isFunctionBody: false,
235
- });
236
- this.indentationLevel--;
237
- code += `${this.indent()}}}\n`;
238
- this.indentationLevel--; // Exit the scope for the for-in loop
239
- return code;
177
+ return `${this.indent()}continue;\n`;
240
178
  }
241
- export function visitForOfStatement(node, context) {
242
- const forOf = node;
243
- let code = "";
244
- this.indentationLevel++; // Enter a new scope for the for-of loop
245
- let varName = "";
246
- if (ts.isVariableDeclarationList(forOf.initializer)) {
247
- const decl = forOf.initializer.declarations[0];
248
- if (decl) {
249
- varName = decl.name.getText();
250
- // Declare the shared_ptr before the loop
251
- code +=
252
- `${this.indent()}{ auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
253
- }
179
+ export function visitLabeledStatement(node, context) {
180
+ const label = node.label.text;
181
+ const statement = node.statement;
182
+ const isLoop = ts.isForStatement(statement) ||
183
+ ts.isForInStatement(statement) ||
184
+ ts.isForOfStatement(statement) ||
185
+ ts.isWhileStatement(statement) ||
186
+ ts.isDoStatement(statement);
187
+ const statementContext = { ...context, currentLabel: label };
188
+ if (ts.isSwitchStatement(statement)) {
189
+ return this.visit(statement, statementContext);
254
190
  }
255
- else if (ts.isIdentifier(forOf.initializer)) {
256
- varName = forOf.initializer.getText();
257
- // Assume it's already declared in an outer scope, just assign to it.
258
- // No explicit declaration here.
259
- code += `${this.indent()}{\n`;
191
+ const statementCode = this.visit(statement, statementContext);
192
+ if (isLoop) {
193
+ return statementCode;
260
194
  }
261
- const iterableExpr = this.visit(forOf.expression, context);
262
- const derefIterable = ts.isIdentifier(forOf.expression)
263
- ? `jspp::Access::deref(${iterableExpr}, ${this.getJsVarName(forOf.expression)})`
264
- : iterableExpr;
265
- const iteratorFn = this.generateUniqueName("__iter_fn_", new Set());
266
- const iteratorPtr = this.generateUniqueName("__iter_ptr_", new Set());
267
- const nextRes = this.generateUniqueName("__res__", new Set());
268
- code +=
269
- `${this.indent()}auto ${iteratorFn} = ${derefIterable}.get_own_property(Symbol.get_own_property("iterator"));\n`;
270
- code +=
271
- `${this.indent()}if (!${iteratorFn}.is_generator()) { throw jspp::RuntimeError::make_error("${iterableExpr} is not iterable", "TypeError"); }\n`;
272
- code +=
273
- `${this.indent()}auto ${iteratorPtr} = ${iteratorFn}.as_function()->call({}).as_iterator();\n`;
274
- code += `${this.indent()}auto ${nextRes} = ${iteratorPtr}->next();\n`;
275
- code += `${this.indent()}while (!${nextRes}.done) {\n`;
195
+ // A non-loop statement can only be broken from.
196
+ // We wrap it in a labeled block.
197
+ let code = `${this.indent()}${label}: {\n`;
276
198
  this.indentationLevel++;
277
- code +=
278
- `${this.indent()}*${varName} = ${nextRes}.value.value_or(jspp::AnyValue::make_undefined());\n`;
279
- code += this.visit(forOf.statement, {
280
- ...context,
281
- isFunctionBody: false,
282
- });
283
- code += `${this.indent()}${nextRes} = ${iteratorPtr}->next();\n`;
199
+ code += statementCode;
284
200
  this.indentationLevel--;
285
- code += `${this.indent()}}}\n`;
286
- this.indentationLevel--; // Exit the scope for the for-of loop
201
+ code += `${this.indent()}}\n`;
202
+ code += `${this.indent()}${label}_break:; // break target for ${label}\n`;
287
203
  return code;
288
204
  }
289
- export function visitBreakStatement(node, context) {
290
- return "break;";
291
- }
292
- export function visitContinueStatement(node, context) {
293
- return "continue;";
294
- }
295
205
  export function visitIfStatement(node, context) {
296
206
  const ifStmt = node;
297
207
  const condition = this.visit(ifStmt.expression, context);
@@ -307,7 +217,7 @@ export function visitIfStatement(node, context) {
307
217
  isFunctionBody: false,
308
218
  });
309
219
  }
310
- return `${this.indent()}if ((${condition}).is_truthy()) ${thenStmt}${elseStmt}`;
220
+ return `${this.indent()}if (is_truthy(${condition})) ${thenStmt}${elseStmt}`;
311
221
  }
312
222
  export function visitExpressionStatement(node, context) {
313
223
  return (this.indent() +
@@ -317,7 +227,7 @@ export function visitExpressionStatement(node, context) {
317
227
  export function visitThrowStatement(node, context) {
318
228
  const throwStmt = node;
319
229
  const expr = this.visit(throwStmt.expression, context);
320
- return `${this.indent()}throw jspp::RuntimeError(${expr});
230
+ return `${this.indent()}throw jspp::Exception(${expr});
321
231
  `;
322
232
  }
323
233
  export function visitTryStatement(node, context) {
@@ -334,18 +244,23 @@ export function visitTryStatement(node, context) {
334
244
  const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
335
245
  let code = `${this.indent()}{\n`;
336
246
  this.indentationLevel++;
337
- code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
338
- code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
247
+ code += `${this.indent()}jspp::AnyValue ${resultVarName};
248
+ `;
249
+ code += `${this.indent()}bool ${hasReturnedFlagName} = false;
250
+ `;
339
251
  const finallyBlockCode = this.visit(tryStmt.finallyBlock, {
340
252
  ...context,
341
253
  isFunctionBody: false,
342
254
  });
343
255
  code +=
344
- `${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()};\n`;
345
- code += `${this.indent()}try {\n`;
256
+ `${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()};
257
+ `;
258
+ code += `${this.indent()}try {
259
+ `;
346
260
  this.indentationLevel++;
347
261
  code +=
348
- `${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {\n`;
262
+ `${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {
263
+ `;
349
264
  this.indentationLevel++;
350
265
  const innerContext = {
351
266
  ...context,
@@ -353,7 +268,8 @@ export function visitTryStatement(node, context) {
353
268
  isInsideTryCatchLambda: true,
354
269
  hasReturnedFlag: hasReturnedFlagName,
355
270
  };
356
- code += `${this.indent()}try {\n`;
271
+ code += `${this.indent()}try {
272
+ `;
357
273
  this.indentationLevel++;
358
274
  code += this.visit(tryStmt.tryBlock, innerContext);
359
275
  this.indentationLevel--;
@@ -362,7 +278,8 @@ export function visitTryStatement(node, context) {
362
278
  const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText());
363
279
  const catchContext = { ...innerContext, exceptionName };
364
280
  code +=
365
- `${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
281
+ `${this.indent()}catch (const std::exception& ${exceptionName}) {
282
+ `;
366
283
  this.indentationLevel++;
367
284
  code += this.visit(tryStmt.catchClause.block, catchContext);
368
285
  this.indentationLevel--;
@@ -371,18 +288,20 @@ export function visitTryStatement(node, context) {
371
288
  else {
372
289
  code += `${this.indent()}catch (...) { throw; }\n`;
373
290
  }
374
- code += `${this.indent()}${this.getReturnCmd(context)} jspp::AnyValue::make_undefined();\n`;
291
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
375
292
  this.indentationLevel--;
376
293
  code += `${this.indent()}})();\n`;
377
294
  this.indentationLevel--;
378
- code += `${this.indent()}} catch (...) {\n`;
295
+ code += `${this.indent()}} catch (...) {
296
+ `;
379
297
  this.indentationLevel++;
380
298
  code += `${this.indent()}${finallyLambdaName}();\n`;
381
299
  code += `${this.indent()}throw;\n`;
382
300
  this.indentationLevel--;
383
301
  code += `${this.indent()}}\n`;
384
302
  code += `${this.indent()}${finallyLambdaName}();\n`;
385
- code += `${this.indent()}if (${hasReturnedFlagName}) {\n`;
303
+ code += `${this.indent()}if (${hasReturnedFlagName}) {
304
+ `;
386
305
  this.indentationLevel++;
387
306
  code += `${this.indent()}return ${resultVarName};\n`;
388
307
  this.indentationLevel--;
@@ -420,13 +339,13 @@ export function visitCatchClause(node, context) {
420
339
  this.indentationLevel++;
421
340
  code += `${this.indent()}{\n`;
422
341
  this.indentationLevel++;
423
- // Always create the JS exception variable.
342
+ // The JS exception variable is always local to the catch block
424
343
  code +=
425
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::RuntimeError::error_to_value(${exceptionName}));\n`;
344
+ `${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
426
345
  // Shadow the C++ exception variable *only if* the names don't clash.
427
346
  if (varName !== exceptionName) {
428
347
  code +=
429
- `${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
348
+ `${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
430
349
  }
431
350
  code += this.visit(catchClause.block, context);
432
351
  this.indentationLevel--;
@@ -446,77 +365,101 @@ export function visitCatchClause(node, context) {
446
365
  export function visitYieldExpression(node, context) {
447
366
  if (node.expression) {
448
367
  const expr = node.expression;
449
- const exprText = this.visit(expr, context);
368
+ let exprText = this.visit(expr, context);
450
369
  if (ts.isIdentifier(expr)) {
451
370
  const scope = this.getScopeForNode(expr);
452
371
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
453
372
  if (!typeInfo) {
454
- return `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})\n`; // THROWS, not returns
373
+ return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
455
374
  }
456
375
  if (typeInfo &&
457
376
  !typeInfo.isParameter &&
458
377
  !typeInfo.isBuiltin) {
459
- return `${this.indent()}co_yield jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
378
+ exprText = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
460
379
  }
461
380
  }
381
+ // Handle `yield*` expression
382
+ if (!!node.asteriskToken) {
383
+ let code = `${this.indent()}{\n`;
384
+ this.indentationLevel++;
385
+ const declaredSymbols = this.getDeclaredSymbols(expr);
386
+ const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
387
+ const iterator = this.generateUniqueName("__iter", declaredSymbols);
388
+ const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
389
+ const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
390
+ const varName = this.getJsVarName(expr);
391
+ code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
392
+ code +=
393
+ `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
394
+ code +=
395
+ `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
396
+ code +=
397
+ `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
398
+ code +=
399
+ `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
400
+ this.indentationLevel++;
401
+ code +=
402
+ `${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
403
+ code +=
404
+ `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
405
+ this.indentationLevel--;
406
+ code += `${this.indent()}}\n`;
407
+ return code;
408
+ }
462
409
  return `${this.indent()}co_yield ${exprText}`;
463
410
  }
464
- return `${this.indent()}co_yield jspp::AnyValue::make_undefined()`;
411
+ return `${this.indent()}co_yield jspp::Constants::UNDEFINED`;
465
412
  }
466
413
  export function visitReturnStatement(node, context) {
467
414
  if (context.isMainContext) {
468
- return `${this.indent()}jspp::RuntimeError::throw_invalid_return_statement_error();\n`;
415
+ return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
469
416
  }
470
417
  const returnStmt = node;
471
- const returnCmd = this.getReturnCmd(context);
418
+ const returnCmd = this.getReturnCommand(context);
472
419
  if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
473
420
  let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
474
421
  if (returnStmt.expression) {
475
422
  const expr = returnStmt.expression;
476
423
  const exprText = this.visit(expr, context);
424
+ let finalExpr = exprText;
477
425
  if (ts.isIdentifier(expr)) {
478
426
  const scope = this.getScopeForNode(expr);
479
427
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
480
428
  if (!typeInfo) {
481
429
  returnCode +=
482
- `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
430
+ `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
483
431
  }
484
- if (typeInfo &&
432
+ else if (typeInfo &&
485
433
  !typeInfo.isParameter &&
486
434
  !typeInfo.isBuiltin) {
487
- returnCode +=
488
- `${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
435
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
489
436
  }
490
- else {
491
- returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
492
- }
493
- }
494
- else {
495
- returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
496
437
  }
438
+ returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
497
439
  }
498
440
  else {
499
441
  returnCode +=
500
- `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
442
+ `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
501
443
  }
502
444
  return returnCode;
503
445
  }
504
446
  if (returnStmt.expression) {
505
447
  const expr = returnStmt.expression;
506
448
  const exprText = this.visit(expr, context);
449
+ let finalExpr = exprText;
507
450
  if (ts.isIdentifier(expr)) {
508
451
  const scope = this.getScopeForNode(expr);
509
452
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
510
453
  if (!typeInfo) {
511
- return `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
454
+ return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
512
455
  }
513
456
  if (typeInfo &&
514
457
  !typeInfo.isParameter &&
515
458
  !typeInfo.isBuiltin) {
516
- return `${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
459
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
517
460
  }
518
461
  }
519
- return `${this.indent()}${returnCmd} ${exprText};\n`;
462
+ return `${this.indent()}${returnCmd} ${finalExpr};\n`;
520
463
  }
521
- return `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
464
+ return `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
522
465
  }