@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,64 +1,159 @@
1
1
  import ts from "typescript";
2
2
  import { CodeGenerator } from "./";
3
- function visitObjectPropertyName(node, context) {
3
+ export function visitObjectPropertyName(node, context) {
4
4
  if (ts.isNumericLiteral(node)) {
5
- return node.getText();
5
+ return context.isBracketNotationPropertyAccess
6
+ ? node.getText()
7
+ : `"${node.getText()}"`;
6
8
  }
7
- else if (ts.isStringLiteral(node)) {
9
+ if (ts.isStringLiteral(node)) {
8
10
  return `"${node.getText().substring(1, node.getText().length - 1) // remove trailing "' from original name
9
11
  }"`;
10
12
  }
11
- else if (ts.isComputedPropertyName(node)) {
12
- let name = ts.isIdentifier(node.expression)
13
- ? `jspp::Access::deref(${node.expression.getText()},${this.getJsVarName(node.expression)})`
14
- : this.visit(node.expression, context);
15
- name += ".to_std_string()";
16
- return name;
13
+ if (ts.isComputedPropertyName(node)) {
14
+ const compExpr = node.expression;
15
+ let propName = this.visit(compExpr, context);
16
+ if (ts.isIdentifier(compExpr)) {
17
+ const scope = this.getScopeForNode(compExpr);
18
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(compExpr.getText(), scope);
19
+ propName = this.getDerefCode(propName, this.getJsVarName(compExpr), context, typeInfo);
20
+ }
21
+ return propName;
17
22
  }
18
- else if (context.isPropertyNameAccess) {
23
+ if (context.isBracketNotationPropertyAccess) {
19
24
  return this.visit(node, context);
20
25
  }
21
- else {
22
- return `"${node.getText()}"`;
23
- }
26
+ return `"${node.getText()}"`;
24
27
  }
25
28
  export function visitObjectLiteralExpression(node, context) {
26
29
  const obj = node;
27
- let props = "";
30
+ const objVar = this.generateUniqueName("__obj_", this.getDeclaredSymbols(node));
31
+ let code = `([&]() {
32
+ ${this.indent()} auto ${objVar} = jspp::AnyValue::make_object_with_proto({}, ::Object.get_own_property("prototype"));\n`;
33
+ this.indentationLevel++;
28
34
  for (const prop of obj.properties) {
29
- // console.log("Property kind:", ts.SyntaxKind[prop.kind]);
30
35
  if (ts.isPropertyAssignment(prop)) {
31
- const key = visitObjectPropertyName.call(this, prop.name, context);
32
- const value = ts.isIdentifier(prop.initializer)
33
- ? `jspp::Access::deref(${this.visit(prop.initializer, context)}, ${this.getJsVarName(prop.initializer)})`
34
- : this.visit(prop.initializer, context);
35
- props += `{${key}, ${value}},`;
36
+ const key = visitObjectPropertyName.call(this, prop.name, {
37
+ ...context,
38
+ isObjectLiteralExpression: true,
39
+ });
40
+ const initializer = prop.initializer;
41
+ let value = this.visit(initializer, context);
42
+ if (ts.isIdentifier(initializer)) {
43
+ const scope = this.getScopeForNode(initializer);
44
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initializer.text, scope);
45
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
46
+ value = this.getDerefCode(value, this.getJsVarName(initializer), context, typeInfo);
47
+ }
48
+ }
49
+ code +=
50
+ `${this.indent()}${objVar}.define_data_property(${key}, ${value});\n`;
36
51
  }
37
52
  else if (ts.isShorthandPropertyAssignment(prop)) {
38
- const key = visitObjectPropertyName.call(this, prop.name, context);
39
- const value = `jspp::Access::deref(${this.visit(prop.name, context)}, ${this.getJsVarName(prop.name)})`;
40
- props += `{${key}, ${value}},`;
53
+ const key = visitObjectPropertyName.call(this, prop.name, {
54
+ ...context,
55
+ isObjectLiteralExpression: true,
56
+ });
57
+ const scope = this.getScopeForNode(prop.name);
58
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prop.name.text, scope);
59
+ let value = this.visit(prop.name, context);
60
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
61
+ value = this.getDerefCode(value, this.getJsVarName(prop.name), context, typeInfo);
62
+ }
63
+ code +=
64
+ `${this.indent()}${objVar}.define_data_property(${key}, ${value});\n`;
65
+ }
66
+ else if (ts.isMethodDeclaration(prop)) {
67
+ const key = visitObjectPropertyName.call(this, prop.name, {
68
+ ...context,
69
+ isObjectLiteralExpression: true,
70
+ });
71
+ const lambda = this.generateLambda(prop, {
72
+ ...context,
73
+ isInsideFunction: true,
74
+ });
75
+ code +=
76
+ `${this.indent()}${objVar}.define_data_property(${key}, ${lambda});\n`;
77
+ }
78
+ else if (ts.isGetAccessor(prop)) {
79
+ const key = visitObjectPropertyName.call(this, prop.name, {
80
+ ...context,
81
+ isObjectLiteralExpression: true,
82
+ });
83
+ const lambda = this.generateLambda(prop, {
84
+ ...context,
85
+ isInsideFunction: true,
86
+ });
87
+ code +=
88
+ `${this.indent()}${objVar}.define_getter(${key}, ${lambda});\n`;
89
+ }
90
+ else if (ts.isSetAccessor(prop)) {
91
+ const key = visitObjectPropertyName.call(this, prop.name, {
92
+ ...context,
93
+ isObjectLiteralExpression: true,
94
+ });
95
+ const lambda = this.generateLambda(prop, {
96
+ ...context,
97
+ isInsideFunction: true,
98
+ });
99
+ code +=
100
+ `${this.indent()}${objVar}.define_setter(${key}, ${lambda});\n`;
41
101
  }
42
102
  }
43
- return `jspp::AnyValue::make_object({${props}})`;
103
+ this.indentationLevel--;
104
+ code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
105
+ return code;
44
106
  }
