@ugo-studio/jspp 0.3.1 → 0.3.3

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 (65) hide show
  1. package/dist/cli/args.js +22 -0
  2. package/dist/cli/compiler.js +53 -0
  3. package/dist/cli/index.js +43 -117
  4. package/dist/cli/pch.js +71 -0
  5. package/dist/cli/runner.js +23 -0
  6. package/dist/cli/spinner.js +27 -11
  7. package/dist/cli/transpiler.js +20 -0
  8. package/dist/cli/utils.js +25 -27
  9. package/dist/cli/wasm.js +70 -0
  10. package/dist/index.js +17 -6
  11. package/dist/{analysis → interpreter/analysis}/scope.js +34 -1
  12. package/dist/{analysis → interpreter/analysis}/typeAnalyzer.js +542 -3
  13. package/dist/{core → interpreter/core}/codegen/class-handlers.js +1 -1
  14. package/dist/{core → interpreter/core}/codegen/control-flow-handlers.js +2 -2
  15. package/dist/{core → interpreter/core}/codegen/declaration-handlers.js +21 -9
  16. package/dist/{core → interpreter/core}/codegen/destructuring-handlers.js +3 -3
  17. package/dist/{core → interpreter/core}/codegen/expression-handlers.js +136 -82
  18. package/dist/{core → interpreter/core}/codegen/function-handlers.js +108 -61
  19. package/dist/{core → interpreter/core}/codegen/helpers.js +79 -11
  20. package/dist/interpreter/core/codegen/index.js +156 -0
  21. package/dist/{core → interpreter/core}/codegen/literal-handlers.js +9 -0
  22. package/dist/{core → interpreter/core}/codegen/statement-handlers.js +39 -1
  23. package/package.json +6 -4
  24. package/scripts/precompile-headers.ts +71 -19
  25. package/scripts/setup-emsdk.ts +114 -0
  26. package/src/prelude/any_value.cpp +851 -599
  27. package/src/prelude/any_value.hpp +28 -30
  28. package/src/prelude/jspp.hpp +3 -1
  29. package/src/prelude/library/boolean.cpp +30 -0
  30. package/src/prelude/library/boolean.hpp +14 -0
  31. package/src/prelude/library/error.cpp +2 -2
  32. package/src/prelude/library/global.cpp +2 -0
  33. package/src/prelude/library/math.cpp +46 -43
  34. package/src/prelude/library/math.hpp +2 -0
  35. package/src/prelude/library/object.cpp +1 -1
  36. package/src/prelude/types.hpp +10 -9
  37. package/src/prelude/utils/access.hpp +2 -36
  38. package/src/prelude/utils/assignment_operators.hpp +136 -20
  39. package/src/prelude/utils/log_any_value/log_any_value.hpp +1 -1
  40. package/src/prelude/utils/log_any_value/primitives.hpp +1 -1
  41. package/src/prelude/utils/operators.hpp +123 -88
  42. package/src/prelude/utils/operators_native.hpp +360 -0
  43. package/src/prelude/utils/well_known_symbols.hpp +13 -13
  44. package/src/prelude/values/array.cpp +3 -3
  45. package/src/prelude/values/boolean.cpp +64 -0
  46. package/src/prelude/values/iterator.cpp +262 -210
  47. package/src/prelude/values/number.cpp +137 -92
  48. package/src/prelude/values/object.cpp +163 -122
  49. package/src/prelude/values/prototypes/boolean.hpp +24 -0
  50. package/src/prelude/values/prototypes/number.hpp +8 -1
  51. package/dist/cli/file-utils.js +0 -20
  52. package/dist/cli-utils/args.js +0 -59
  53. package/dist/cli-utils/colors.js +0 -9
  54. package/dist/cli-utils/file-utils.js +0 -20
  55. package/dist/cli-utils/spinner.js +0 -55
  56. package/dist/cli.js +0 -153
  57. package/dist/core/codegen/index.js +0 -88
  58. package/src/prelude/utils/operators_primitive.hpp +0 -337
  59. /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
  60. /package/dist/{ast → interpreter/ast}/types.js +0 -0
  61. /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
  62. /package/dist/{core → interpreter/core}/constants.js +0 -0
  63. /package/dist/{core → interpreter/core}/error.js +0 -0
  64. /package/dist/{core → interpreter/core}/parser.js +0 -0
  65. /package/dist/{core → interpreter/core}/traverser.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
