@ugo-studio/jspp 0.1.2 → 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 (72) hide show
  1. package/README.md +5 -3
  2. package/dist/analysis/scope.js +38 -15
  3. package/dist/analysis/typeAnalyzer.js +257 -23
  4. package/dist/ast/types.js +6 -0
  5. package/dist/cli.js +3 -4
  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 +432 -116
  10. package/dist/core/codegen/function-handlers.js +110 -13
  11. package/dist/core/codegen/helpers.js +76 -8
  12. package/dist/core/codegen/index.js +18 -5
  13. package/dist/core/codegen/literal-handlers.js +3 -0
  14. package/dist/core/codegen/statement-handlers.js +152 -186
  15. package/dist/core/codegen/visitor.js +35 -3
  16. package/package.json +3 -3
  17. package/src/prelude/any_value.hpp +658 -734
  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 -0
  21. package/src/prelude/exception.hpp +31 -0
  22. package/src/prelude/exception_helpers.hpp +49 -0
  23. package/src/prelude/index.hpp +35 -12
  24. package/src/prelude/library/console.hpp +20 -20
  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 +25 -0
  28. package/src/prelude/library/promise.hpp +121 -0
  29. package/src/prelude/library/symbol.hpp +60 -4
  30. package/src/prelude/library/timer.hpp +92 -0
  31. package/src/prelude/scheduler.hpp +145 -0
  32. package/src/prelude/types.hpp +33 -6
  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} +31 -12
  43. package/src/prelude/utils/well_known_symbols.hpp +13 -0
  44. package/src/prelude/values/array.hpp +5 -2
  45. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  46. package/src/prelude/values/function.hpp +76 -19
  47. package/src/prelude/values/{operators → helpers}/array.hpp +30 -14
  48. package/src/prelude/values/helpers/function.hpp +125 -0
  49. package/src/prelude/values/helpers/iterator.hpp +107 -0
  50. package/src/prelude/values/helpers/object.hpp +64 -0
  51. package/src/prelude/values/helpers/promise.hpp +181 -0
  52. package/src/prelude/values/helpers/string.hpp +50 -0
  53. package/src/prelude/values/helpers/symbol.hpp +23 -0
  54. package/src/prelude/values/iterator.hpp +96 -0
  55. package/src/prelude/values/object.hpp +8 -3
  56. package/src/prelude/values/promise.hpp +73 -0
  57. package/src/prelude/values/prototypes/array.hpp +23 -16
  58. package/src/prelude/values/prototypes/function.hpp +26 -0
  59. package/src/prelude/values/prototypes/iterator.hpp +58 -0
  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 +366 -357
  63. package/src/prelude/values/prototypes/symbol.hpp +41 -0
  64. package/src/prelude/values/string.hpp +25 -0
  65. package/src/prelude/values/symbol.hpp +102 -0
  66. package/src/prelude/access.hpp +0 -86
  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 -403
  70. package/src/prelude/values/operators/function.hpp +0 -34
  71. package/src/prelude/values/operators/object.hpp +0 -34
  72. package/src/prelude/well_known_symbols.hpp +0 -10
