@ugo-studio/jspp 0.1.3 → 0.1.4

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 (69) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/scope.js +16 -4
  3. package/dist/analysis/typeAnalyzer.js +253 -20
  4. package/dist/ast/types.js +6 -0
  5. package/dist/cli.js +1 -2
  6. package/dist/core/codegen/class-handlers.js +127 -0
  7. package/dist/core/codegen/control-flow-handlers.js +464 -0
  8. package/dist/core/codegen/declaration-handlers.js +31 -14
  9. package/dist/core/codegen/expression-handlers.js +429 -117
  10. package/dist/core/codegen/function-handlers.js +91 -15
  11. package/dist/core/codegen/helpers.js +66 -2
  12. package/dist/core/codegen/index.js +17 -6
  13. package/dist/core/codegen/literal-handlers.js +3 -0
  14. package/dist/core/codegen/statement-handlers.js +133 -204
  15. package/dist/core/codegen/visitor.js +29 -3
  16. package/package.json +3 -3
  17. package/src/prelude/any_value.hpp +658 -634
  18. package/src/prelude/any_value_access.hpp +103 -0
  19. package/src/prelude/any_value_defines.hpp +151 -0
  20. package/src/prelude/any_value_helpers.hpp +246 -225
  21. package/src/prelude/exception.hpp +31 -0
  22. package/src/prelude/exception_helpers.hpp +49 -0
  23. package/src/prelude/index.hpp +18 -9
  24. package/src/prelude/library/console.hpp +13 -13
  25. package/src/prelude/library/error.hpp +111 -0
  26. package/src/prelude/library/global.hpp +15 -4
  27. package/src/prelude/library/performance.hpp +2 -2
  28. package/src/prelude/library/promise.hpp +121 -0
  29. package/src/prelude/library/symbol.hpp +3 -4
  30. package/src/prelude/library/timer.hpp +92 -0
  31. package/src/prelude/scheduler.hpp +145 -0
  32. package/src/prelude/types.hpp +10 -1
  33. package/src/prelude/utils/access.hpp +174 -0
  34. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  35. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  36. package/src/prelude/utils/log_any_value/function.hpp +37 -0
  37. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  38. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  39. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  40. package/src/prelude/utils/log_any_value/object.hpp +119 -0
  41. package/src/prelude/utils/log_any_value/primitives.hpp +41 -0
  42. package/src/prelude/{operators.hpp → utils/operators.hpp} +29 -10
  43. package/src/prelude/{well_known_symbols.hpp → utils/well_known_symbols.hpp} +0 -1
  44. package/src/prelude/values/array.hpp +3 -2
  45. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  46. package/src/prelude/values/function.hpp +76 -51
  47. package/src/prelude/values/helpers/array.hpp +20 -11
  48. package/src/prelude/values/helpers/function.hpp +125 -77
  49. package/src/prelude/values/helpers/iterator.hpp +13 -7
  50. package/src/prelude/values/helpers/object.hpp +36 -6
  51. package/src/prelude/values/helpers/promise.hpp +181 -0
  52. package/src/prelude/values/helpers/string.hpp +3 -3
  53. package/src/prelude/values/helpers/symbol.hpp +2 -2
  54. package/src/prelude/values/iterator.hpp +13 -5
  55. package/src/prelude/values/object.hpp +6 -2
  56. package/src/prelude/values/promise.hpp +73 -0
  57. package/src/prelude/values/prototypes/array.hpp +16 -16
  58. package/src/prelude/values/prototypes/function.hpp +4 -4
  59. package/src/prelude/values/prototypes/iterator.hpp +11 -10
  60. package/src/prelude/values/prototypes/object.hpp +26 -0
  61. package/src/prelude/values/prototypes/promise.hpp +124 -0
  62. package/src/prelude/values/prototypes/string.hpp +26 -26
  63. package/src/prelude/values/prototypes/symbol.hpp +5 -3
  64. package/src/prelude/values/string.hpp +1 -1
  65. package/src/prelude/values/symbol.hpp +1 -1
  66. package/src/prelude/access.hpp +0 -91
  67. package/src/prelude/error.hpp +0 -31
  68. package/src/prelude/error_helpers.hpp +0 -59
  69. package/src/prelude/log_string.hpp +0 -407
@@ -7,41 +7,39 @@ export function visitSourceFile(node, context) {
7
7
  .filter(ts.isVariableStatement)
8
8
  .flatMap((stmt) => stmt.declarationList.declarations);
9
9
  const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
10
- const hoistedSymbols = new Set();
10
+ const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
11
+ const hoistedSymbols = new Map();
11
12
  // 1. Hoist function declarations
12
13
  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
- }
14
+ code += this.hoistDeclaration(func, hoistedSymbols);
15
+ });
16
+ // Hoist class declarations
17
+ classDecls.forEach((cls) => {
18
+ code += this.hoistDeclaration(cls, hoistedSymbols);
19
19
  });
