@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
@@ -1,52 +1,122 @@
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), 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({});\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), 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), 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))
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), typeInfo);
116
+ }
117
+ }
118
+ return elemText;
119
+ })
50
120
  .join(", ");
51
121
  return `jspp::AnyValue::make_array({${elements}})`;
52
122
  }
@@ -55,10 +125,32 @@ export function visitPrefixUnaryExpression(node, context) {
55
125
  const operand = this.visit(prefixUnaryExpr.operand, context);
56
126
  const operator = ts.tokenToString(prefixUnaryExpr.operator);
57
127
  if (operator === "++" || operator === "--") {
58
- return `${operator}(*${operand})`;
128
+ let target = operand;
129
+ if (ts.isIdentifier(prefixUnaryExpr.operand)) {
130
+ const scope = this.getScopeForNode(prefixUnaryExpr.operand);
131
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
132
+ if (context.derefBeforeAssignment) {
133
+ target = this.getDerefCode(operand, operand, typeInfo);
134
+ }
135
+ else if (typeInfo.needsHeapAllocation) {
136
+ target = `*${operand}`;
137
+ }
138
+ }
139
+ return `${operator}(${target})`;
59
140
  }
60
141
  if (operator === "~") {
61
- return `${operator}(*${operand})`;
142
+ let target = operand;
143
+ if (ts.isIdentifier(prefixUnaryExpr.operand)) {
144
+ const scope = this.getScopeForNode(prefixUnaryExpr.operand);
145
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(prefixUnaryExpr.operand.getText(), scope);
146
+ if (context.derefBeforeAssignment) {
147
+ target = this.getDerefCode(operand, operand, typeInfo);
148
+ }
149
+ else if (typeInfo.needsHeapAllocation) {
150
+ target = `*${operand}`;
151
+ }
152
+ }
153
+ return `${operator}(${target})`;
62
154
  }
63
155
  return `${operator}${operand}`;
64
156
  }
@@ -66,7 +158,18 @@ export function visitPostfixUnaryExpression(node, context) {
66
158
  const postfixUnaryExpr = node;
67
159
  const operand = this.visit(postfixUnaryExpr.operand, context);
68
160
  const operator = ts.tokenToString(postfixUnaryExpr.operator);
69
- return `(*${operand})${operator}`;
161
+ let target = operand;
162
+ if (ts.isIdentifier(postfixUnaryExpr.operand)) {
163
+ const scope = this.getScopeForNode(postfixUnaryExpr.operand);
164
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(postfixUnaryExpr.operand.getText(), scope);
165
+ if (context.derefBeforeAssignment) {
166
+ target = this.getDerefCode(operand, operand, typeInfo);
167
+ }
168
+ else if (typeInfo.needsHeapAllocation) {
169
+ target = `*${operand}`;
170
+ }
171
+ }
172
+ return `(${target})${operator}`;
70
173
  }