@@ -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,14 +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 +=
134
- `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
147
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
135
148
  }
136
149
  }
137
150
  this.indentationLevel--;
@@ -143,137 +156,45 @@ export function visitVariableStatement(node, context) {
143
156
  this.visit(node.declarationList, context) +
144
157
  ";\n");
145
158
  }
146
- export function visitForStatement(node, context) {
147
- const forStmt = node;
148
- let code = "";
149
- this.indentationLevel++; // Enter a new scope for the for loop
150
- // Handle initializer
151
- let initializerCode = "";
152
- if (forStmt.initializer) {
153
- if (ts.isVariableDeclarationList(forStmt.initializer)) {
154
- const varDeclList = forStmt.initializer;
155
- const isLetOrConst = (varDeclList.flags &
156
- (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
157
- 0;
158
- if (isLetOrConst) {
159
- // For `let` or `const` in for loop, they are block-scoped to the loop.
160
- // Declare the variable within the loop's scope.
161
- // The C++ for loop initializer can contain a declaration.
162
- const decl = varDeclList.declarations[0]; // Assuming single declaration for simplicity
163
- if (decl) {
164
- const name = decl.name.getText();
165
- const initValue = decl.initializer
166
- ? this.visit(decl.initializer, context)
167
- : "jspp::AnyValue::make_undefined()";
168
- initializerCode =
169
- `auto ${name} = std::make_unique<jspp::AnyValue>(${initValue})`;
170
- }
171
- }
172
- else {
173
- // For 'var', it's already hoisted, so this is an assignment.
174
- initializerCode = this.visit(forStmt.initializer, {
175
- ...context,
176
- isAssignmentOnly: true,
177
- });
178
- }
179
- }
180
- else {
181
- // If it's an expression (e.g., `i = 0`)
182
- initializerCode = this.visit(forStmt.initializer, context);
183
- }
159
+ export function visitBreakStatement(node, context) {
160
+ if (node.label) {
161
+ return `${this.indent()}goto ${node.label.text}_break;\n`;
184
162
  }
185
- code += `${this.indent()}for (${initializerCode}; `;
186
- if (forStmt.condition) {
187
- code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
163
+ if (context.switchBreakLabel) {
164
+ return `${this.indent()}goto ${context.switchBreakLabel};\n`;
188
165
  }
189
- code += "; ";
190
- if (forStmt.incrementor) {
191
- code += this.visit(forStmt.incrementor, context);
192
- }
193
- code += ") ";
194
- code += this.visit(forStmt.statement, {
195
- ...context,
196
- isFunctionBody: false,
197
- });
198
- this.indentationLevel--; // Exit the scope for the for loop
199
- return code;
166
+ return `${this.indent()}break;\n`;
200
167
  }
201
- export function visitForInStatement(node, context) {
202
- const forIn = node;
203
- let code = `${this.indent()}{\n`;
204
- this.indentationLevel++; // Enter a new scope for the for-in loop
205
- let varName = "";
206
- if (ts.isVariableDeclarationList(forIn.initializer)) {
207
- const decl = forIn.initializer.declarations[0];
208
- if (decl) {
209
- varName = decl.name.getText();
210
- // Declare the shared_ptr before the loop
211
- code +=
212
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
213
- }
214
- }
215
- else if (ts.isIdentifier(forIn.initializer)) {
216
- varName = forIn.initializer.getText();
217
- // Assume it's already declared in an outer scope, just assign to it.
218
- // No explicit declaration here.
219
- }
220
- const expr = forIn.expression;
221
- const exprText = this.visit(expr, context);
222
- let derefExpr = exprText;
223
- if (ts.isIdentifier(expr)) {
224
- 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`;
225
171
  }
226
- const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
227
- code +=
228
- `${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
229
- code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
230
- this.indentationLevel++;
231
- code +=
232
- `${this.indent()}*${varName} = jspp::AnyValue::make_string(${varName}_str);\n`;
233
- code += this.visit(forIn.statement, {
234
- ...context,
235
- isFunctionBody: false,
236
- });
237
- this.indentationLevel--;
238
- code += `${this.indent()}}}\n`;
239
- this.indentationLevel--; // Exit the scope for the for-in loop
240
- return code;
172
+ return `${this.indent()}continue;\n`;
241
173
  }
242
- export function visitForOfStatement(node, context) {
243
- const forOf = node;
244
- let code = "";
245
- this.indentationLevel++; // Enter a new scope for the for-of loop
246
- let varName = "";
247
- if (ts.isVariableDeclarationList(forOf.initializer)) {
248
- const decl = forOf.initializer.declarations[0];
249
- if (decl) {
250
- varName = decl.name.getText();
251
- // Declare the shared_ptr before the loop
252
- code +=
253
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
254
- }
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);
255
185
  }
256
- else if (ts.isIdentifier(forOf.initializer)) {
257
- varName = forOf.initializer.getText();
258
- // Assume it's already declared in an outer scope, just assign to it.
259
- // No explicit declaration here.
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 = `jspp::Access::deref(${iterableExpr}, ${this.getJsVarName(forOf.expression)})`;
263
- const arrayPtr = this.generateUniqueName("__array_ptr_", new Set());
264
- code +=
265
- `${this.indent()}{ auto ${arrayPtr} = std::any_cast<std::shared_ptr<jspp::JsArray>>(${derefIterable});\n`;
266
- code +=
267
- `${this.indent()}for (const auto& ${varName}_val : ${arrayPtr}->items) {\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`;
268
193
  this.indentationLevel++;
269
- code += `${this.indent()}*${varName} = ${varName}_val;\n`;
270
- code += this.visit(forOf.statement, {
271
- ...context,
272
- isFunctionBody: false,
273
- });
194
+ code += statementCode;
274
195
  this.indentationLevel--;
275
- code += `${this.indent()}}}\n`;
276
- 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`;
277
198
  return code;