45
107
  export function visitArrayLiteralExpression(node, context) {
46
108
  const elements = node.elements
47
- .map((elem) => ts.isIdentifier(elem)
48
- ? `jspp::Access::deref(${this.visit(elem, context)}, ${this.getJsVarName(elem)})`
49
- : this.visit(elem, context))
50
- .join(", ");
51
- return `jspp::AnyValue::make_array({${elements}})`;
109
+ .map((elem) => {
110
+ let elemText = this.visit(elem, context);
111
+ if (ts.isIdentifier(elem)) {
112
+ const scope = this.getScopeForNode(elem);
113
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elem.text, scope);
114
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
115
+ elemText = this.getDerefCode(elemText, this.getJsVarName(elem), context, typeInfo);
116
+ }
117
+ }
118
+ return elemText;
119
+ });
120
+ const elementsJoined = elements.join(", ");
121
+ const elementsSpan = elements.length > 0
122
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${elementsJoined}}, ${elements.length})`
123
+ : "std::span<const jspp::AnyValue>{}";
124
+ return `jspp::AnyValue::make_array_with_proto(${elementsSpan}, ::Array.get_own_property("prototype"))`;
52
125
  }
53
126
  export function visitPrefixUnaryExpression(node, context) {
54
127
  const prefixUnaryExpr = node;
55
128
  const operand = this.visit(prefixUnaryExpr.operand, context);
56
129
  const operator = ts.tokenToString(prefixUnaryExpr.operator);
57
130
  if (operator === "++" || operator === "--") {
58
- return `${operator}(*${operand})`;
131
+ let target = operand;
132
+ if (ts.isIdentifier(prefixUnaryExpr.operand)) {
133
+ const scope = this.getScopeForNode(prefixUnaryExpr.operand);
134
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
135
+ if (context.derefBeforeAssignment) {
136
+ target = this.getDerefCode(operand, operand, context, typeInfo);
137
+ }
138
+ else if (typeInfo.needsHeapAllocation) {
139
+ target = `*${operand}`;
140
+ }
141
+ }
142
+ return `${operator}(${target})`;
59
143
  }
60
144
  if (operator === "~") {
61
- return `${operator}(*${operand})`;
145
+ let target = operand;
146
+ if (ts.isIdentifier(prefixUnaryExpr.operand)) {
147
+ const scope = this.getScopeForNode(prefixUnaryExpr.operand);
148
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
149
+ if (context.derefBeforeAssignment) {
150
+ target = this.getDerefCode(operand, operand, context, typeInfo);
151
+ }
152
+ else if (typeInfo.needsHeapAllocation) {
153
+ target = `*${operand}`;
154
+ }
155
+ }
156
+ return `${operator}(${target})`;
62
157
  }
63
158
  return `${operator}${operand}`;
64
159
  }
@@ -66,7 +161,18 @@ export function visitPostfixUnaryExpression(node, context) {
66
161
  const postfixUnaryExpr = node;
67
162
  const operand = this.visit(postfixUnaryExpr.operand, context);
68
163
  const operator = ts.tokenToString(postfixUnaryExpr.operator);
69
- return `(*${operand})${operator}`;
164
+ let target = operand;
165
+ if (ts.isIdentifier(postfixUnaryExpr.operand)) {
166
+ const scope = this.getScopeForNode(postfixUnaryExpr.operand);
167
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(postfixUnaryExpr.operand.getText(), scope);
168
+ if (context.derefBeforeAssignment) {
169
+ target = this.getDerefCode(operand, operand, context, typeInfo);
170
+ }
171
+ else if (typeInfo.needsHeapAllocation) {
172
+ target = `*${operand}`;
173
+ }
174
+ }
175
+ return `(${target})${operator}`;
70
176
  }
71
177
  export function visitParenthesizedExpression(node, context) {
72
178
  const parenExpr = node;
@@ -74,6 +180,13 @@ export function visitParenthesizedExpression(node, context) {
74
180
  }
75
181
  export function visitPropertyAccessExpression(node, context) {
76
182
  const propAccess = node;
183
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
184
+ if (!context.superClassVar) {
185
+ throw new Error("super.prop accessed but no super class variable found in context");
186
+ }
187
+ const propName = propAccess.name.getText();
188
+ return `jspp::AnyValue::resolve_property_for_read((${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}"), ${this.globalThisVar}, "${this.escapeString(propName)}")`;
189
+ }
77
190
  const exprText = this.visit(propAccess.expression, context);
78
191
  const propName = propAccess.name.getText();
79
192
  const scope = this.getScopeForNode(propAccess.expression);
@@ -82,21 +195,24 @@ export function visitPropertyAccessExpression(node, context) {
82
195
  : null;
83
196
  if (ts.isIdentifier(propAccess.expression) && !typeInfo &&
84
197
  !this.isBuiltinObject(propAccess.expression)) {
85
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(propAccess.expression)})`;
198
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(propAccess.expression)})`;
86
199
  }