71
174
  export function visitParenthesizedExpression(node, context) {
72
175
  const parenExpr = node;
@@ -74,6 +177,13 @@ export function visitParenthesizedExpression(node, context) {
74
177
  }
75
178
  export function visitPropertyAccessExpression(node, context) {
76
179
  const propAccess = node;
180
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
181
+ if (!context.superClassVar) {
182
+ throw new Error("super.prop accessed but no super class variable found in context");
183
+ }
184
+ const propName = propAccess.name.getText();
185
+ return `jspp::AnyValue::resolve_property_for_read((${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}"), ${this.globalThisVar}, "${this.escapeString(propName)}")`;
186
+ }
77
187
  const exprText = this.visit(propAccess.expression, context);
78
188
  const propName = propAccess.name.getText();
79
189
  const scope = this.getScopeForNode(propAccess.expression);
@@ -82,21 +192,21 @@ export function visitPropertyAccessExpression(node, context) {
82
192
  : null;
83
193
  if (ts.isIdentifier(propAccess.expression) && !typeInfo &&
84
194
  !this.isBuiltinObject(propAccess.expression)) {
85
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(propAccess.expression)})`;
86
- }
87
- let finalExpr = "";
88
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
89
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(propAccess.expression)})`;
195
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(propAccess.expression)})`;
90
196
  }
91
- else {
92
- finalExpr = exprText;
197
+ let finalExpr = exprText;
198
+ if (typeInfo &&
199
+ !typeInfo.isParameter &&
200
+ !typeInfo.isBuiltin &&
201
+ ts.isIdentifier(propAccess.expression)) {
202
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(propAccess.expression), typeInfo);
93
203
  }
94
204
  return `${finalExpr}.get_own_property("${propName}")`;
95
205
  }
96
206
  export function visitElementAccessExpression(node, context) {
97
207
  const elemAccess = node;
98
208
  const exprText = this.visit(elemAccess.expression, context);
99
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
209
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
100
210
  // Dereference the expression being accessed
101
211
  const exprScope = this.getScopeForNode(elemAccess.expression);
102
212
  const exprTypeInfo = ts.isIdentifier(elemAccess.expression)
@@ -104,20 +214,26 @@ export function visitElementAccessExpression(node, context) {
104
214
  : null;
105
215
  if (ts.isIdentifier(elemAccess.expression) && !exprTypeInfo &&
106
216
  !this.isBuiltinObject(elemAccess.expression)) {
107
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.expression)})`;
217
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.expression)})`;
218
+ }
219
+ let finalExpr = exprText;
220
+ if (exprTypeInfo &&
221
+ !exprTypeInfo.isParameter &&
222
+ !exprTypeInfo.isBuiltin &&
223
+ ts.isIdentifier(elemAccess.expression)) {
224
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(elemAccess.expression), exprTypeInfo);
108
225
  }
109
- const finalExpr = exprTypeInfo && !exprTypeInfo.isParameter && !exprTypeInfo.isBuiltin
110
- ? `jspp::Access::deref(${exprText}, ${this.getJsVarName(elemAccess.expression)})`
111
- : exprText;
112
226
  // Dereference the argument expression if it's an identifier
113
227
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
114
228
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
115
229
  const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
116
230
  if (!argTypeInfo && !this.isBuiltinObject(elemAccess.argumentExpression)) {
117
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.argumentExpression)})`;
231
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
118
232
  }
119
- if (argTypeInfo && !argTypeInfo.isParameter && !argTypeInfo.isBuiltin) {
120
- argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
233
+ if (argTypeInfo &&
234
+ !argTypeInfo.isParameter &&
235
+ !argTypeInfo.isBuiltin) {
236
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
121
237
  }
122
238
  }
123
239
  return `${finalExpr}.get_own_property(${argText})`;
@@ -126,28 +242,65 @@ export function visitBinaryExpression(node, context) {
126
242
  const binExpr = node;
127
243
  const opToken = binExpr.operatorToken;
128
244
  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) {
245
+ const assignmentOperators = [
246
+ ts.SyntaxKind.PlusEqualsToken,
247
+ ts.SyntaxKind.MinusEqualsToken,
248
+ ts.SyntaxKind.AsteriskEqualsToken,
249
+ ts.SyntaxKind.SlashEqualsToken,
250
+ ts.SyntaxKind.PercentEqualsToken,
251
+ ];
252
+ if (assignmentOperators.includes(opToken.kind)) {
134
253
  const leftText = this.visit(binExpr.left, context);
135
- const rightText = this.visit(binExpr.right, context);
136
- return `*${leftText} ${op} ${rightText}`;
254
+ let rightText = this.visit(binExpr.right, context);
255
+ if (ts.isIdentifier(binExpr.right)) {
256
+ const scope = this.getScopeForNode(binExpr.right);
257
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
258
+ rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), typeInfo);
259
+ }
260
+ let target = leftText;
261
+ if (ts.isIdentifier(binExpr.left)) {
262
+ const scope = this.getScopeForNode(binExpr.left);
263
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
264
+ if (context.derefBeforeAssignment) {
265
+ target = this.getDerefCode(leftText, leftText, typeInfo);
266
+ }
267
+ else if (typeInfo.needsHeapAllocation) {
268
+ target = `*${leftText}`;
269
+ }
270
+ }
271
+ return `${target} ${op} ${rightText}`;
137
272
  }