20
20
  // Hoist variable declarations
21
21
  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`;
22
+ code += this.hoistDeclaration(decl, hoistedSymbols);
34
23
  });
35
24
  // 2. Assign all hoisted functions first
25
+ const contextForFunctions = {
26
+ ...context,
27
+ currentScopeSymbols: new Map(context.currentScopeSymbols),
28
+ };
29
+ hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
36
30
  funcDecls.forEach((stmt) => {
37
31
  const funcName = stmt.name?.getText();
38
32
  if (funcName) {
39
- const lambda = this.generateLambda(stmt, true);
33
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
34
+ isAssignment: true,
35
+ });
40
36
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
41
37
  }
42
38
  });
43
39
  // 3. Process other statements
44
40
  sourceFile.statements.forEach((stmt) => {
41
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
42
+ const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
45
43
  if (ts.isFunctionDeclaration(stmt)) {
46
44
  // Already handled
47
45
  }
@@ -51,6 +49,8 @@ export function visitSourceFile(node, context) {
51
49
  0;
52
50
  const contextForVisit = {
53
51
  ...context,
52
+ topLevelScopeSymbols,
53
+ currentScopeSymbols,
54
54
  isAssignmentOnly: !isLetOrConst,
55
55
  };
56
56
  const assignments = this.visit(stmt.declarationList, contextForVisit);
@@ -59,7 +59,14 @@ export function visitSourceFile(node, context) {
59
59
  }
60
60
  }
61
61
  else {
62
- code += this.visit(stmt, context);
62
+ code += this.visit(stmt, {
63
+ ...context,
64
+ isFunctionBody: false,
65
+ topLevelScopeSymbols,
66
+ currentScopeSymbols,
67
+ // currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
68
+ // topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
69
+ });
63
70
  }
64
71
  });
65
72
  return code;
@@ -72,41 +79,39 @@ export function visitBlock(node, context) {
72
79
  .filter(ts.isVariableStatement)
73
80
  .flatMap((stmt) => stmt.declarationList.declarations);
74
81
  const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
75
- const hoistedSymbols = new Set();
82
+ const classDecls = block.statements.filter(ts.isClassDeclaration);
83
+ const hoistedSymbols = new Map();
76
84
  // 1. Hoist all function declarations
77
85
  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
- }
86
+ code += this.hoistDeclaration(func, hoistedSymbols);
87
+ });
88
+ // Hoist class declarations
89
+ classDecls.forEach((cls) => {
90
+ code += this.hoistDeclaration(cls, hoistedSymbols);
84
91
  });
85
92
  // Hoist variable declarations
86
93
  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`;
94
+ code += this.hoistDeclaration(decl, hoistedSymbols);
99
95
  });
100
96
  // 2. Assign all hoisted functions first
97
+ const contextForFunctions = {
98
+ ...context,
99
+ currentScopeSymbols: new Map(context.currentScopeSymbols),
100
+ };
101
+ hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
101
102
  funcDecls.forEach((stmt) => {
102
103
  const funcName = stmt.name?.getText();
103
104
  if (funcName) {
104
- const lambda = this.generateLambda(stmt, true);
105
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
106
+ isAssignment: true,
107
+ });
105
108
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
106
109
  }
107
110
  });
108
111
  // 3. Process other statements
109
112
  block.statements.forEach((stmt) => {
113
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
114
+ const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
110
115
  if (ts.isFunctionDeclaration(stmt)) {
111
116
  // Do nothing, already handled
112
117
  }
@@ -116,6 +121,8 @@ export function visitBlock(node, context) {
116
121
  0;
117
122
  const contextForVisit = {
118
123
  ...context,
124
+ topLevelScopeSymbols,
125
+ currentScopeSymbols,
119
126
  isAssignmentOnly: !isLetOrConst,
120
127
  };
121
128
  const assignments = this.visit(stmt.declarationList, contextForVisit);
@@ -124,13 +131,20 @@ export function visitBlock(node, context) {
124
131
  }
125
132
  }
126
133
  else {
127
- code += this.visit(stmt, context);
134
+ code += this.visit(stmt, {
135
+ ...context,
136
+ isFunctionBody: false,
137
+ topLevelScopeSymbols,
138
+ currentScopeSymbols,
139
+ // currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
140
+ // topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
141
+ });
128
142
  }
129
143
  });
