@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
@@ -1,46 +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
+ if (ts.isNumericLiteral(node)) {
5
+ return context.isBracketNotationPropertyAccess
6
+ ? node.getText()
7
+ : `"${node.getText()}"`;
8
+ }
4
9
  if (ts.isStringLiteral(node)) {
5
10
  return `"${node.getText().substring(1, node.getText().length - 1) // remove trailing "' from original name
6
11
  }"`;
7
12
  }
8
- else if (ts.isComputedPropertyName(node)) {
9
- let name = ts.isIdentifier(node.expression)
10
- ? `jspp::Access::deref(${node.expression.getText()},${this.getJsVarName(node.expression)})`
11
- : this.visit(node.expression, context);
12
- name += ".to_std_string()";
13
- 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;
22
+ }
23
+ if (context.isBracketNotationPropertyAccess) {
24
+ return this.visit(node, context);
14
25
  }
15
- return context.isPropertyNameAccess
16
- ? node.getText()
17
- : `"${node.getText()}"`;
26
+ return `"${node.getText()}"`;
18
27
  }
19
28
  export function visitObjectLiteralExpression(node, context) {
20
29
  const obj = node;
21
- 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++;
22
34
  for (const prop of obj.properties) {
23
- // console.log("Property kind:", ts.SyntaxKind[prop.kind]);
24
35
  if (ts.isPropertyAssignment(prop)) {
25
- const key = visitObjectPropertyName.call(this, prop.name, context);
26
- const value = ts.isIdentifier(prop.initializer)
27
- ? `jspp::Access::deref(${this.visit(prop.initializer, context)}, ${this.getJsVarName(prop.initializer)})`
28
- : this.visit(prop.initializer, context);
29
- 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`;
30
51
  }
31
52
  else if (ts.isShorthandPropertyAssignment(prop)) {
32
- const key = visitObjectPropertyName.call(this, prop.name, context);
33
- const value = `jspp::Access::deref(${this.visit(prop.name, context)}, ${this.getJsVarName(prop.name)})`;
34
- 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`;
35
101
  }
36
102
  }
37
- return `jspp::AnyValue::make_object({${props}})`;
103
+ this.indentationLevel--;
104
+ code += `${this.indent()} return ${objVar};\n${this.indent()}})()`;
105
+ return code;
38
106
  }
39
107
  export function visitArrayLiteralExpression(node, context) {
40
108
  const elements = node.elements
41
- .map((elem) => ts.isIdentifier(elem)
42
- ? `jspp::Access::deref(${this.visit(elem, context)}, ${this.getJsVarName(elem)})`
43
- : 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
+ })
44
120
  .join(", ");
45
121
  return `jspp::AnyValue::make_array({${elements}})`;
46
122
  }
@@ -49,10 +125,32 @@ export function visitPrefixUnaryExpression(node, context) {
49
125
  const operand = this.visit(prefixUnaryExpr.operand, context);
50
126
  const operator = ts.tokenToString(prefixUnaryExpr.operator);
51
127
  if (operator === "++" || operator === "--") {
52
- 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})`;
53
140
  }
54
141
  if (operator === "~") {
55
- 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})`;
56
154
  }
57
155
  return `${operator}${operand}`;
58
156
  }
@@ -60,7 +158,18 @@ export function visitPostfixUnaryExpression(node, context) {
60
158
  const postfixUnaryExpr = node;
61
159
  const operand = this.visit(postfixUnaryExpr.operand, context);
62
160
  const operator = ts.tokenToString(postfixUnaryExpr.operator);
63
- 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}`;
64
173
  }
65
174
  export function visitParenthesizedExpression(node, context) {
66
175
  const parenExpr = node;
@@ -68,6 +177,13 @@ export function visitParenthesizedExpression(node, context) {
68
177
  }
69
178
  export function visitPropertyAccessExpression(node, context) {
70
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
+ }
71
187
  const exprText = this.visit(propAccess.expression, context);
72
188
  const propName = propAccess.name.getText();
73
189
  const scope = this.getScopeForNode(propAccess.expression);
@@ -76,22 +192,21 @@ export function visitPropertyAccessExpression(node, context) {
76
192
  : null;
77
193
  if (ts.isIdentifier(propAccess.expression) && !typeInfo &&
78
194
  !this.isBuiltinObject(propAccess.expression)) {
79
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(propAccess.expression)})`;
195
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(propAccess.expression)})`;
80
196
  }
81
- let finalExpr = "";
82
- if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
83
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(propAccess.expression)})`;
84
- }
85
- else {
86
- 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);
87
203
  }