138
273
  if (opToken.kind === ts.SyntaxKind.EqualsToken) {
139
274
  const rightText = this.visit(binExpr.right, context);
140
275
  if (ts.isPropertyAccessExpression(binExpr.left)) {
141
276
  const propAccess = binExpr.left;
277
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
278
+ const propName = propAccess.name.getText();
279
+ let finalRightText = rightText;
280
+ // Deref right text logic...
281
+ if (ts.isIdentifier(binExpr.right)) {
282
+ const rightScope = this.getScopeForNode(binExpr.right);
283
+ const rightTypeInfo = this.typeAnalyzer.scopeManager
284
+ .lookupFromScope(binExpr.right.getText(), rightScope);
285
+ if (rightTypeInfo &&
286
+ !rightTypeInfo.isParameter &&
287
+ !rightTypeInfo.isBuiltin) {
288
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
289
+ }
290
+ }
291
+ // Approximate super assignment as setting property on 'this'
292
+ return `(${this.globalThisVar}).set_own_property("${propName}", ${finalRightText})`;
293
+ }
142
294
  const objExprText = this.visit(propAccess.expression, context);
143
295
  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;
296
+ let finalObjExpr = objExprText;
297
+ if (ts.isIdentifier(propAccess.expression)) {
298
+ const scope = this.getScopeForNode(propAccess.expression);
299
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope);
300
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
301
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(propAccess.expression), typeInfo);
302
+ }
303
+ }
151
304
  let finalRightText = rightText;
152
305
  if (ts.isIdentifier(binExpr.right)) {
153
306
  const rightScope = this.getScopeForNode(binExpr.right);
@@ -156,23 +309,23 @@ export function visitBinaryExpression(node, context) {
156
309
  if (rightTypeInfo &&
157
310
  !rightTypeInfo.isParameter &&
158
311
  !rightTypeInfo.isBuiltin) {
159
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
312
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
160
313
  }
161
314
  }
162
315
  return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
163
- // return `${finalObjExpr}["${propName}"] = ${finalRightText}`;
164
316
  }
165
317
  else if (ts.isElementAccessExpression(binExpr.left)) {
166
318
  const elemAccess = binExpr.left;
167
319
  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;
320
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
321
+ let finalObjExpr = objExprText;
322
+ if (ts.isIdentifier(elemAccess.expression)) {
323
+ const scope = this.getScopeForNode(elemAccess.expression);
324
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope);
325
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
326
+ finalObjExpr = this.getDerefCode(objExprText, this.getJsVarName(elemAccess.expression), typeInfo);
327
+ }
328
+ }
176
329
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
177
330
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
178
331
  const argTypeInfo = this.typeAnalyzer.scopeManager
@@ -180,7 +333,7 @@ export function visitBinaryExpression(node, context) {
180
333
  if (argTypeInfo &&
181
334
  !argTypeInfo.isParameter &&
182
335
  !argTypeInfo.isBuiltin) {
183
- argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
336
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
184
337
  }
185
338
  }
186
339
  let finalRightText = rightText;
@@ -191,61 +344,61 @@ export function visitBinaryExpression(node, context) {
191
344
  if (rightTypeInfo &&
192
345
  !rightTypeInfo.isParameter &&
193
346
  !rightTypeInfo.isBuiltin) {
194
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
347
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
195
348
  }
196
349
  }
197
350
  return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
198
- // return `${finalObjExpr}[${argText}] = ${finalRightText}`;
199
351
  }
200
352
  const leftText = this.visit(binExpr.left, context);
201
353
  const scope = this.getScopeForNode(binExpr.left);
202
354
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
203
355
  if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
204
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
356
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
205
357
  }
206
358
  if (typeInfo?.isConst) {
207
- return `jspp::RuntimeError::throw_immutable_assignment_error()`;
359
+ return `jspp::Exception::throw_immutable_assignment()`;
208
360
  }
209
- return `*${leftText} ${op} ${rightText}`;
361
+ const target = context.derefBeforeAssignment
362
+ ? this.getDerefCode(leftText, leftText, typeInfo)
363
+ : (typeInfo.needsHeapAllocation ? `*${leftText}` : leftText);
364
+ return `${target} ${op} ${rightText}`;
210
365
  }
211
366
  const leftText = this.visit(binExpr.left, context);