130
144
  if (context.isFunctionBody) {
131
145
  const lastStatement = block.statements[block.statements.length - 1];
132
146
  if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
133
- code += `${this.indent()}${this.getReturnCmd(context)} jspp::AnyValue::make_undefined();\n`;
147
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
134
148
  }
135
149
  }
136
150
  this.indentationLevel--;
@@ -142,156 +156,47 @@ export function visitVariableStatement(node, context) {
142
156
  this.visit(node.declarationList, context) +
143
157
  ";\n");
144
158
  }
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()`;
159
+ export function visitBreakStatement(node, context) {
160
+ if (node.label) {
161
+ return `${this.indent()}goto ${node.label.text}_break;\n`;
187
162
  }
188
- code += "; ";
189
- if (forStmt.incrementor) {
190
- code += this.visit(forStmt.incrementor, context);
163
+ if (context.switchBreakLabel) {
164
+ return `${this.indent()}goto ${context.switchBreakLabel};\n`;
191
165
  }
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;
166
+ return `${this.indent()}break;\n`;
199
167
  }
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)})`;
168
+ export function visitContinueStatement(node, context) {
169
+ if (node.label) {
170
+ return `${this.indent()}goto ${node.label.text}_continue;\n`;
224
171
  }
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;
172
+ return `${this.indent()}continue;\n`;
240
173
  }
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
- }
174
+ export function visitLabeledStatement(node, context) {
175
+ const label = node.label.text;
176
+ const statement = node.statement;
177
+ const isLoop = ts.isForStatement(statement) ||
178
+ ts.isForInStatement(statement) ||
179
+ ts.isForOfStatement(statement) ||
180
+ ts.isWhileStatement(statement) ||
181
+ ts.isDoStatement(statement);
182
+ const statementContext = { ...context, currentLabel: label };
183
+ if (ts.isSwitchStatement(statement)) {
184
+ return this.visit(statement, statementContext);
254
185
  }
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`;
186
+ const statementCode = this.visit(statement, statementContext);
187
+ if (isLoop) {
188
+ return statementCode;
260
189
  }
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`;
190
+ // A non-loop statement can only be broken from.
191
+ // We wrap it in a labeled block.
192
+ let code = `${this.indent()}${label}: {\n`;
276
193
  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`;
194
+ code += statementCode;
284
195
  this.indentationLevel--;
285
- code += `${this.indent()}}}\n`;
286
- this.indentationLevel--; // Exit the scope for the for-of loop
196
+ code += `${this.indent()}}\n`;
197
+ code += `${this.indent()}${label}_break:; // break target for ${label}\n`;
287
198
  return code;
288
199
  }
289
- export function visitBreakStatement(node, context) {
290
- return "break;";
291
- }
292
- export function visitContinueStatement(node, context) {
293
- return "continue;";
294
- }
295
200
  export function visitIfStatement(node, context) {
296
201
  const ifStmt = node;
297
202
  const condition = this.visit(ifStmt.expression, context);
@@ -317,7 +222,7 @@ export function visitExpressionStatement(node, context) {
317
222
  export function visitThrowStatement(node, context) {
318
223
  const throwStmt = node;
319
224
  const expr = this.visit(throwStmt.expression, context);
320
- return `${this.indent()}throw jspp::RuntimeError(${expr});
225
+ return `${this.indent()}throw jspp::Exception(${expr});
321
226
  `;
322
227
  }
323
228
  export function visitTryStatement(node, context) {
@@ -371,7 +276,7 @@ export function visitTryStatement(node, context) {
371
276
  else {
372
277
  code += `${this.indent()}catch (...) { throw; }\n`;
373
278
  }
374
- code += `${this.indent()}${this.getReturnCmd(context)} jspp::AnyValue::make_undefined();\n`;
279
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
375
280
  this.indentationLevel--;
376
281
  code += `${this.indent()}})();\n`;
377
282
  this.indentationLevel--;
@@ -420,9 +325,9 @@ export function visitCatchClause(node, context) {
420
325
  this.indentationLevel++;
421
326
  code += `${this.indent()}{\n`;
422
327
  this.indentationLevel++;
423
- // Always create the JS exception variable.
328
+ // The JS exception variable is always local to the catch block
424
329
  code +=
425
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::RuntimeError::error_to_value(${exceptionName}));\n`;
330
+ `${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
426
331
  // Shadow the C++ exception variable *only if* the names don't clash.
427
332
  if (varName !== exceptionName) {
428
333
  code +=
@@ -446,54 +351,77 @@ export function visitCatchClause(node, context) {
446
351
  export function visitYieldExpression(node, context) {
447
352
  if (node.expression) {
448
353
  const expr = node.expression;
449
- const exprText = this.visit(expr, context);
354
+ let exprText = this.visit(expr, context);
450
355
  if (ts.isIdentifier(expr)) {
451
356
  const scope = this.getScopeForNode(expr);
452
357
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
453
358
  if (!typeInfo) {
454
- return `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})\n`; // THROWS, not returns
359
+ return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
455
360
  }
456
361
  if (typeInfo &&
457
362
  !typeInfo.isParameter &&
458
363
  !typeInfo.isBuiltin) {
459
- return `${this.indent()}co_yield jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
364
+ exprText = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
460
365
  }