87
- let finalExpr = "";
88
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
89
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(propAccess.expression)})`;
200
+ let finalExpr = exprText;
201
+ if (typeInfo &&
202
+ !typeInfo.isParameter &&
203
+ !typeInfo.isBuiltin &&
204
+ ts.isIdentifier(propAccess.expression)) {
205
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), context, typeInfo);
90
206
  }
91
- else {
92
- finalExpr = exprText;
207
+ if (propAccess.questionDotToken) {
208
+ return `jspp::Access::optional_get_property(${finalExpr}, "${propName}")`;
93
209
  }
94
210
  return `${finalExpr}.get_own_property("${propName}")`;
95
211
  }
96
212
  export function visitElementAccessExpression(node, context) {
97
213
  const elemAccess = node;
98
214
  const exprText = this.visit(elemAccess.expression, context);
99
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
215
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
100
216
  // Dereference the expression being accessed
101
217
  const exprScope = this.getScopeForNode(elemAccess.expression);
102
218
  const exprTypeInfo = ts.isIdentifier(elemAccess.expression)
@@ -104,50 +220,159 @@ export function visitElementAccessExpression(node, context) {
104
220
  : null;
105
221
  if (ts.isIdentifier(elemAccess.expression) && !exprTypeInfo &&
106
222
  !this.isBuiltinObject(elemAccess.expression)) {
107
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.expression)})`;
223
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.expression)})`;
224
+ }
225
+ let finalExpr = exprText;
226
+ if (exprTypeInfo &&
227
+ !exprTypeInfo.isParameter &&
228
+ !exprTypeInfo.isBuiltin &&
229
+ ts.isIdentifier(elemAccess.expression)) {
230
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(elemAccess.expression), context, exprTypeInfo);
108
231
  }
109
- const finalExpr = exprTypeInfo && !exprTypeInfo.isParameter && !exprTypeInfo.isBuiltin
110
- ? `jspp::Access::deref(${exprText}, ${this.getJsVarName(elemAccess.expression)})`
111
- : exprText;
112
232
  // Dereference the argument expression if it's an identifier
113
233
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
114
234
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
115
235
  const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
116
236
  if (!argTypeInfo && !this.isBuiltinObject(elemAccess.argumentExpression)) {
117
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.argumentExpression)})`;
237
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
118
238
  }
119
- if (argTypeInfo && !argTypeInfo.isParameter && !argTypeInfo.isBuiltin) {
120
- argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
239
+ if (argTypeInfo &&
240
+ !argTypeInfo.isParameter &&
241
+ !argTypeInfo.isBuiltin) {
242
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
121
243
  }
122
244
  }
245
+ if (elemAccess.questionDotToken) {
246
+ return `jspp::Access::optional_get_element(${finalExpr}, ${argText})`;
247
+ }
123
248
  return `${finalExpr}.get_own_property(${argText})`;
124
249
  }