212
367
  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)})`;
368
+ let finalLeft = leftText;
369
+ if (ts.isIdentifier(binExpr.left)) {
370
+ const scope = this.getScopeForNode(binExpr.left);
371
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope);
372
+ if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
373
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
374
+ }
375
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
376
+ finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), typeInfo);
377
+ }
378
+ }
379
+ let finalRight = rightText;
380
+ if (ts.isIdentifier(binExpr.right)) {
381
+ const scope = this.getScopeForNode(binExpr.right);
382
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
383
+ if (!typeInfo &&
384
+ !this.isBuiltinObject(binExpr.right)) {
385
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.right)})`;
386
+ }
387
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
388
+ finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), typeInfo);
389
+ }
237
390
  }
238
391
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
239
- return `${finalLeft}.is_strictly_equal_to_primitive(${finalRight})`;
392
+ return `${finalLeft}.is_strictly_equal_to(${finalRight})`;
240
393
  }
241
394
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
242
- return `${finalLeft}.is_equal_to_primitive(${finalRight})`;
395
+ return `${finalLeft}.is_equal_to(${finalRight})`;
243
396
  }
244
397
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
245
- return `${finalLeft}.not_strictly_equal_to_primitive(${finalRight})`;
398
+ return `${finalLeft}.not_strictly_equal_to(${finalRight})`;
246
399
  }
247
400
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
248
- return `${finalLeft}.not_equal_to_primitive(${finalRight})`;
401
+ return `${finalLeft}.not_equal_to(${finalRight})`;
249
402
  }
250
403
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
251
404
  return `jspp::pow(${finalLeft}, ${finalRight})`;
@@ -262,10 +415,30 @@ export function visitBinaryExpression(node, context) {
262
415
  }
263
416
  return `${finalLeft} ${op} ${finalRight}`;
264
417
  }