88
204
  return `${finalExpr}.get_own_property("${propName}")`;
89
- // return `${finalExpr}["${propName}"]`;
90
205
  }
91
206
  export function visitElementAccessExpression(node, context) {
92
207
  const elemAccess = node;
93
208
  const exprText = this.visit(elemAccess.expression, context);
94
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
209
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
95
210
  // Dereference the expression being accessed
96
211
  const exprScope = this.getScopeForNode(elemAccess.expression);
97
212
  const exprTypeInfo = ts.isIdentifier(elemAccess.expression)
@@ -99,51 +214,93 @@ export function visitElementAccessExpression(node, context) {
99
214
  : null;
100
215
  if (ts.isIdentifier(elemAccess.expression) && !exprTypeInfo &&
101
216
  !this.isBuiltinObject(elemAccess.expression)) {
102
- 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);
103
225
  }
104
- const finalExpr = exprTypeInfo && !exprTypeInfo.isParameter && !exprTypeInfo.isBuiltin
105
- ? `jspp::Access::deref(${exprText}, ${this.getJsVarName(elemAccess.expression)})`
106
- : exprText;
107
226
  // Dereference the argument expression if it's an identifier
108
227
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
109
228
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
110
229
  const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
111
230
  if (!argTypeInfo && !this.isBuiltinObject(elemAccess.argumentExpression)) {
112
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.argumentExpression)})`;
231
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(elemAccess.argumentExpression)})`;
113
232
  }
114
- if (argTypeInfo && !argTypeInfo.isParameter && !argTypeInfo.isBuiltin) {
115
- 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);
116
237
  }
117
238
  }
118
239
  return `${finalExpr}.get_own_property(${argText})`;
119
- // return `${finalExpr}[${argText}]`;
120
240
  }
121
241
  export function visitBinaryExpression(node, context) {
122
242
  const binExpr = node;
123
243
  const opToken = binExpr.operatorToken;
124
244
  let op = opToken.getText();
125
- if (opToken.kind === ts.SyntaxKind.PlusEqualsToken ||
126
- opToken.kind === ts.SyntaxKind.MinusEqualsToken ||
127
- opToken.kind === ts.SyntaxKind.AsteriskEqualsToken ||
128
- opToken.kind === ts.SyntaxKind.SlashEqualsToken ||
129
- 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)) {
130
253
  const leftText = this.visit(binExpr.left, context);
131
- const rightText = this.visit(binExpr.right, context);
132
- 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}`;
133
272
  }
134
273
  if (opToken.kind === ts.SyntaxKind.EqualsToken) {
135
274
  const rightText = this.visit(binExpr.right, context);
136
275
  if (ts.isPropertyAccessExpression(binExpr.left)) {
137
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
+ }
138
294
  const objExprText = this.visit(propAccess.expression, context);
139
295
  const propName = propAccess.name.getText();
140
- const scope = this.getScopeForNode(propAccess.expression);
141
- const typeInfo = ts.isIdentifier(propAccess.expression)
142
- ? this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope)
143
- : null;
144
- const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
145
- ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(propAccess.expression)})`
146
- : 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
+ }
147
304
  let finalRightText = rightText;