3
3
  import { CompilerError } from "../error.js";
4
4
  import { CodeGenerator } from "./index.js";
5
5
  export function visitVariableDeclarationList(node, context) {
@@ -18,8 +18,8 @@ export function visitVariableDeclaration(node, context) {
18
18
  return this.generateDestructuring(varDecl.name, rhsCode, context);
19
19
  }
20
20
  const name = varDecl.name.getText();
21
- if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
22
- throw new CompilerError(`Cannot declare a variable named '${name}' in strict mode.`, varDecl.name, "SyntaxError");
21
+ if (RESERVED_VAR_NAMES.has(name)) {
22
+ throw new CompilerError(`Cannot declare a variable named '${name}'.`, varDecl.name, "SyntaxError");
23
23
  }
24
24
  const scope = this.getScopeForNode(varDecl);
25
25
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
@@ -53,7 +53,7 @@ export function visitVariableDeclaration(node, context) {
53
53
  (ts.isFunctionExpression(initExpr) && !initExpr.name)) {
54
54
  const initContext = {
55
55
  ...context,
56
- lambdaName: name, // Use the variable name as function name
56
+ functionName: name, // Use the variable name as function name
57
57
  };
58
58
  const nativeName = this.generateUniqueName(`__${name}_native_`, context.localScopeSymbols, context.globalScopeSymbols);
59
59
  const scopeNode = ts.isVariableDeclarationList(varDecl.parent)
@@ -92,16 +92,28 @@ export function visitVariableDeclaration(node, context) {
92
92
  return nativeLambdaCode;
93
93
  }
94
94
  }
95
- initializer = " = " + initText;
95
+ initializer = initText;
96
96
  }
97
97
  const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
98
98
  const shouldDeref = context.derefBeforeAssignment &&
99
99
  (!context.localScopeSymbols.has(name));
100
- const assignmentTarget = shouldDeref
100
+ let assignmentTarget = shouldDeref
101
101
  ? this.getDerefCode(name, name, context, typeInfo)
102
102
  : (typeInfo?.needsHeapAllocation && !shouldSkipDeref
103
103
  ? `*${name}`
104
104
  : name);
105
+ const sym = context.localScopeSymbols.get(name) ||
106
+ context.globalScopeSymbols.get(name);
107
+ if (sym?.checks.skippedHoisting) {
108
+ assignmentTarget = typeInfo?.needsHeapAllocation && !shouldSkipDeref
109
+ ? `auto ${name}`
110
+ : `jspp::AnyValue ${name}`;
111
+ if (initializer) {
112
+ initializer = typeInfo?.needsHeapAllocation && !shouldSkipDeref
113
+ ? `std::make_shared<jspp::AnyValue>(${initializer})`
114
+ : initializer;
115
+ }
116
+ }
105
117
  if (nativeLambdaCode)
106
118
  nativeLambdaCode += `;\n${this.indent()}`;
107
119
  if (isLetOrConst) {
@@ -116,19 +128,19 @@ export function visitVariableDeclaration(node, context) {
116
128
  return `${nativeLambdaCode}${assignmentTarget} = jspp::Constants::UNDEFINED`;
117
129
  }
118
130
  }
119
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
131
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
120
132
  }
121
133
  // For 'var', it's a bit more complex.
122
134
  if (context.isAssignmentOnly) {
123
135
  if (!initializer)
124
136
  return "";
125
- return `${nativeLambdaCode}${assignmentTarget}${initializer}`;
137
+ return `${nativeLambdaCode}${assignmentTarget} = ${initializer}`;
126
138
  }