418
+ export function visitConditionalExpression(node, context) {
419
+ const condExpr = node;
420
+ const condition = this.visit(condExpr.condition, context);
421
+ const whenTrueStmt = this.visit(condExpr.whenTrue, {
422
+ ...context,
423
+ isFunctionBody: false,
424
+ });
425
+ const whenFalseStmt = this.visit(condExpr.whenFalse, {
426
+ ...context,
427
+ isFunctionBody: false,
428
+ });
429
+ return `(${condition}).is_truthy() ? ${whenTrueStmt} : ${whenFalseStmt}`;
430
+ }
265
431
  export function visitCallExpression(node, context) {
266
432
  const callExpr = node;
267
433
  const callee = callExpr.expression;
268
- const calleeName = this.escapeString(callee.getText());
434
+ if (callee.kind === ts.SyntaxKind.SuperKeyword) {
435
+ if (!context.superClassVar) {
436
+ throw new Error("super() called but no super class variable found in context");
437
+ }
438
+ const args = callExpr.arguments.map((arg) => this.visit(arg, context))
439
+ .join(", ");
440
+ return `(${context.superClassVar}).as_function("super")->call(${this.globalThisVar}, {${args}})`;
441
+ }
269
442
  const args = callExpr.arguments
270
443
  .map((arg) => {
271
444
  const argText = this.visit(arg, context);
@@ -273,35 +446,100 @@ export function visitCallExpression(node, context) {
273
446
  const scope = this.getScopeForNode(arg);
274
447
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
275
448
  if (!typeInfo) {
276
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(arg)})`;
449
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
277
450
  }
278
451
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
279
- return `jspp::Access::deref(${argText}, ${this.getJsVarName(arg)})`;
452
+ return this.getDerefCode(argText, this.getJsVarName(arg), typeInfo);
280
453
  }
281
454
  }
282
455
  return argText;
283
456
  })
284
457
  .join(", ");
458
+ // Handle obj.method() -> pass obj as 'this'
459
+ if (ts.isPropertyAccessExpression(callee)) {
460
+ const propAccess = callee;
461
+ if (propAccess.expression.kind === ts.SyntaxKind.SuperKeyword) {
462
+ if (!context.superClassVar) {
463
+ throw new Error("super.method() called but no super class variable found in context");
464
+ }
465
+ const propName = propAccess.name.getText();
466
+ return `(${context.superClassVar}).get_own_property("prototype").get_own_property("${propName}").as_function("${this.escapeString(propName)}")->call(${this.globalThisVar}, {${args}})`;
467
+ }
468
+ const objExpr = propAccess.expression;
469
+ const propName = propAccess.name.getText();
470
+ const objCode = this.visit(objExpr, context);
471
+ // We need to dereference the object expression if it's a variable
472
+ let derefObj = objCode;
473
+ if (ts.isIdentifier(objExpr)) {
474
+ const scope = this.getScopeForNode(objExpr);
475
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
476
+ if (!typeInfo && !this.isBuiltinObject(objExpr)) {
477
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
478
+ }
479
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
480
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), typeInfo);
481
+ }
482
+ }
483
+ return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property("${propName}").as_function("${this.escapeString(propName)}")->call(__obj, {${args}}); })()`;
484
+ }
485
+ // Handle obj[method]() -> pass obj as 'this'
486
+ if (ts.isElementAccessExpression(callee)) {
487
+ const elemAccess = callee;
488
+ const objExpr = elemAccess.expression;
489
+ const objCode = this.visit(objExpr, context);
490
+ let derefObj = objCode;
491
+ if (ts.isIdentifier(objExpr)) {
492
+ const scope = this.getScopeForNode(objExpr);
493
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(objExpr.getText(), scope);
494
+ if (!typeInfo && !this.isBuiltinObject(objExpr)) {
495
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(objExpr)})`;
496
+ }
497
+ if (typeInfo && !typeInfo.isBuiltin && !typeInfo.isParameter) {
498
+ derefObj = this.getDerefCode(objCode, this.getJsVarName(objExpr), typeInfo);
499
+ }
500
+ }
501
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
502
+ // Dereference argument if needed (logic copied from visitElementAccessExpression)
503
+ if (ts.isIdentifier(elemAccess.argumentExpression)) {
504
+ const argScope = this.getScopeForNode(elemAccess.argumentExpression);
505
+ const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
506
+ if (!argTypeInfo &&
507
+ !this.isBuiltinObject(elemAccess.argumentExpression)) {
508
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
509
+ }
510
+ if (argTypeInfo && !argTypeInfo.isParameter &&
511
+ !argTypeInfo.isBuiltin) {
512
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
513
+ }
514
+ }
515
+ return `([&](){ auto __obj = ${derefObj}; return __obj.get_own_property(${argText}).as_function()->call(__obj, {${args}}); })()`;
516
+ }
285
517
  const calleeCode = this.visit(callee, context);
286
- let derefCallee;
518
+ let derefCallee = calleeCode;
287
519
  if (ts.isIdentifier(callee)) {
288
520
  const scope = this.getScopeForNode(callee);
289
521
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
290
522
  if (!typeInfo && !this.isBuiltinObject(callee)) {
291
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(callee)})`;
523
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(callee)})`;
292
524
  }
293
525
  if (typeInfo?.isBuiltin) {
294
526
  derefCallee = calleeCode;
295
527
  }
296
- else {
297
- derefCallee = `jspp::Access::deref(${calleeCode}, ${this.getJsVarName(callee)})`;
528
+ else if (typeInfo) {
529
+ derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), typeInfo);
298
530
  }
299
531
  }
300
- else {
301
- derefCallee = calleeCode;
532
+ let calleeName = "";
533
+ if (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee)) {
534
+ calleeName = this.escapeString(callee.getText());
302
535
  }
303
- return `${derefCallee}.as_function()->call({${args}})`;
304
- // return `${derefCallee}.as_function("${calleeName}")->call({${args}})`;
536
+ else if (ts.isParenthesizedExpression(callee) &&
537
+ ts.isFunctionExpression(callee.expression)) {
538
+ const funcExpr = callee.expression;
539
+ calleeName = this.escapeString(funcExpr.name?.getText() || "");
540
+ }
541
+ // Pass undefined as 'this' for normal function calls
542
+ return `${derefCallee}.as_function("${calleeName}")->call(jspp::AnyValue::make_undefined(), {${args}})`;
305
543
  }
306
544
  export function visitVoidExpression(node, context) {
307
545
  const voidExpr = node;
@@ -319,13 +557,12 @@ export function visitTemplateExpression(node, context) {
319
557
  const scope = this.getScopeForNode(expr);
320
558
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
321
559
  if (!typeInfo && !this.isBuiltinObject(expr)) {
322
- finalExpr =
323
- `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})`;
560
+ finalExpr = `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
324
561
  }