148
305
  if (ts.isIdentifier(binExpr.right)) {
149
306
  const rightScope = this.getScopeForNode(binExpr.right);
@@ -152,23 +309,23 @@ export function visitBinaryExpression(node, context) {
152
309
  if (rightTypeInfo &&
153
310
  !rightTypeInfo.isParameter &&
154
311
  !rightTypeInfo.isBuiltin) {
155
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
312
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
156
313
  }
157
314
  }
158
315
  return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
159
- // return `${finalObjExpr}["${propName}"] = ${finalRightText}`;
160
316
  }
161
317
  else if (ts.isElementAccessExpression(binExpr.left)) {
162
318
  const elemAccess = binExpr.left;
163
319
  const objExprText = this.visit(elemAccess.expression, context);
164
- let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
165
- const scope = this.getScopeForNode(elemAccess.expression);
166
- const typeInfo = ts.isIdentifier(elemAccess.expression)
167
- ? this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope)
168
- : null;
169
- const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
170
- ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(elemAccess.expression)})`
171
- : 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
+ }
172
329
  if (ts.isIdentifier(elemAccess.argumentExpression)) {
173
330
  const argScope = this.getScopeForNode(elemAccess.argumentExpression);
174
331
  const argTypeInfo = this.typeAnalyzer.scopeManager
@@ -176,7 +333,7 @@ export function visitBinaryExpression(node, context) {
176
333
  if (argTypeInfo &&
177
334
  !argTypeInfo.isParameter &&
178
335
  !argTypeInfo.isBuiltin) {
179
- argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
336
+ argText = this.getDerefCode(argText, this.getJsVarName(elemAccess.argumentExpression), argTypeInfo);
180
337
  }
181
338
  }
182
339
  let finalRightText = rightText;
@@ -187,61 +344,61 @@ export function visitBinaryExpression(node, context) {
187
344
  if (rightTypeInfo &&
188
345
  !rightTypeInfo.isParameter &&
189
346
  !rightTypeInfo.isBuiltin) {
190
- finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
347
+ finalRightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), rightTypeInfo);
191
348
  }
192
349
  }
193
350
  return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
194
- // return `${finalObjExpr}[${argText}] = ${finalRightText}`;
195
351
  }
196
352
  const leftText = this.visit(binExpr.left, context);
197
353
  const scope = this.getScopeForNode(binExpr.left);
198
354
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
199
355
  if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
200
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
356
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(binExpr.left)})`;
201
357
  }
202
358
  if (typeInfo?.isConst) {
203
- return `jspp::RuntimeError::throw_immutable_assignment_error()`;
359
+ return `jspp::Exception::throw_immutable_assignment()`;
204
360
  }