127
139
  else {
128
140
  // This case should not be hit with the new hoisting logic,
129
141
  // but is kept for safety.
130
142
  const initValue = initializer
131
- ? initializer.substring(3)
143
+ ? initializer
132
144
  : "jspp::Constants::UNDEFINED";
133
145
  if (typeInfo?.needsHeapAllocation) {
134
146
  return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import { RESERVED_VAR_NAMES_FOR_STRICT_MODE } from "../../analysis/scope.js";
2
+ import { RESERVED_VAR_NAMES } from "../../analysis/scope.js";
3
3
  import { CompilerError } from "../error.js";
4
4
  import { visitObjectPropertyName } from "./expression-handlers.js";
5
5
  import { CodeGenerator } from "./index.js";
@@ -13,8 +13,8 @@ export function generateDestructuring(lhs, rhsCode, context) {
13
13
  const genAssignment = (pattern, valueCode) => {
14
14
  if (ts.isIdentifier(pattern)) {
15
15
  const name = pattern.text;
16
- if (RESERVED_VAR_NAMES_FOR_STRICT_MODE.has(name)) {
17
- throw new CompilerError(`Cannot destructure to a variable named '${name}' in strict mode.`, pattern, "SyntaxError");
16
+ if (RESERVED_VAR_NAMES.has(name)) {
17
+ throw new CompilerError(`Cannot destructure to a variable named '${name}'.`, pattern, "SyntaxError");
18
18
  }
19
19
  const scope = this.getScopeForNode(pattern);
20
20
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
@@ -247,6 +247,17 @@ export function visitPrefixUnaryExpression(node, context) {
247
247
  const operand = this.visit(prefixUnaryExpr.operand, context);
248
248
  const operator = ts.tokenToString(prefixUnaryExpr.operator);
249
249
  if (operator === "++" || operator === "--") {
250
+ const opFunc = operator === "++" ? "add" : "sub";
251
+ if (ts.isPropertyAccessExpression(prefixUnaryExpr.operand)) {
252
+ const obj = this.visit(prefixUnaryExpr.operand.expression, context);
253
+ const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.name, context);
254
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
255
+ }
256
+ else if (ts.isElementAccessExpression(prefixUnaryExpr.operand)) {
257
+ const obj = this.visit(prefixUnaryExpr.operand.expression, context);
258
+ const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
259
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
260
+ }
250
261
  let target = operand;
251
262
  if (ts.isIdentifier(prefixUnaryExpr.operand)) {
252
263
  const scope = this.getScopeForNode(prefixUnaryExpr.operand);
@@ -289,6 +300,20 @@ export function visitPostfixUnaryExpression(node, context) {
289
300
  const postfixUnaryExpr = node;
290
301
  const operand = this.visit(postfixUnaryExpr.operand, context);
291
302
  const operator = ts.tokenToString(postfixUnaryExpr.operator);
303
+ if (ts.isPropertyAccessExpression(postfixUnaryExpr.operand)) {
304
+ const obj = this.visit(postfixUnaryExpr.operand.expression, context);
305
+ const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.name, context);
306
+ const opFunc = operator === "++" ? "add" : "sub";
307
+ // Postfix needs IILE to return old value
308
+ return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
309
+ }
310
+ else if (ts.isElementAccessExpression(postfixUnaryExpr.operand)) {
311
+ const obj = this.visit(postfixUnaryExpr.operand.expression, context);
312
+ const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
313
+ const opFunc = operator === "++" ? "add" : "sub";
314
+ // Postfix needs IILE to return old value
315
+ return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
316
+ }
292
317
  let target = operand;
293
318
  if (ts.isIdentifier(postfixUnaryExpr.operand)) {
294
319
  const scope = this.getScopeForNode(postfixUnaryExpr.operand);
@@ -402,42 +427,36 @@ export function visitBinaryExpression(node, context) {
402
427
  if (assignmentOperators.includes(opToken.kind)) {
403
428
  if (opToken.kind ===
404
429
  ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
405
- const leftText = this.visit(binExpr.left, visitContext);
406
430
  const rightText = this.visit(binExpr.right, visitContext);
407
- let target = leftText;
408
431
  if (ts.isIdentifier(binExpr.left)) {
432
+ const leftText = this.visit(binExpr.left, visitContext);
409
433
  const scope = this.getScopeForNode(binExpr.left);
410
434
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
411
- target = typeInfo?.needsHeapAllocation
435
+ const target = typeInfo?.needsHeapAllocation
412
436
  ? `*${leftText}`
413
437
  : leftText;
414
- return `${target} = jspp::unsigned_right_shift(${target}, ${rightText})`;
438
+ return `jspp::unsigned_right_shift_assign(${target}, ${rightText})`;
415
439
  }
416
- }
417
- if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
418
- const leftText = this.visit(binExpr.left, visitContext);
419
- const rightText = this.visit(binExpr.right, visitContext);
420
- let target = leftText;
421
- if (ts.isIdentifier(binExpr.left)) {
422
- const scope = this.getScopeForNode(binExpr.left);
423
- const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
424
- target = typeInfo?.needsHeapAllocation
425
- ? `*${leftText}`
426
- : leftText;
427
- return `${target} = jspp::pow(${target}, ${rightText})`;
440
+ else if (ts.isPropertyAccessExpression(binExpr.left)) {
441
+ const obj = this.visit(binExpr.left.expression, context);
442
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
443
+ return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
444
+ }
445
+ else if (ts.isElementAccessExpression(binExpr.left)) {
446
+ const obj = this.visit(binExpr.left.expression, context);
447
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
448
+ return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
428
449
  }
429
- // For complex LHS, we need a different approach, but this is a start.
430
450
  }
431
451
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
432
452
  opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
433
453
  opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
434
- const leftText = this.visit(binExpr.left, visitContext);
435
454
  const rightText = this.visit(binExpr.right, visitContext);
436
- let target = leftText;
437
455
  if (ts.isIdentifier(binExpr.left)) {
456
+ const leftText = this.visit(binExpr.left, visitContext);
438
457
  const scope = this.getScopeForNode(binExpr.left);
439
458
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
440
- target = typeInfo?.needsHeapAllocation
459
+ const target = typeInfo?.needsHeapAllocation
441
460
  ? `*${leftText}`
442
461
  : leftText;
443
462
  if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
@@ -450,6 +469,29 @@ export function visitBinaryExpression(node, context) {
450
469
  return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
451
470
  }
452
471
  }
472
+ else if (ts.isPropertyAccessExpression(binExpr.left)) {
473
+ const obj = this.visit(binExpr.left.expression, context);
474
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
475
+ const func = opToken.kind ===
476
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken
477
+ ? "logical_and"
478
+ : (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
479
+ ? "logical_or"
480
+ : "nullish_coalesce");
481
+ // Logical assignments return newVal, set_own_property returns newVal.
482
+ return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
483
+ }
484
+ else if (ts.isElementAccessExpression(binExpr.left)) {
485
+ const obj = this.visit(binExpr.left.expression, context);
486
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
487
+ const func = opToken.kind ===
488
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken
489
+ ? "logical_and"
490
+ : (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
491
+ ? "logical_or"
492
+ : "nullish_coalesce");
493
+ return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
494
+ }
453
495
  }
454
496
  const leftText = this.visit(binExpr.left, visitContext);
455
497
  let rightText = ts.isNumericLiteral(binExpr.right)
@@ -460,6 +502,35 @@ export function visitBinaryExpression(node, context) {
460
502
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
461
503
  rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
462
504
  }
505
+ if (ts.isPropertyAccessExpression(binExpr.left)) {
506
+ const obj = this.visit(binExpr.left.expression, context);
507
+ const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
508
+ const opBase = op.slice(0, -1); // "+=", "-=" -> "+", "-"
509
+ const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
510
+ ? "right_shift"
511
+ : "")))))))));
512
+ if (opFunc) {
513
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
514
+ }
515
+ else {
516
+ // Fallback to IILE if we don't have an opFunc mapping
517
+ return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
518
+ }
519
+ }
520
+ else if (ts.isElementAccessExpression(binExpr.left)) {
521
+ const obj = this.visit(binExpr.left.expression, context);
522
+ const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
523
+ const opBase = op.slice(0, -1);
524
+ const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
525
+ ? "right_shift"
526
+ : "")))))))));
527
+ if (opFunc) {
528
+ return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
529
+ }
530
+ else {
531
+ return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
532
+ }
533
+ }
463
534
  let target = leftText;