125
250
  export function visitBinaryExpression(node, context) {
126
251
  const binExpr = node;
127
252
  const opToken = binExpr.operatorToken;
128
253
  let op = opToken.getText();
129
- if (opToken.kind === ts.SyntaxKind.PlusEqualsToken ||
130
- opToken.kind === ts.SyntaxKind.MinusEqualsToken ||
131
- opToken.kind === ts.SyntaxKind.AsteriskEqualsToken ||
132
- opToken.kind === ts.SyntaxKind.SlashEqualsToken ||
133
- opToken.kind === ts.SyntaxKind.PercentEqualsToken) {
254
+ const assignmentOperators = [
255
+ ts.SyntaxKind.PlusEqualsToken,
256
+ ts.SyntaxKind.MinusEqualsToken,
257
+ ts.SyntaxKind.AsteriskEqualsToken,
258
+ ts.SyntaxKind.SlashEqualsToken,
259
+ ts.SyntaxKind.PercentEqualsToken,
260
+ ts.SyntaxKind.AsteriskAsteriskEqualsToken,
261
+ ts.SyntaxKind.LessThanLessThanEqualsToken,
262
+ ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
263
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
264
+ ts.SyntaxKind.AmpersandEqualsToken,
265
+ ts.SyntaxKind.BarEqualsToken,
266
+ ts.SyntaxKind.CaretEqualsToken,
267
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken,
268
+ ts.SyntaxKind.BarBarEqualsToken,
269
+ ts.SyntaxKind.QuestionQuestionEqualsToken,
270
+ ];
271
+ if (assignmentOperators.includes(opToken.kind)) {
272
+ if (opToken.kind ===
273
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
274
+ const leftText = this.visit(binExpr.left, context);
275
+ const rightText = this.visit(binExpr.right, context);
276
+ let target = leftText;
277
+ if (ts.isIdentifier(binExpr.left)) {
278
+ const scope = this.getScopeForNode(binExpr.left);
279
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
280
+ target = typeInfo.needsHeapAllocation
281
+ ? `*${leftText}`
282
+ : leftText;
283
+ return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
284
+ }
285
+ }
286
+ if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
287
+ const leftText = this.visit(binExpr.left, context);
288
+ const rightText = this.visit(binExpr.right, context);
289
+ let target = leftText;
290
+ if (ts.isIdentifier(binExpr.left)) {
291
+ const scope = this.getScopeForNode(binExpr.left);
292
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
293
+ target = typeInfo.needsHeapAllocation
294
+ ? `*${leftText}`
295
+ : leftText;
296
+ return `${target} = jspp::pow(${target}, ${rightText})`;
297
+ }
298
+ // For complex LHS, we need a different approach, but this is a start.
299
+ }
300
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
301
+ opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
302
+ opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
303
+ const leftText = this.visit(binExpr.left, context);
304
+ const rightText = this.visit(binExpr.right, context);
305
+ let target = leftText;
306
+ if (ts.isIdentifier(binExpr.left)) {
307
+ const scope = this.getScopeForNode(binExpr.left);
308
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
309
+ target = typeInfo.needsHeapAllocation
310
+ ? `*${leftText}`
311
+ : leftText;
312
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
313
+ return `jspp::logical_and_assign(${target}, ${rightText})`;
314
+ }
315
+ else if (opToken.kind === ts.SyntaxKind.BarBarEqualsToken) {
316
+ return `jspp::logical_or_assign(${target}, ${rightText})`;
317
+ }
318
+ else {
319
+ return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
320
+ }
321
+ }
322
+ }
134
323
  const leftText = this.visit(binExpr.left, context);
135
- const rightText = this.visit(binExpr.right, context);
136
- return `*${leftText} ${op} ${rightText}`;
324
+ let rightText = ts.isNumericLiteral(binExpr.right)
325
+ ? binExpr.right.getText()
326
+ : this.visit(binExpr.right, context);
327
+ if (ts.isIdentifier(binExpr.right)) {
328
+ const scope = this.getScopeForNode(binExpr.right);
329
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
330
+ rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
331
+ }
332
+ let target = leftText;
333
+ if (ts.isIdentifier(binExpr.left)) {
334
+ const scope = this.getScopeForNode(binExpr.left);
335
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
336
+ if (context.derefBeforeAssignment) {
337
+ target = this.getDerefCode(leftText, leftText, context, typeInfo);
338
+ }
339
+ else if (typeInfo.needsHeapAllocation) {
340
+ target = `*${leftText}`;
341
+ }
342
+ }
343
+ return `${target} ${op} ${rightText}`;
137
344
  }
138
345
  if (opToken.kind === ts.SyntaxKind.EqualsToken) {
139
- const rightText = this.visit(binExpr.right, context);
346
+ let rightText = this.visit(binExpr.right, context);
140
347
  if (ts.isPropertyAccessExpression(binExpr.left)) {
141
348
  const propAccess = binExpr.left;
349
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
350
+ const propName = propAccess.name.getText();
351
+ let finalRightText = rightText;
352
+ // Deref right text logic...
353
+ if (ts.isIdentifier(binExpr.right)) {
354
+ const rightScope = this.getScopeForNode(binExpr.right);
355
+ const rightTypeInfo = this.typeAnalyzer.scopeManager
356
+ .lookupFromScope(binExpr.right.getText(), rightScope);
357
+ if (rightTypeInfo &&
358
+ !rightTypeInfo.isParameter &&
359
+ !rightTypeInfo.isBuiltin) {
360
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
361
+ }
362
+ }
363
+ // Approximate super assignment as setting property on 'this'
364
+ return `(${this.globalThisVar}).set_own_property("${propName}", ${finalRightText})`;
365
+ }
142
366
  const objExprText = this.visit(propAccess.expression, context);
143
367
  const propName = propAccess.name.getText();
144
- const scope = this.getScopeForNode(propAccess.expression);
145
- const typeInfo = ts.isIdentifier(propAccess.expression)
146
- ? this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope)
147
- : null;
148
- const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
149
- ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(propAccess.expression)})`
150
- : objExprText;
368
+ let finalObjExpr = objExprText;
369
+ if (ts.isIdentifier(propAccess.expression)) {
370
+ const scope = this.getScopeForNode(propAccess.expression);
371
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope);
372
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
373
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(propAccess.expression), context, typeInfo);
374
+ }
375
+ }
151
376
  let finalRightText = rightText;
152
377
  if (ts.isIdentifier(binExpr.right)) {
153
378
  const rightScope = this.getScopeForNode(binExpr.right);
@@ -156,23 +381,23 @@ export function visitBinaryExpression(node, context) {
156
381
  if (rightTypeInfo &&
157
382
  !rightTypeInfo.isParameter &&
158
383
  !rightTypeInfo.isBuiltin) {
159
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
384
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
160
385
  }
161
386
  }
162
387
  return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
163
- // return `${finalObjExpr}["${propName}"] = ${finalRightText}`;
164
388
  }
165
389
  else if (ts.isElementAccessExpression(binExpr.left)) {
166
390
  const elemAccess = binExpr.left;
167
391
  const objExprText = this.visit(elemAccess.expression, context);
168
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
169
- const scope = this.getScopeForNode(elemAccess.expression);
170
- const typeInfo = ts.isIdentifier(elemAccess.expression)
171
- ? this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope)
172
- : null;
173
- const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
174
- ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(elemAccess.expression)})`
175
- : objExprText;
392
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
393
+ let finalObjExpr = objExprText;
394
+ if (ts.isIdentifier(elemAccess.expression)) {
395
+ const scope = this.getScopeForNode(elemAccess.expression);
396
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope);
397
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
398
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), context, typeInfo);
399
+ }
400
+ }
176
401
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
177
402
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
178
403
  const argTypeInfo = this.typeAnalyzer.scopeManager