205
- 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}`;
206
365
  }
207
366
  const leftText = this.visit(binExpr.left, context);
208
367
  const rightText = this.visit(binExpr.right, context);
209
- const leftIsIdentifier = ts.isIdentifier(binExpr.left);
210
- const rightIsIdentifier = ts.isIdentifier(binExpr.right);
211
- const scope = this.getScopeForNode(node);
212
- const leftTypeInfo = leftIsIdentifier
213
- ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope)
214
- : null;
215
- const rightTypeInfo = rightIsIdentifier
216
- ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope)
217
- : null;
218
- const finalLeft = leftIsIdentifier && leftTypeInfo && !leftTypeInfo.isParameter &&
219
- !leftTypeInfo.isBuiltin
220
- ? `jspp::Access::deref(${leftText}, ${this.getJsVarName(binExpr.left)})`
221
- : leftText;
222
- const finalRight = rightIsIdentifier && rightTypeInfo && !rightTypeInfo.isParameter &&
223
- !rightTypeInfo.isBuiltin
224
- ? `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`
225
- : rightText;
226
- if (leftIsIdentifier && !leftTypeInfo &&
227
- !this.isBuiltinObject(binExpr.left)) {
228
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
229
- }
230
- if (rightIsIdentifier && !rightTypeInfo &&
231
- !this.isBuiltinObject(binExpr.right)) {
232
- 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
+ }
233
390
  }
234
391
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
235
- return `${finalLeft}.is_strictly_equal_to_primitive(${finalRight})`;
392
+ return `${finalLeft}.is_strictly_equal_to(${finalRight})`;
236
393
  }
237
394
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
238
- return `${finalLeft}.is_equal_to_primitive(${finalRight})`;
395
+ return `${finalLeft}.is_equal_to(${finalRight})`;
239
396
  }
240
397
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
241
- return `${finalLeft}.not_strictly_equal_to_primitive(${finalRight})`;
398
+ return `${finalLeft}.not_strictly_equal_to(${finalRight})`;
242
399
  }
243
400
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
244
- return `${finalLeft}.not_equal_to_primitive(${finalRight})`;
401
+ return `${finalLeft}.not_equal_to(${finalRight})`;
245
402
  }
246
403
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
247
404
  return `jspp::pow(${finalLeft}, ${finalRight})`;
@@ -258,10 +415,30 @@ export function visitBinaryExpression(node, context) {
258
415
  }
259
416
  return `${finalLeft} ${op} ${finalRight}`;
260
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
+ }
261
431
  export function visitCallExpression(node, context) {
262
432
  const callExpr = node;
263
433
  const callee = callExpr.expression;
264
- 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
+ }
265
442
  const args = callExpr.arguments
266
443
  .map((arg) => {
267
444
  const argText = this.visit(arg, context);
@@ -269,35 +446,100 @@ export function visitCallExpression(node, context) {
269
446
  const scope = this.getScopeForNode(arg);
270
447
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
271
448
  if (!typeInfo) {
272
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(arg)})`;
449
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(arg)})`;
273
450
  }
274
451
  if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
275
- return `jspp::Access::deref(${argText}, ${this.getJsVarName(arg)})`;
452
+ return this.getDerefCode(argText, this.getJsVarName(arg), typeInfo);
276
453
  }
277
454
  }
278
455
  return argText;
279
456
  })
280
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
+ }
281
517
  const calleeCode = this.visit(callee, context);
282
- let derefCallee;
518
+ let derefCallee = calleeCode;
283
519
  if (ts.isIdentifier(callee)) {
284
520
  const scope = this.getScopeForNode(callee);
285
521
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
286
522
  if (!typeInfo && !this.isBuiltinObject(callee)) {
287
- return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(callee)})`;
523
+ return `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(callee)})`;
288
524
  }
289
525
  if (typeInfo?.isBuiltin) {
290
526
  derefCallee = calleeCode;
291
527
  }
292
- else {
293
- derefCallee = `jspp::Access::deref(${calleeCode}, ${this.getJsVarName(callee)})`;
528
+ else if (typeInfo) {
529
+ derefCallee = this.getDerefCode(calleeCode, this.getJsVarName(callee), typeInfo);
294
530
  }
295
531
  }
296
- else {
297
- derefCallee = calleeCode;
532
+ let calleeName = "";
533
+ if (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee)) {
534
+ calleeName = this.escapeString(callee.getText());
298
535
  }
299
- return `${derefCallee}.as_function()->call({${args}})`;
300
- // 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}})`;
301
543
  }
302
544
  export function visitVoidExpression(node, context) {
303
545
  const voidExpr = node;
@@ -315,13 +557,12 @@ export function visitTemplateExpression(node, context) {
315
557
  const scope = this.getScopeForNode(expr);
316
558
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
317
559
  if (!typeInfo && !this.isBuiltinObject(expr)) {
318
- finalExpr =
319
- `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})`;
560
+ finalExpr = `jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})`;
320
561
  }
321
562
  else if (typeInfo &&
322
563
  !typeInfo.isParameter &&
323
564
  !typeInfo.isBuiltin) {
324
- finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
565
+ finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
325
566
  }
326
567
  }
327
568
  result += ` + (${finalExpr})`;
@@ -331,3 +572,78 @@ export function visitTemplateExpression(node, context) {
331
572
  }
332
573
  return result;
333
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
+ }