325
562
  else if (typeInfo &&
326
563
  !typeInfo.isParameter &&
327
564
  !typeInfo.isBuiltin) {
328
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
565
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
329
566
  }
330
567
  }
331
568
  result += ` + (${finalExpr})`;
@@ -335,3 +572,78 @@ export function visitTemplateExpression(node, context) {
335
572
  }
336
573
  return result;
337
574
  }
575
+ export function visitNewExpression(node, context) {
576
+ const newExpr = node;
577
+ const exprText = this.visit(newExpr.expression, context);
578
+ let derefExpr = exprText;
579
+ if (ts.isIdentifier(newExpr.expression)) {
580
+ const scope = this.getScopeForNode(newExpr.expression);
581
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(newExpr.expression.getText(), scope);
582
+ if (!typeInfo &&
583
+ !this.isBuiltinObject(newExpr.expression)) {
584
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(newExpr.expression)})`;
585
+ }
586
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
587
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(newExpr.expression), typeInfo);
588
+ }
589
+ }
590
+ const args = newExpr.arguments
591
+ ? newExpr.arguments
592
+ .map((arg) => {
593
+ const argText = this.visit(arg, context);
594
+ if (ts.isIdentifier(arg)) {
595
+ const scope = this.getScopeForNode(arg);
596
+ const typeInfo = this.typeAnalyzer.scopeManager
597
+ .lookupFromScope(arg.text, scope);
598
+ if (!typeInfo) {
599
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
600
+ }
601
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
602
+ return this.getDerefCode(argText, this.getJsVarName(arg), typeInfo);
603
+ }
604
+ }
605
+ return argText;
606
+ })
607
+ .join(", ")
608
+ : "";
609
+ return `${derefExpr}.construct({${args}})`;
610
+ }
611
+ export function visitTypeOfExpression(node, context) {
612
+ const typeOfExpr = node;
613
+ const exprText = this.visit(typeOfExpr.expression, context);
614
+ let derefExpr = exprText;
615
+ if (ts.isIdentifier(typeOfExpr.expression)) {
616
+ const scope = this.getScopeForNode(typeOfExpr.expression);
617
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(typeOfExpr.expression.getText(), scope);
618
+ if (!typeInfo &&
619
+ !this.isBuiltinObject(typeOfExpr.expression)) {
620
+ derefExpr = `/* undeclared variable: ${this.getJsVarName(typeOfExpr.expression)} */`; // typeof undeclared variable is 'undefined'
621
+ }
622
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
623
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), typeInfo);
624
+ }
625
+ }
626
+ return `jspp::Access::typeof(${derefExpr})`;
627
+ }
628
+ export function visitAwaitExpression(node, context) {
629
+ const awaitExpr = node;
630
+ const exprText = this.visit(awaitExpr.expression, context);
631
+ let derefExpr = exprText;
632
+ if (ts.isIdentifier(awaitExpr.expression)) {
633
+ const scope = this.getScopeForNode(awaitExpr.expression);
634
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(awaitExpr.expression.getText(), scope);
635
+ if (!typeInfo &&
636
+ !this.isBuiltinObject(awaitExpr.expression)) {
637
+ // This assumes co_awaiting the result of throw_unresolved_reference (which throws)
638
+ // But throw_unresolved_reference returns AnyValue (void-ish).
639
+ // We can just throw before co_await.
640
+ // But we need to return a string expression.
641
+ // Using comma operator: (throw..., AnyValue())
642
+ return `(jspp::Exception::throw_unresolved_reference(${this.getJsVarName(awaitExpr.expression)}), co_await jspp::AnyValue::make_undefined())`;
643
+ }
644
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
645
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(awaitExpr.expression), typeInfo);
646
+ }
647
+ }
648
+ return `co_await ${derefExpr}`;
649
+ }