464
535
  if (ts.isIdentifier(binExpr.left)) {
465
536
  const scope = this.getScopeForNode(binExpr.left);
@@ -607,7 +678,8 @@ export function visitBinaryExpression(node, context) {
607
678
  finalLeft = this.getDerefCode(leftText, this.getJsVarName(binExpr.left), visitContext, typeInfo);
608
679
  }
609
680
  // Number optimizations
610
- if (typeInfo && typeInfo.type === "number") {
681
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.left);
682
+ if (nodeType === "number") {
611
683
  finalLeft = `${finalLeft}.as_double()`;
612
684
  }
613
685
  }
@@ -623,7 +695,8 @@ export function visitBinaryExpression(node, context) {
623
695
  finalRight = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
624
696
  }
625
697
  // Number optimizations
626
- if (typeInfo && typeInfo.type === "number") {
698
+ const nodeType = this.typeAnalyzer.inferNodeReturnType(binExpr.right);
699
+ if (nodeType === "number") {
627
700
  finalRight = `${finalRight}.as_double()`;
628
701
  }
629
702
  }
@@ -642,101 +715,82 @@ export function visitBinaryExpression(node, context) {
642
715
  if (opToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
643
716
  return `jspp::nullish_coalesce(${finalLeft}, ${finalRight})`;
644
717
  }
645
- const isLiteral = (n) => ts.isNumericLiteral(n);
646
- const supportsNativeBoolean = ts.isIfStatement(node.parent) ||
647
- ts.isConditionalExpression(node.parent);
648
718
  // Native values for lhs and rhs
649
- const literalLeft = isLiteral(binExpr.left)
719
+ const literalLeft = ts.isNumericLiteral(binExpr.left)
650
720
  ? binExpr.left.getText()
651
721
  : finalLeft;
652
- const literalRight = isLiteral(binExpr.right)
722
+ const literalRight = ts.isNumericLiteral(binExpr.right)
653
723
  ? binExpr.right.getText()
654
724
  : finalRight;
655
- // Operations that returns boolean should return the native boolean if supported
656
- if (constants.booleanOperators.includes(opToken.kind) &&
657
- supportsNativeBoolean) {
658
- if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
659
- return `jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
660
- }
661
- if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
662
- return `jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
663
- }
664
- if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
665
- return `!jspp::is_strictly_equal_to_primitive(${literalLeft}, ${literalRight})`;
666
- }
667
- if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
668
- return `!jspp::is_equal_to_primitive(${literalLeft}, ${literalRight})`;
669
- }
670
- let funcName = "";
671
- if (opToken.kind === ts.SyntaxKind.LessThanToken) {
672
- funcName = "jspp::less_than_primitive";
673
- }
674
- if (opToken.kind === ts.SyntaxKind.LessThanEqualsToken) {
675
- funcName = "jspp::less_than_or_equal_primitive";
676
- }
677
- if (opToken.kind === ts.SyntaxKind.GreaterThanToken) {
678
- funcName = "jspp::greater_than_primitive";
679
- }
680
- if (opToken.kind === ts.SyntaxKind.GreaterThanEqualsToken) {
681
- funcName = "jspp::greater_than_or_equal_primitive";
725
+ let supportsNativeValue = false;
726
+ const exprReturnType = this.typeAnalyzer.inferNodeReturnType(node);
727
+ if (exprReturnType === "boolean" &&
728
+ (ts.isIfStatement(node.parent) ||
729
+ ts.isConditionalExpression(node.parent))) {
730
+ supportsNativeValue = true;
731
+ }
732
+ else if (exprReturnType === "number" &&
733
+ context.isInsideNativeLambda &&
734
+ context.isInsideFunction) {
735
+ const funcDecl = this
736
+ .findEnclosingFunctionDeclarationFromReturnStatement(node);
737
+ if (funcDecl) {
738
+ const funcReturnType = this.typeAnalyzer.inferFunctionReturnType(funcDecl);
739
+ if (funcReturnType === "number") {
740
+ supportsNativeValue = true;
741
+ }
682
742
  }
683
- // For C++ primitive literals, standard operators are fine if they map directly,
684
- // but we are safe using our functions (which handle doubles correctly).
685
- // Actually, for pure numeric literals like "1 < 2", we can leave it as is if we want optimization,
686
- // but consistency is safer.
687
- // Let's stick to valid C++ syntax for literals if possible to avoid overhead?
688
- // jspp::less_than(1, 2) works.
689
- return `${funcName}(${literalLeft}, ${literalRight})`;
690
743
  }
744
+ const method = supportsNativeValue ? "_native" : "";
691
745
  // Return boxed value
692
746
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
693
- return `jspp::is_strictly_equal_to(${literalLeft}, ${literalRight})`;
747
+ return `jspp::is_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
694
748
  }
695
749
  if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
696
- return `jspp::is_equal_to(${literalLeft}, ${literalRight})`;
750
+ return `jspp::is_equal_to${method}(${literalLeft}, ${literalRight})`;
697
751
  }
698
752
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
699
- return `jspp::not_strictly_equal_to(${literalLeft}, ${literalRight})`;
753
+ return `jspp::not_strictly_equal_to${method}(${literalLeft}, ${literalRight})`;
700
754
  }
701
755
  if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
702
- return `jspp::not_equal_to(${literalLeft}, ${literalRight})`;
756
+ return `jspp::not_equal_to${method}(${literalLeft}, ${literalRight})`;
703
757
  }
704
758
  if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
705
- return `jspp::pow(${literalLeft}, ${literalRight})`;
759
+ return `jspp::pow${method}(${literalLeft}, ${literalRight})`;
706
760
  }
707
761
  if (opToken.kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) {
708
- return `jspp::unsigned_right_shift(${literalLeft}, ${literalRight})`;
762
+ return `jspp::unsigned_right_shift${method}(${literalLeft}, ${literalRight})`;
709
763
  }
710
764
  // For other arithmetic and bitwise operations, use native operations if possible
711
765
  switch (op) {
712
766
  case "+":
713
- return `jspp::add(${literalLeft}, ${literalRight})`;
767
+ return `jspp::add${method}(${literalLeft}, ${literalRight})`;
714
768
  case "-":
715
- return `jspp::sub(${literalLeft}, ${literalRight})`;
769
+ return `jspp::sub${method}(${literalLeft}, ${literalRight})`;
716
770
  case "*":
717
- return `jspp::mul(${literalLeft}, ${literalRight})`;
771
+ return `jspp::mul${method}(${literalLeft}, ${literalRight})`;
718
772
  case "/":
719
- return `jspp::div(${literalLeft}, ${literalRight})`;
773
+ return `jspp::div${method}(${literalLeft}, ${literalRight})`;
720
774
  case "%":
721
- return `jspp::mod(${literalLeft}, ${literalRight})`;
775
+ return `jspp::mod${method}(${literalLeft}, ${literalRight})`;
722
776
  case "^":
723
- return `jspp::bitwise_xor(${literalLeft}, ${literalRight})`;
777
+ return `jspp::bitwise_xor${method}(${literalLeft}, ${literalRight})`;
724
778
  case "&":
725
- return `jspp::bitwise_and(${literalLeft}, ${literalRight})`;
779
+ return `jspp::bitwise_and${method}(${literalLeft}, ${literalRight})`;
726
780
  case "|":
727
- return `jspp::bitwise_or(${literalLeft}, ${literalRight})`;
781
+ return `jspp::bitwise_or${method}(${literalLeft}, ${literalRight})`;
728
782
  case "<<":
729
- return `jspp::left_shift(${literalLeft}, ${literalRight})`;
783
+ return `jspp::left_shift${method}(${literalLeft}, ${literalRight})`;
730
784
  case ">>":
731
- return `jspp::right_shift(${literalLeft}, ${literalRight})`;
785
+ return `jspp::right_shift${method}(${literalLeft}, ${literalRight})`;
732
786
  case "<":
733
- return `jspp::less_than(${literalLeft}, ${literalRight})`;
787
+ return `jspp::less_than${method}(${literalLeft}, ${literalRight})`;
734
788
  case ">":
735
- return `jspp::greater_than(${literalLeft}, ${literalRight})`;
789
+ return `jspp::greater_than${method}(${literalLeft}, ${literalRight})`;
736
790
  case "<=":
737
- return `jspp::less_than_or_equal(${literalLeft}, ${literalRight})`;
791
+ return `jspp::less_than_or_equal${method}(${literalLeft}, ${literalRight})`;
738
792
  case ">=":
739
- return `jspp::greater_than_or_equal(${literalLeft}, ${literalRight})`;
793
+ return `jspp::greater_than_or_equal${method}(${literalLeft}, ${literalRight})`;
740
794
  }
741
795
  return `/* Unhandled Operator: ${finalLeft} ${op} ${finalRight} */`; // Default fallback
742
796
  }
@@ -1283,7 +1337,7 @@ export function visitTypeOfExpression(node, context) {
1283
1337
  derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
1284
1338
  }
1285
1339
  }
1286
- return `jspp::Access::type_of(${derefExpr})`;
1340
+ return `jspp::type_of(${derefExpr})`;
1287
1341
  }
1288
1342
  export function visitAwaitExpression(node, context) {
1289
1343
  const awaitExpr = node;