461
366
  }
367
+ // Handle `yield*` expression
368
+ if (!!node.asteriskToken) {
369
+ let code = `${this.indent()}{\n`;
370
+ this.indentationLevel++;
371
+ const declaredSymbols = this.getDeclaredSymbols(expr);
372
+ const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
373
+ const iterator = this.generateUniqueName("__iter", declaredSymbols);
374
+ const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
375
+ const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
376
+ const varName = this.getJsVarName(expr);
377
+ code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
378
+ code +=
379
+ `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
380
+ code +=
381
+ `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next").as_function();\n`;
382
+ code +=
383
+ `${this.indent()}auto ${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
384
+ code +=
385
+ `${this.indent()}while (!${nextRes}.get_own_property("done").is_truthy()) {\n`;
386
+ this.indentationLevel++;
387
+ code +=
388
+ `${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
389
+ code +=
390
+ `${this.indent()}${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
391
+ this.indentationLevel--;
392
+ code += `${this.indent()}}}\n`;
393
+ return code;
394
+ }
462
395
  return `${this.indent()}co_yield ${exprText}`;
463
396
  }
464
397
  return `${this.indent()}co_yield jspp::AnyValue::make_undefined()`;
465
398
  }
466
399
  export function visitReturnStatement(node, context) {
467
400
  if (context.isMainContext) {
468
- return `${this.indent()}jspp::RuntimeError::throw_invalid_return_statement_error();\n`;
401
+ return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
469
402
  }
470
403
  const returnStmt = node;
471
- const returnCmd = this.getReturnCmd(context);
404
+ const returnCmd = this.getReturnCommand(context);
472
405
  if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
473
406
  let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
474
407
  if (returnStmt.expression) {
475
408
  const expr = returnStmt.expression;
476
409
  const exprText = this.visit(expr, context);
410
+ let finalExpr = exprText;
477
411
  if (ts.isIdentifier(expr)) {
478
412
  const scope = this.getScopeForNode(expr);
479
413
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
480
414
  if (!typeInfo) {
481
415
  returnCode +=
482
- `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
416
+ `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
483
417
  }
484
- if (typeInfo &&
418
+ else if (typeInfo &&
485
419
  !typeInfo.isParameter &&
486
420
  !typeInfo.isBuiltin) {
487
- returnCode +=
488
- `${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
421
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
489
422
  }
490
- else {
491
- returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
492
- }
493
- }
494
- else {
495
- returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
496
423
  }
424
+ returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
497
425
  }