@@ -180,7 +405,7 @@ export function visitBinaryExpression(node, context) {
180
405
  if (argTypeInfo &&
181
406
  !argTypeInfo.isParameter &&
182
407
  !argTypeInfo.isBuiltin) {
183
- argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
408
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
184
409
  }
185
410
  }
186
411
  let finalRightText = rightText;
@@ -191,65 +416,97 @@ export function visitBinaryExpression(node, context) {
191
416
  if (rightTypeInfo &&
192
417
  !rightTypeInfo.isParameter &&
193
418
  !rightTypeInfo.isBuiltin) {
194
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
419
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, rightTypeInfo);
195
420
  }
196
421
  }
197
422
  return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
198
- // return `${finalObjExpr}[${argText}] = ${finalRightText}`;
199
423
  }
200
424
  const leftText = this.visit(binExpr.left, context);
201
425
  const scope = this.getScopeForNode(binExpr.left);
202
426
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
203
427
  if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
204
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
428
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
205
429
  }
206
430
  if (typeInfo?.isConst) {
207
- return `jspp::RuntimeError::throw_immutable_assignment_error()`;
431
+ return `jspp::Exception::throw_immutable_assignment()`;
432
+ }
433
+ if (ts.isNumericLiteral(binExpr.right)) {
434
+ rightText = binExpr.right.getText();
208
435
  }
209
- return `*${leftText} ${op} ${rightText}`;
436
+ const target = context.derefBeforeAssignment
437
+ ? this.getDerefCode(leftText, leftText, context, typeInfo)
438
+ : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
439
+ return `${target} ${op} ${rightText}`;
210
440
  }
211
441
  const leftText = this.visit(binExpr.left, context);
212
442
  const rightText = this.visit(binExpr.right, context);
213
- const leftIsIdentifier = ts.isIdentifier(binExpr.left);
214
- const rightIsIdentifier = ts.isIdentifier(binExpr.right);
215
- const scope = this.getScopeForNode(node);
216
- const leftTypeInfo = leftIsIdentifier
217
- ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope)
218
- : null;
219
- const rightTypeInfo = rightIsIdentifier
220
- ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope)
221
- : null;
222
- const finalLeft = leftIsIdentifier && leftTypeInfo && !leftTypeInfo.isParameter &&
223
- !leftTypeInfo.isBuiltin
224
- ? `jspp::Access::deref(${leftText}, ${this.getJsVarName(binExpr.left)})`
225
- : leftText;
226
- const finalRight = rightIsIdentifier && rightTypeInfo && !rightTypeInfo.isParameter &&
227
- !rightTypeInfo.isBuiltin
228
- ? `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`
229
- : rightText;
230
- if (leftIsIdentifier && !leftTypeInfo &&
231
- !this.isBuiltinObject(binExpr.left)) {
232
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
233
- }
234
- if (rightIsIdentifier && !rightTypeInfo &&
235
- !this.isBuiltinObject(binExpr.right)) {
236
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.right)})`;
443
+ let finalLeft = leftText;
444
+ if (ts.isIdentifier(binExpr.left)) {
445
+ const scope = this.getScopeForNode(binExpr.left);
446
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
447
+ if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
448
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
449
+ }
450
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
451
+ finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), context, typeInfo);
452
+ }
453
+ }
454
+ let finalRight = rightText;
455
+ if (ts.isIdentifier(binExpr.right)) {
456
+ const scope = this.getScopeForNode(binExpr.right);
457
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
458
+ if (!typeInfo &&
459
+ !this.isBuiltinObject(binExpr.right)) {
460
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.right)})`;
461
+ }
462
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
463
+ finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), context, typeInfo);
464
+ }
465
+ }
466
+ if (opToken.kind === ts.SyntaxKind.InKeyword) {
467
+ return `jspp::Access::in(${finalLeft}, ${finalRight})`;
468
+ }
469
+ if (opToken.kind === ts.SyntaxKind.InstanceOfKeyword) {
470
+ return `jspp::Access::instance_of(${finalLeft}, ${finalRight})`;
471
+ }
472
+ if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
473
+ return `jspp::logical_and(${finalLeft}, ${finalRight})`;
474
+ }
475
+ if (opToken.kind === ts.SyntaxKind.BarBarToken) {
476
+ return `jspp::logical_or(${finalLeft}, ${finalRight})`;
477
+ }
478
+ if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
479
+ return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
480
+ }
481
+ // Optimizations to prevent calling make_number multiple times
482
+ if (ts.isNumericLiteral(binExpr.left)) {
483
+ finalLeft = binExpr.left.getText();
484
+ }
485
+ if (ts.isNumericLiteral(binExpr.right)) {
486
+ finalRight = binExpr.right.getText();
237
487
  }