278
199
  }
279
200
  export function visitIfStatement(node, context) {
@@ -301,7 +222,7 @@ export function visitExpressionStatement(node, context) {
301
222
  export function visitThrowStatement(node, context) {
302
223
  const throwStmt = node;
303
224
  const expr = this.visit(throwStmt.expression, context);
304
- return `${this.indent()}throw jspp::RuntimeError(${expr});
225
+ return `${this.indent()}throw jspp::Exception(${expr});
305
226
  `;
306
227
  }
307
228
  export function visitTryStatement(node, context) {
@@ -355,7 +276,7 @@ export function visitTryStatement(node, context) {
355
276
  else {
356
277
  code += `${this.indent()}catch (...) { throw; }\n`;
357
278
  }
358
- code += `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
279
+ code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
359
280
  this.indentationLevel--;
360
281
  code += `${this.indent()}})();\n`;
361
282
  this.indentationLevel--;
@@ -404,9 +325,9 @@ export function visitCatchClause(node, context) {
404
325
  this.indentationLevel++;
405
326
  code += `${this.indent()}{\n`;
406
327
  this.indentationLevel++;
407
- // Always create the JS exception variable.
328
+ // The JS exception variable is always local to the catch block
408
329
  code +=
409
- `${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`;
410
331
  // Shadow the C++ exception variable *only if* the names don't clash.
411
332
  if (varName !== exceptionName) {
412
333
  code +=
@@ -427,59 +348,104 @@ export function visitCatchClause(node, context) {
427
348
  return code;
428
349
  }
429
350
  }
351
+ export function visitYieldExpression(node, context) {
352
+ if (node.expression) {
353
+ const expr = node.expression;
354
+ let exprText = this.visit(expr, context);
355
+ if (ts.isIdentifier(expr)) {
356
+ const scope = this.getScopeForNode(expr);
357
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
358
+ if (!typeInfo) {
359
+ return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
360
+ }
361
+ if (typeInfo &&
362
+ !typeInfo.isParameter &&
363
+ !typeInfo.isBuiltin) {
364
+ exprText = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
365
+ }
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
+ }
395
+ return `${this.indent()}co_yield ${exprText}`;
396
+ }
397
+ return `${this.indent()}co_yield jspp::AnyValue::make_undefined()`;
398
+ }
430
399
  export function visitReturnStatement(node, context) {
431
400
  if (context.isMainContext) {
432
- return `${this.indent()}jspp::RuntimeError::throw_invalid_return_statement_error();\n`;
401
+ return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
433
402
  }
434
403
  const returnStmt = node;
404
+ const returnCmd = this.getReturnCommand(context);
435
405
  if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
436
406
  let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
437
407
  if (returnStmt.expression) {
438
408
  const expr = returnStmt.expression;
439
409
  const exprText = this.visit(expr, context);
410
+ let finalExpr = exprText;
440
411
  if (ts.isIdentifier(expr)) {
441
412
  const scope = this.getScopeForNode(expr);
442
413
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
443
414
  if (!typeInfo) {
444
415
  returnCode +=
445
- `${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
446
417
  }
447
- if (typeInfo &&
418
+ else if (typeInfo &&
448
419
  !typeInfo.isParameter &&
449
420
  !typeInfo.isBuiltin) {
450
- returnCode +=
451
- `${this.indent()}return jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
421
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
452
422
  }
453
- else {
454
- returnCode += `${this.indent()}return ${exprText};\n`;
455
- }
456
- }
457
- else {
458
- returnCode += `${this.indent()}return ${exprText};\n`;
459
423
  }
424
+ returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
460
425
  }
461
426
  else {
462
427
  returnCode +=
463
- `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
428
+ `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
464
429
  }
465
430
  return returnCode;
466
431
  }
467
432
  if (returnStmt.expression) {
468
433
  const expr = returnStmt.expression;
469
434
  const exprText = this.visit(expr, context);
435
+ let finalExpr = exprText;
470
436
  if (ts.isIdentifier(expr)) {
471
437
  const scope = this.getScopeForNode(expr);
472
438
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
473
439
  if (!typeInfo) {
474
- 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
475
441
  }
476
442
  if (typeInfo &&
477
443
  !typeInfo.isParameter &&
478
444
  !typeInfo.isBuiltin) {
479
- return `${this.indent()}return jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
445
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
480
446
  }
481
447
  }
482
- return `${this.indent()}return ${exprText};\n`;
448
+ return `${this.indent()}${returnCmd} ${finalExpr};\n`;
483
449
  }
484
- return `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
450
+ return `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
485
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, visitCatchClause, visitExpressionStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitIfStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, } 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,6 +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);
51
+ case ts.SyntaxKind.BreakStatement:
52
+ return visitBreakStatement.call(this, node, context);
53
+ case ts.SyntaxKind.ContinueStatement:
54
+ return visitContinueStatement.call(this, node, context);
55
+ case ts.SyntaxKind.LabeledStatement:
56
+ return visitLabeledStatement.call(this, node, context);
37
57
  case ts.SyntaxKind.IfStatement:
38
58
  return visitIfStatement.call(this, node, context);
39
59
  case ts.SyntaxKind.PrefixUnaryExpression:
@@ -50,6 +70,8 @@ export function visit(node, context) {
50
70
  return visitExpressionStatement.call(this, node, context);
51
71
  case ts.SyntaxKind.BinaryExpression:
52
72
  return visitBinaryExpression.call(this, node, context);
73
+ case ts.SyntaxKind.ConditionalExpression:
74
+ return visitConditionalExpression.call(this, node, context);
53
75
  case ts.SyntaxKind.ThrowStatement:
54
76
  return visitThrowStatement.call(this, node, context);
55
77
  case ts.SyntaxKind.TryStatement:
@@ -58,6 +80,8 @@ export function visit(node, context) {
58
80
  return visitCatchClause.call(this, node, context);
59
81
  case ts.SyntaxKind.CallExpression:
60
82
  return visitCallExpression.call(this, node, context);
83
+ case ts.SyntaxKind.YieldExpression:
84
+ return visitYieldExpression.call(this, node, context);
61
85
  case ts.SyntaxKind.ReturnStatement:
62
86
  return visitReturnStatement.call(this, node, context);
63
87
  case ts.SyntaxKind.Identifier:
@@ -70,6 +94,12 @@ export function visit(node, context) {
70
94
  return visitNoSubstitutionTemplateLiteral.call(this, node);
71
95
  case ts.SyntaxKind.TemplateExpression:
72
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);
73
103
  case ts.SyntaxKind.TrueKeyword:
74
104
  return visitTrueKeyword.call(this);
75
105
  case ts.SyntaxKind.FalseKeyword:
@@ -80,6 +110,8 @@ export function visit(node, context) {
80
110
  return visitUndefinedKeyword.call(this);
81
111
  case ts.SyntaxKind.NullKeyword:
82
112
  return visitNullKeyword.call(this);
113
+ case ts.SyntaxKind.ThisKeyword:
114
+ return visitThisKeyword.call(this);
83
115
  default:
84
116
  return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
85
117
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.1.2",
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"