498
426
  else {
499
427
  returnCode +=
@@ -504,19 +432,20 @@ export function visitReturnStatement(node, context) {
504
432
  if (returnStmt.expression) {
505
433
  const expr = returnStmt.expression;
506
434
  const exprText = this.visit(expr, context);
435
+ let finalExpr = exprText;
507
436
  if (ts.isIdentifier(expr)) {
508
437
  const scope = this.getScopeForNode(expr);
509
438
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
510
439
  if (!typeInfo) {
511
- return `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
440
+ return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
512
441
  }
513
442
  if (typeInfo &&
514
443
  !typeInfo.isParameter &&
515
444
  !typeInfo.isBuiltin) {
516
- return `${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
445
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
517
446
  }
518
447
  }
519
- return `${this.indent()}${returnCmd} ${exprText};\n`;
448
+ return `${this.indent()}${returnCmd} ${finalExpr};\n`;
520
449
  }
521
450
  return `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
522
451
  }
@@ -1,10 +1,12 @@
1
1
  import ts from "typescript";
2
2
  import { CodeGenerator } from "./";
3
+ import { visitClassDeclaration } from "./class-handlers";
4
+ import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers";
3
5
  import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers";
4
- import { visitArrayLiteralExpression, visitBinaryExpression, visitCallExpression, visitElementAccessExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitVoidExpression, } from "./expression-handlers";
6
+ import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers";
5
7
  import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers";
6
- import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
7
- import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitIfStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers";
8
+ import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitThisKeyword, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
9
+ import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitIfStatement, visitLabeledStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers";
8
10
  export function visit(node, context) {
9
11
  if (ts.isFunctionDeclaration(node)) {
10
12
  return visitFunctionDeclaration.call(this, node, context);
@@ -14,6 +16,8 @@ export function visit(node, context) {
14
16
  return visitArrowFunction.call(this, node, context);
15
17
  case ts.SyntaxKind.FunctionExpression:
16
18
  return visitFunctionExpression.call(this, node, context);
19
+ case ts.SyntaxKind.ClassDeclaration:
20
+ return visitClassDeclaration.call(this, node, context);
17
21
  case ts.SyntaxKind.SourceFile:
18
22
  return visitSourceFile.call(this, node, context);
19
23
  case ts.SyntaxKind.Block:
@@ -34,10 +38,22 @@ export function visit(node, context) {
34
38
  return visitForInStatement.call(this, node, context);
35
39
  case ts.SyntaxKind.ForOfStatement:
36
40
  return visitForOfStatement.call(this, node, context);
41
+ case ts.SyntaxKind.WhileStatement:
42
+ return visitWhileStatement.call(this, node, context);
43
+ case ts.SyntaxKind.DoStatement:
44
+ return visitDoStatement.call(this, node, context);
45
+ case ts.SyntaxKind.SwitchStatement:
46
+ return visitSwitchStatement.call(this, node, context);
47
+ case ts.SyntaxKind.CaseClause:
48
+ return visitCaseClause.call(this, node, context);
49
+ case ts.SyntaxKind.DefaultClause:
50
+ return visitDefaultClause.call(this, node, context);
37
51
  case ts.SyntaxKind.BreakStatement:
38
52
  return visitBreakStatement.call(this, node, context);
39
53
  case ts.SyntaxKind.ContinueStatement:
40
54
  return visitContinueStatement.call(this, node, context);
55
+ case ts.SyntaxKind.LabeledStatement:
56
+ return visitLabeledStatement.call(this, node, context);
41
57
  case ts.SyntaxKind.IfStatement:
42
58
  return visitIfStatement.call(this, node, context);
43
59
  case ts.SyntaxKind.PrefixUnaryExpression:
@@ -54,6 +70,8 @@ export function visit(node, context) {
54
70
  return visitExpressionStatement.call(this, node, context);
55
71
  case ts.SyntaxKind.BinaryExpression:
56
72
  return visitBinaryExpression.call(this, node, context);
73
+ case ts.SyntaxKind.ConditionalExpression:
74
+ return visitConditionalExpression.call(this, node, context);
57
75
  case ts.SyntaxKind.ThrowStatement:
58
76
  return visitThrowStatement.call(this, node, context);
59
77
  case ts.SyntaxKind.TryStatement:
@@ -76,6 +94,12 @@ export function visit(node, context) {
76
94
  return visitNoSubstitutionTemplateLiteral.call(this, node);
77
95
  case ts.SyntaxKind.TemplateExpression:
78
96
  return visitTemplateExpression.call(this, node, context);
97
+ case ts.SyntaxKind.AwaitExpression:
98
+ return visitAwaitExpression.call(this, node, context);
99
+ case ts.SyntaxKind.NewExpression:
100
+ return visitNewExpression.call(this, node, context);
101
+ case ts.SyntaxKind.TypeOfExpression:
102
+ return visitTypeOfExpression.call(this, node, context);
79
103
  case ts.SyntaxKind.TrueKeyword:
80
104
  return visitTrueKeyword.call(this);
81
105
  case ts.SyntaxKind.FalseKeyword:
@@ -86,6 +110,8 @@ export function visit(node, context) {
86
110
  return visitUndefinedKeyword.call(this);
87
111
  case ts.SyntaxKind.NullKeyword:
88
112
  return visitNullKeyword.call(this);
113
+ case ts.SyntaxKind.ThisKeyword:
114
+ return visitThisKeyword.call(this);
89
115
  default:
90
116
  return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
91
117
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.1.3",
4
- "description": "A modern, experimental transpiler that converts JavaScript code into high-performance, standard C++23.",
3
+ "version": "0.1.4",
4
+ "description": "A modern transpiler that converts JavaScript code into high-performance, standard C++23.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.ts",
7
7
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "dev": "bun run src/cli.ts",
17
17
  "typecheck": "tsc --noEmit",
18
18
  "precompile": "bun run scripts/precompile-headers.ts",
19
- "test": "bun run precompile && bun test",
19
+ "test": "bun test",
20
20
  "build": "tsc",
21
21
  "prepack": "bun run build",
22
22
  "publish:npm": "npm publish --access=public"