238
488
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
239
- return `${finalLeft}.is_strictly_equal_to_primitive(${finalRight})`;
489
+ return `jspp::is_strictly_equal_to(${finalLeft}, ${finalRight})`;
240
490
  }
241
491
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
242
- return `${finalLeft}.is_equal_to_primitive(${finalRight})`;
492
+ return `jspp::is_equal_to(${finalLeft}, ${finalRight})`;
243
493
  }
244
494
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
245
- return `${finalLeft}.not_strictly_equal_to_primitive(${finalRight})`;
495
+ return `jspp::not_strictly_equal_to(${finalLeft}, ${finalRight})`;
246
496
  }
247
497
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
248
- return `${finalLeft}.not_equal_to_primitive(${finalRight})`;
498
+ return `jspp::not_equal_to(${finalLeft}, ${finalRight})`;
249
499
  }
250
500
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
251
501
  return `jspp::pow(${finalLeft}, ${finalRight})`;
252
502
  }
503
+ if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
504
+ return `jspp::unsigned_right_shift(${finalLeft}, ${finalRight})`;
505
+ }
506
+ // Optimizations to prevent calling make_number multiple times
507
+ if (ts.isNumericLiteral(binExpr.left) && ts.isNumericLiteral(binExpr.right)) {
508
+ return `jspp::AnyValue::make_number(${finalLeft} ${op} ${finalRight})`;
509
+ }
253
510
  if (op === "+" ||
254
511
  op === "-" ||
255
512
  op === "*" ||
@@ -257,56 +514,159 @@ export function visitBinaryExpression(node, context) {
257
514
  op === "%" ||
258
515
  op === "^" ||
259
516
  op === "&" ||
260
- op === "|") {
517
+ op === "|" ||
518
+ op === "<<" ||
519
+ op === ">>") {
261
520
  return `(${finalLeft} ${op} ${finalRight})`;
262
521
  }
263
522
  return `${finalLeft} ${op} ${finalRight}`;
264
523
  }
524
+ export function visitConditionalExpression(node, context) {
525
+ const condExpr = node;
526
+ const condition = this.visit(condExpr.condition, context);
527
+ const whenTrueStmt = this.visit(condExpr.whenTrue, {
528
+ ...context,
529
+ isFunctionBody: false,
530
+ });
531
+ const whenFalseStmt = this.visit(condExpr.whenFalse, {
532
+ ...context,
533
+ isFunctionBody: false,
534
+ });
535
+ return `is_truthy(${condition}) ? ${whenTrueStmt} : ${whenFalseStmt}`;
536
+ }
265
537
  export function visitCallExpression(node, context) {
266
538
  const callExpr = node;
267
539
  const callee = callExpr.expression;
268
- const calleeName = this.escapeString(callee.getText());
269
- const args = callExpr.arguments
540
+ if (callee.kind === ts.SyntaxKind.SuperKeyword) {
541
+ if (!context.superClassVar) {
542
+ throw new Error("super() called but no super class variable found in context");
543
+ }
544
+ const args = callExpr.arguments.map((arg) => this.visit(arg, context))
545
+ .join(", ");
546
+ return `(${context.superClassVar}).call(${this.globalThisVar}, (const jspp::AnyValue[]){${args}}, "super")`;
547
+ }
548
+ const argsArray = callExpr.arguments
270
549
  .map((arg) => {
271
550
  const argText = this.visit(arg, context);
272
551
  if (ts.isIdentifier(arg)) {
273
552
  const scope = this.getScopeForNode(arg);
274
553
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
275
554
  if (!typeInfo) {
276
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(arg)})`;
555
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
277
556
  }
278
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
279
- return `jspp::Access::deref(${argText}, ${this.getJsVarName(arg)})`;
557
+ if (typeInfo && !typeInfo.isBuiltin) {
558
+ return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
280
559
  }
281
560
  }
282
561
  return argText;
283
- })
284
- .join(", ");
562
+ });
563
+ const args = argsArray.join(", ");
564
+ const argsCount = argsArray.length;
565
+ const argsSpan = argsCount > 0
566
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
567
+ : "std::span<const jspp::AnyValue>{}";
568
+ // Handle obj.method() -> pass obj as 'this'
569
+ if (ts.isPropertyAccessExpression(callee)) {
570
+ const propAccess = callee;
571
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
572
+ if (!context.superClassVar) {
573
+ throw new Error("super.method() called but no super class variable found in context");
574
+ }
575
+ const propName = propAccess.name.getText();
576
+ return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").call(${this.globalThisVar}, ${argsSpan}, "${this.escapeString(propName)}")`;
577
+ }
578
+ const objExpr = propAccess.expression;
579
+ const propName = propAccess.name.getText();
580
+ const objCode = this.visit(objExpr, context);
581
+ // We need to dereference the object expression if it's a variable
582
+ let derefObj = objCode;
583
+ if (ts.isIdentifier(objExpr)) {
584
+ const scope = this.getScopeForNode(objExpr);
585
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
586
+ if (!typeInfo && !this.isBuiltinObject(objExpr)) {
587
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
588
+ }
589
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
590
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
591
+ }
592
+ }
593
+ if (callExpr.questionDotToken) {
594
+ return `jspp::Access::optional_call(${derefObj}.get_own_property("${propName}"), ${derefObj}, ${argsSpan}, "${this.escapeString(propName)}")`;
595
+ }
596
+ return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property("${propName}").call(__obj, ${argsSpan}, "${this.escapeString(propName)}"); })()`;
597
+ }
598
+ // Handle obj[method]() -> pass obj as 'this'
599
+ if (ts.isElementAccessExpression(callee)) {
600
+ const elemAccess = callee;
601
+ const objExpr = elemAccess.expression;
602
+ const objCode = this.visit(objExpr, context);
603
+ let derefObj = objCode;
604
+ if (ts.isIdentifier(objExpr)) {
605
+ const scope = this.getScopeForNode(objExpr);
606
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
607
+ if (!typeInfo && !this.isBuiltinObject(objExpr)) {
608
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
609
+ }
610
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
611
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), context, typeInfo);
612
+ }
613
+ }
614
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
615
+ // Dereference argument if needed (logic copied from visitElementAccessExpression)
616
+ if (ts.isIdentifier(elemAccess.argumentExpression)) {
617
+ const argScope = this.getScopeForNode(elemAccess.argumentExpression);
618
+ const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
619
+ if (!argTypeInfo &&
620
+ !this.isBuiltinObject(elemAccess.argumentExpression)) {
621
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
622
+ }
623
+ if (argTypeInfo && !argTypeInfo.isParameter &&
624
+ !argTypeInfo.isBuiltin) {
625
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), context, argTypeInfo);
626
+ }
627
+ }
628
+ if (callExpr.questionDotToken) {
629
+ return `jspp::Access::optional_call(${derefObj}.get_own_property(${argText}), ${derefObj}, ${argsSpan})`;
630
+ }
631
+ return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property(${argText}).call(__obj, ${argsSpan}); })()`;
632
+ }
285
633
  const calleeCode = this.visit(callee, context);
286
- let derefCallee;
634
+ let derefCallee = calleeCode;
287
635
  if (ts.isIdentifier(callee)) {
288
636
  const scope = this.getScopeForNode(callee);
289
637
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
290
638
  if (!typeInfo && !this.isBuiltinObject(callee)) {
291
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(callee)})`;
639
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(callee)})`;
292
640
  }
293
641
  if (typeInfo?.isBuiltin) {
294
642
  derefCallee = calleeCode;
295
643
  }
296
- else {
297
- derefCallee = `jspp::Access::deref(${calleeCode}, ${this.getJsVarName(callee)})`;
644
+ else if (typeInfo) {
645
+ derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), context, typeInfo);
298
646
  }
299
647
  }
300
- else {
301
- derefCallee = calleeCode;
648
+ let calleeName = "";
649
+ if (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee)) {
650
+ calleeName = this.escapeString(callee.getText());
651
+ }
652
+ else if (ts.isParenthesizedExpression(callee) &&
653
+ ts.isFunctionExpression(callee.expression)) {
654
+ const funcExpr = callee.expression;
655
+ calleeName = this.escapeString(funcExpr.name?.getText() || "");
656
+ }
657
+ // Pass undefined as 'this' for normal function calls
658
+ const calleeNamePart = calleeName && calleeName.length > 0
659
+ ? `, "${calleeName}"`
660
+ : "";
661
+ if (callExpr.questionDotToken) {
662
+ return `jspp::Access::optional_call(${derefCallee}, jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
302
663
  }
303
- return `${derefCallee}.as_function()->call({${args}})`;
304
- // return `${derefCallee}.as_function("${calleeName}")->call({${args}})`;
664
+ return `${derefCallee}.call(jspp::Constants::UNDEFINED, ${argsSpan}${calleeNamePart})`;
305
665
  }
306
666
  export function visitVoidExpression(node, context) {
307
667
  const voidExpr = node;
308
668
  const exprText = this.visit(voidExpr.expression, context);
309
- return `(${exprText}, jspp::AnyValue::make_undefined())`;
669
+ return `(${exprText}, jspp::Constants::UNDEFINED)`;
310
670
  }
311
671
  export function visitTemplateExpression(node, context) {
312
672
  const templateExpr = node;
@@ -319,13 +679,12 @@ export function visitTemplateExpression(node, context) {
319
679
  const scope = this.getScopeForNode(expr);
320
680
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
321
681
  if (!typeInfo && !this.isBuiltinObject(expr)) {
322
- finalExpr =
323
- `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})`;
682
+ finalExpr = `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
324
683
  }
325
684
  else if (typeInfo &&
326
685
  !typeInfo.isParameter &&
327
686
  !typeInfo.isBuiltin) {
328
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
687
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
329
688
  }
330
689
  }
331
690
  result += ` + (${finalExpr})`;
@@ -335,3 +694,98 @@ export function visitTemplateExpression(node, context) {
335
694
  }
336
695
  return result;
337
696
  }
697
+ export function visitNewExpression(node, context) {
698
+ const newExpr = node;
699
+ const exprText = this.visit(newExpr.expression, context);
700
+ let derefExpr = exprText;
701
+ let name = `"${exprText}"`;
702
+ if (ts.isIdentifier(newExpr.expression)) {
703
+ name = this.getJsVarName(newExpr.expression);
704
+ const scope = this.getScopeForNode(newExpr.expression);
705
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(newExpr.expression.getText(), scope);
706
+ if (!typeInfo &&
707
+ !this.isBuiltinObject(newExpr.expression)) {
708
+ return `jspp::Exception::throw_unresolved_reference(${name})`;
709
+ }
710
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
711
+ derefExpr = this.getDerefCode(exprText, name, context, typeInfo);
712
+ }
713
+ }
714
+ const argsArray = newExpr.arguments
715
+ ? newExpr.arguments
716
+ .map((arg) => {
717
+ const argText = this.visit(arg, context);
718
+ if (ts.isIdentifier(arg)) {
719
+ const scope = this.getScopeForNode(arg);
720
+ const typeInfo = this.typeAnalyzer.scopeManager
721
+ .lookupFromScope(arg.text, scope);
722
+ if (!typeInfo) {
723
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
724
+ }
725
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
726
+ return this.getDerefCode(argText, this.getJsVarName(arg), context, typeInfo);
727
+ }
728
+ }
729
+ return argText;
730
+ })
731
+ : [];
732
+ const args = argsArray.join(", ");
733
+ const argsCount = argsArray.length;
734
+ const argsSpan = argsCount > 0
735
+ ? `std::span<const jspp::AnyValue>((const jspp::AnyValue[]){${args}}, ${argsCount})`
736
+ : "std::span<const jspp::AnyValue>{}";
737
+ return `${derefExpr}.construct(${argsSpan}, ${name})`;
738
+ }
739
+ export function visitTypeOfExpression(node, context) {
740
+ const typeOfExpr = node;
741
+ const exprText = this.visit(typeOfExpr.expression, context);
742
+ let derefExpr = exprText;
743
+ if (ts.isIdentifier(typeOfExpr.expression)) {
744
+ const scope = this.getScopeForNode(typeOfExpr.expression);
745
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(typeOfExpr.expression.getText(), scope);
746
+ if (!typeInfo &&
747
+ !this.isBuiltinObject(typeOfExpr.expression)) {
748
+ derefExpr = `/* undeclared variable: ${this.getJsVarName(typeOfExpr.expression)} */`; // typeof undeclared variable is 'undefined'
749
+ }
750
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
751
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
752
+ }
753
+ }
754
+ return `jspp::Access::type_of(${derefExpr})`;
755
+ }
756
+ export function visitAwaitExpression(node, context) {
757
+ const awaitExpr = node;
758
+ const exprText = this.visit(awaitExpr.expression, context);
759
+ let derefExpr = exprText;
760
+ if (ts.isIdentifier(awaitExpr.expression)) {
761
+ const scope = this.getScopeForNode(awaitExpr.expression);
762
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(awaitExpr.expression.getText(), scope);
763
+ if (!typeInfo &&
764
+ !this.isBuiltinObject(awaitExpr.expression)) {
765
+ // This assumes co_awaiting the result of throw_unresolved_reference (which throws)
766
+ // But throw_unresolved_reference returns AnyValue (void-ish).
767
+ // We can just throw before co_await.
768
+ // But we need to return a string expression.
769
+ // Using comma operator: (throw..., AnyValue())
770
+ return `(jspp::Exception::throw_unresolved_reference(${this.getJsVarName(awaitExpr.expression)}), co_await jspp::Constants::UNDEFINED)`;
771
+ }
772
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
773
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(awaitExpr.expression), context, typeInfo);
774
+ }
775
+ }
776
+ return `co_await ${derefExpr}`;
777
+ }
778
+ export function visitDeleteExpression(node, context) {
779
+ const expr = node.expression;
780
+ if (ts.isPropertyAccessExpression(expr)) {
781
+ const obj = this.visit(expr.expression, context);
782
+ const prop = `jspp::AnyValue::make_string("${expr.name.getText()}")`;
783
+ return `jspp::Access::delete_property(${obj}, ${prop})`;
784
+ }
785
+ else if (ts.isElementAccessExpression(expr)) {
786
+ const obj = this.visit(expr.expression, context);
787
+ const prop = this.visit(expr.argumentExpression, context);
788
+ return `jspp::Access::delete_property(${obj}, ${prop})`;
789
+ }
790
+ return "jspp::Constants::TRUE"; // delete on non-property is true in JS
791
+ }