@ugo-studio/jspp 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/analysis/scope.js +17 -0
  2. package/dist/analysis/typeAnalyzer.js +7 -1
  3. package/dist/ast/symbols.js +29 -0
  4. package/dist/ast/types.js +0 -6
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -30
  10. package/dist/core/codegen/class-handlers.js +10 -6
  11. package/dist/core/codegen/control-flow-handlers.js +31 -21
  12. package/dist/core/codegen/declaration-handlers.js +10 -6
  13. package/dist/core/codegen/expression-handlers.js +202 -60
  14. package/dist/core/codegen/function-handlers.js +179 -70
  15. package/dist/core/codegen/helpers.js +107 -17
  16. package/dist/core/codegen/index.js +9 -8
  17. package/dist/core/codegen/literal-handlers.js +15 -6
  18. package/dist/core/codegen/statement-handlers.js +67 -53
  19. package/dist/core/codegen/visitor.js +3 -1
  20. package/package.json +1 -1
  21. package/src/prelude/any_value.hpp +195 -342
  22. package/src/prelude/any_value_access.hpp +78 -30
  23. package/src/prelude/any_value_defines.hpp +74 -35
  24. package/src/prelude/any_value_helpers.hpp +73 -180
  25. package/src/prelude/exception.hpp +1 -0
  26. package/src/prelude/exception_helpers.hpp +4 -4
  27. package/src/prelude/index.hpp +9 -2
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +6 -5
  30. package/src/prelude/library/error.hpp +10 -8
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +20 -0
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +1 -1
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +53 -43
  38. package/src/prelude/library/symbol.hpp +45 -57
  39. package/src/prelude/library/timer.hpp +6 -6
  40. package/src/prelude/types.hpp +48 -0
  41. package/src/prelude/utils/access.hpp +182 -11
  42. package/src/prelude/utils/assignment_operators.hpp +99 -0
  43. package/src/prelude/utils/log_any_value/array.hpp +8 -8
  44. package/src/prelude/utils/log_any_value/function.hpp +6 -4
  45. package/src/prelude/utils/log_any_value/object.hpp +41 -24
  46. package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
  47. package/src/prelude/utils/operators.hpp +750 -274
  48. package/src/prelude/utils/well_known_symbols.hpp +12 -0
  49. package/src/prelude/values/array.hpp +8 -6
  50. package/src/prelude/values/descriptors.hpp +2 -2
  51. package/src/prelude/values/function.hpp +71 -62
  52. package/src/prelude/values/helpers/array.hpp +64 -28
  53. package/src/prelude/values/helpers/function.hpp +77 -92
  54. package/src/prelude/values/helpers/iterator.hpp +3 -3
  55. package/src/prelude/values/helpers/object.hpp +54 -9
  56. package/src/prelude/values/helpers/promise.hpp +3 -3
  57. package/src/prelude/values/iterator.hpp +1 -1
  58. package/src/prelude/values/object.hpp +10 -3
  59. package/src/prelude/values/promise.hpp +3 -3
  60. package/src/prelude/values/prototypes/array.hpp +851 -12
  61. package/src/prelude/values/prototypes/function.hpp +2 -2
  62. package/src/prelude/values/prototypes/iterator.hpp +5 -5
  63. package/src/prelude/values/prototypes/number.hpp +153 -0
  64. package/src/prelude/values/prototypes/object.hpp +2 -2
  65. package/src/prelude/values/prototypes/promise.hpp +40 -30
  66. package/src/prelude/values/prototypes/string.hpp +28 -28
  67. package/src/prelude/values/prototypes/symbol.hpp +20 -3
  68. package/src/prelude/values/shape.hpp +52 -0
@@ -14,12 +14,17 @@ export const BUILTIN_OBJECTS = new Set([
14
14
  { name: "performance", isConst: false },
15
15
  { name: "global", isConst: false },
16
16
  { name: "globalThis", isConst: false },
17
+ { name: "process", isConst: false },
17
18
  { name: "Error", isConst: false },
18
19
  { name: "Promise", isConst: false },
20
+ { name: "Function", isConst: false },
19
21
  { name: "setTimeout", isConst: false },
20
22
  { name: "clearTimeout", isConst: false },
21
23
  { name: "setInterval", isConst: false },
22
24
  { name: "clearInterval", isConst: false },
25
+ { name: "Array", isConst: false },
26
+ { name: "Object", isConst: false },
27
+ { name: "Math", isConst: false },
23
28
  ]);
24
29
  // Represents a single scope (e.g., a function body or a block statement)
25
30
  export class Scope {
@@ -79,11 +84,23 @@ export class ScopeManager {
79
84
  }
80
85
  // Defines a variable in the current scope.
81
86
  define(name, type) {
87
+ // if (name === "named" || name === "letVal") console.log("Defining", name, "in scope. isBuiltin:", type.isBuiltin, " type:", type.type);
82
88
  if (this.reservedKeywords.has(name) && !type.isBuiltin) {
83
89
  throw new Error(`SyntaxError: Unexpected reserved word "${name}"`);
84
90
  }
85
91
  this.currentScope.define(name, type);
86
92
  }
93
+ // Defines a `var` variable (hoisted to function or global scope).
94
+ defineVar(name, type) {
95
+ if (this.reservedKeywords.has(name) && !type.isBuiltin) {
96
+ throw new Error(`SyntaxError: Unexpected reserved word "${name}"`);
97
+ }
98
+ let scope = this.currentScope;
99
+ while (scope.parent && scope.ownerFunction === scope.parent.ownerFunction) {
100
+ scope = scope.parent;
101
+ }
102
+ scope.define(name, type);
103
+ }
87
104
  // Looks up a variable's type information from the current scope upwards.
88
105
  lookup(name) {
89
106
  const scope = this.currentScope.findScopeFor(name);
@@ -362,6 +362,7 @@ export class TypeAnalyzer {
362
362
  enter: (node) => {
363
363
  if (ts.isVariableDeclaration(node)) {
364
364
  const name = node.name.getText();
365
+ const isBlockScoped = (node.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
365
366
  const isConst = (node.parent.flags & ts.NodeFlags.Const) !== 0;
366
367
  let type = "auto";
367
368
  let needsHeap = false;
@@ -381,7 +382,12 @@ export class TypeAnalyzer {
381
382
  isConst,
382
383
  needsHeapAllocation: needsHeap,
383
384
  };
384
- this.scopeManager.define(name, typeInfo);
385
+ if (isBlockScoped) {
386
+ this.scopeManager.define(name, typeInfo);
387
+ }
388
+ else {
389
+ this.scopeManager.defineVar(name, typeInfo);
390
+ }
385
391
  }
386
392
  },
387
393
  },
@@ -0,0 +1,29 @@
1
+ export var DeclaredSymbolType;
2
+ (function (DeclaredSymbolType) {
3
+ DeclaredSymbolType["letOrConst"] = "letOrConst";
4
+ DeclaredSymbolType["function"] = "function";
5
+ DeclaredSymbolType["var"] = "var";
6
+ })(DeclaredSymbolType || (DeclaredSymbolType = {}));
7
+ export class DeclaredSymbols {
8
+ symbols;
9
+ constructor(...m) {
10
+ this.symbols = new Map();
11
+ m.forEach((ds) => ds.symbols.forEach((v, k) => this.symbols.set(k, v)));
12
+ }
13
+ has(name) {
14
+ return this.symbols.has(name);
15
+ }
16
+ get(name) {
17
+ return this.symbols.get(name);
18
+ }
19
+ set(name, value) {
20
+ return this.symbols.set(name, value);
21
+ }
22
+ update(name, update) {
23
+ const oldValue = this.get(name);
24
+ if (oldValue) {
25
+ const newValue = { ...oldValue, ...update };
26
+ return this.symbols.set(name, newValue);
27
+ }
28
+ }
29
+ }
package/dist/ast/types.js CHANGED
@@ -1,7 +1 @@
1
1
  import * as ts from "typescript";
2
- export var DeclaredSymbolType;
3
- (function (DeclaredSymbolType) {
4
- DeclaredSymbolType["letOrConst"] = "letOrConst";
5
- DeclaredSymbolType["function"] = "function";
6
- DeclaredSymbolType["var"] = "var";
7
- })(DeclaredSymbolType || (DeclaredSymbolType = {}));
@@ -0,0 +1,57 @@
1
+ import path from "path";
2
+ import { COLORS } from "./colors";
3
+ export function parseArgs(rawArgs) {
4
+ let jsFilePathArg = null;
5
+ let isRelease = false;
6
+ let keepCpp = false;
7
+ let outputExePath = null;
8
+ let scriptArgs = [];
9
+ for (let i = 0; i < rawArgs.length; i++) {
10
+ const arg = rawArgs[i];
11
+ if (!arg)
12
+ continue;
13
+ if (arg === "--") {
14
+ scriptArgs = rawArgs.slice(i + 1);
15
+ break;
16
+ }
17
+ if (arg === "--release") {
18
+ isRelease = true;
19
+ }
20
+ else if (arg === "--keep-cpp") {
21
+ keepCpp = true;
22
+ }
23
+ else if (arg === "-o" || arg === "--output") {
24
+ if (i + 1 < rawArgs.length) {
25
+ outputExePath = rawArgs[i + 1] ?? null;
26
+ i++;
27
+ }
28
+ else {
29
+ console.error(`${COLORS.red}Error: --output requires a file path argument.${COLORS.reset}`);
30
+ process.exit(1);
31
+ }
32
+ }
33
+ else if (arg.startsWith("-")) {
34
+ console.warn(`${COLORS.yellow}Warning: Unknown argument '${arg}'${COLORS.reset}`);
35
+ }
36
+ else {
37
+ if (jsFilePathArg) {
38
+ console.error(`${COLORS.red}Error: Multiple input files specified.${COLORS.reset}`);
39
+ process.exit(1);
40
+ }
41
+ jsFilePathArg = arg;
42
+ }
43
+ }
44
+ if (!jsFilePathArg) {
45
+ console.log(`${COLORS.bold}Usage:${COLORS.reset} jspp <path-to-js-file> [--release] [--keep-cpp] [-o <output-path>] [-- <args...>]`);
46
+ process.exit(1);
47
+ }
48
+ return {
49
+ jsFilePath: path.resolve(process.cwd(), jsFilePathArg),
50
+ isRelease,
51
+ keepCpp,
52
+ outputExePath: outputExePath
53
+ ? path.resolve(process.cwd(), outputExePath)
54
+ : null,
55
+ scriptArgs,
56
+ };
57
+ }
@@ -0,0 +1,9 @@
1
+ export const COLORS = {
2
+ reset: "\x1b[0m",
3
+ cyan: "\x1b[36m",
4
+ green: "\x1b[32m",
5
+ yellow: "\x1b[33m",
6
+ red: "\x1b[31m",
7
+ dim: "\x1b[2m",
8
+ bold: "\x1b[1m",
9
+ };
@@ -0,0 +1,20 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ export async function getLatestMtime(dirPath) {
4
+ let maxMtime = 0;
5
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
6
+ for (const entry of entries) {
7
+ const fullPath = path.join(dirPath, entry.name);
8
+ if (entry.isDirectory()) {
9
+ const nestedMtime = await getLatestMtime(fullPath);
10
+ if (nestedMtime > maxMtime)
11
+ maxMtime = nestedMtime;
12
+ }
13
+ else {
14
+ const stats = await fs.stat(fullPath);
15
+ if (stats.mtimeMs > maxMtime)
16
+ maxMtime = stats.mtimeMs;
17
+ }
18
+ }
19
+ return maxMtime;
20
+ }
@@ -0,0 +1,55 @@
1
+ import { COLORS } from "./colors";
2
+ export class Spinner {
3
+ frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ interval = null;
5
+ frameIndex = 0;
6
+ text;
7
+ constructor(text) {
8
+ this.text = text;
9
+ }
10
+ start() {
11
+ process.stdout.write("\x1b[?25l"); // Hide cursor
12
+ this.frameIndex = 0;
13
+ this.render();
14
+ this.interval = setInterval(() => {
15
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
16
+ this.render();
17
+ }, 80);
18
+ }
19
+ update(text) {
20
+ this.text = text;
21
+ this.render();
22
+ }
23
+ stop(symbol = "", color = COLORS.reset) {
24
+ if (this.interval) {
25
+ clearInterval(this.interval);
26
+ this.interval = null;
27
+ }
28
+ this.clearLine();
29
+ process.stdout.write(`${color}${symbol} ${COLORS.reset} ${this.text}\n`);
30
+ process.stdout.write("\x1b[?25h"); // Show cursor
31
+ }
32
+ succeed(text) {
33
+ if (text)
34
+ this.text = text;
35
+ this.stop("✔", COLORS.green);
36
+ }
37
+ fail(text) {
38
+ if (text)
39
+ this.text = text;
40
+ this.stop("✖", COLORS.red);
41
+ }
42
+ info(text) {
43
+ if (text)
44
+ this.text = text;
45
+ this.stop("ℹ", COLORS.cyan);
46
+ }
47
+ render() {
48
+ this.clearLine();
49
+ const frame = this.frames[this.frameIndex];
50
+ process.stdout.write(`${COLORS.cyan}${frame} ${COLORS.reset} ${this.text}`);
51
+ }
52
+ clearLine() {
53
+ process.stdout.write("\r\x1b[K");
54
+ }
55
+ }
package/dist/cli.js CHANGED
@@ -1,63 +1,138 @@
1
1
  #!/usr/bin/env bun
2
2
  import fs from "fs/promises";
3
3
  import path from "path";
4
+ import pkg from "../package.json";
5
+ import { parseArgs } from "./cli-utils/args";
6
+ import { COLORS } from "./cli-utils/colors";
7
+ import { getLatestMtime } from "./cli-utils/file-utils";
8
+ import { Spinner } from "./cli-utils/spinner";
4
9
  import { Interpreter } from "./index";
5
10
  async function main() {
6
- const args = process.argv.slice(2);
7
- if (args.length === 0) {
8
- console.log("Usage: jspp <path-to-js-file>");
9
- process.exit(1);
10
- }
11
- const jsFilePath = path.resolve(process.cwd(), args[0]);
11
+ const { jsFilePath, isRelease, keepCpp, outputExePath, scriptArgs } = parseArgs(process.argv.slice(2));
12
12
  const jsFileName = path.basename(jsFilePath, ".js");
13
- const outputDir = path.dirname(jsFilePath);
14
- const cppFilePath = path.join(outputDir, `${jsFileName}.cpp`);
15
- const exeFilePath = path.join(outputDir, `${jsFileName}.exe`);
13
+ const sourceDir = path.dirname(jsFilePath);
14
+ // Intermediate C++ file goes alongside the source JS file
15
+ const cppFilePath = path.join(sourceDir, `${jsFileName}.cpp`);
16
+ // Determine output executable path
17
+ let exeFilePath;
18
+ if (outputExePath) {
19
+ exeFilePath = outputExePath;
20
+ }
21
+ else {
22
+ const ext = process.platform === "win32" ? ".exe" : "";
23
+ exeFilePath = path.join(sourceDir, `${jsFileName}${ext}`);
24
+ }
25
+ // Mode Configuration
26
+ const mode = isRelease ? "release" : "debug";
27
+ console.log(`${COLORS.bold}JSPP Compiler${COLORS.reset} ${COLORS.dim}v${pkg.version}${COLORS.reset}`);
28
+ console.log(`Mode: ${isRelease ? COLORS.green : COLORS.yellow}${mode.toUpperCase()}${COLORS.reset}\n`);
29
+ const flags = isRelease
30
+ ? ["-O3", "-DNDEBUG", "-Wa,-mbig-obj"]
31
+ : ["-O0", "-Wa,-mbig-obj"];
32
+ const pchDir = path.resolve(process.cwd(), "prelude-build", mode);
33
+ const spinner = new Spinner("Initializing...");
16
34
  try {
35
+ spinner.start();
36
+ // 1. Interpreter Phase
37
+ spinner.update(`Reading ${path.basename(jsFilePath)}...`);
17
38
  const jsCode = await fs.readFile(jsFilePath, "utf-8");
39
+ spinner.update("Transpiling to C++...");
18
40
  const interpreter = new Interpreter();
19
41
  const { cppCode, preludePath } = interpreter.interpret(jsCode);
20
- console.log(`Generated C++ code ${cppFilePath}...`);
21
- await fs.mkdir(outputDir, { recursive: true });
42
+ // Ensure directory for cpp file exists (should exist as it's source dir, but for safety if we change logic)
43
+ await fs.mkdir(path.dirname(cppFilePath), { recursive: true });
22
44
  await fs.writeFile(cppFilePath, cppCode);
23
- console.log(`Compiling ${cppFilePath}...`);
24
- const compile = Bun.spawnSync({
45
+ spinner.succeed(`Generated ${COLORS.dim}${path.basename(cppFilePath)}${COLORS.reset}`);
46
+ // 2. Precompiled Header Check
47
+ spinner.text = "Checking precompiled headers...";
48
+ spinner.start();
49
+ const pchFile = path.join(pchDir, "index.hpp.gch");
50
+ let shouldRebuild = false;
51
+ try {
52
+ const pchStats = await fs.stat(pchFile);
53
+ const sourceMtime = await getLatestMtime(preludePath);
54
+ if (sourceMtime > pchStats.mtimeMs) {
55
+ shouldRebuild = true;
56
+ }
57
+ }
58
+ catch (e) {
59
+ shouldRebuild = true;
60
+ }
61
+ if (shouldRebuild) {
62
+ spinner.update("Rebuilding precompiled headers (this may take a while)...");
63
+ // Use spawn (async) instead of spawnSync to keep spinner alive
64
+ const rebuild = Bun.spawn({
65
+ cmd: ["bun", "run", "scripts/precompile-headers.ts"],
66
+ stdout: "pipe", // pipe to hide output unless error, or handle differently
67
+ stderr: "pipe",
68
+ });
69
+ const exitCode = await rebuild.exited;
70
+ if (exitCode !== 0) {
71
+ const stderr = await new Response(rebuild.stderr).text();
72
+ spinner.fail("Failed to rebuild precompiled headers");
73
+ console.error(stderr);
74
+ process.exit(1);
75
+ }
76
+ spinner.succeed("Precompiled headers updated");
77
+ }
78
+ else {
79
+ spinner.succeed("Precompiled headers up-to-date");
80
+ }
81
+ // 3. Compilation Phase
82
+ spinner.text = `Compiling binary...`;
83
+ spinner.start();
84
+ // Ensure output directory exists
85
+ await fs.mkdir(path.dirname(exeFilePath), { recursive: true });
86
+ const compile = Bun.spawn({
25
87
  cmd: [
26
88
  "g++",
27
89
  "-std=c++23",
90
+ ...flags,
28
91
  cppFilePath,
29
92
  "-o",
30
93
  exeFilePath,
31
94
  "-I",
95
+ pchDir,
96
+ "-I",
32
97
  preludePath,
33
- "-O3",
34
- "-DNDEBUG",
35
- // "-include",
36
- // path.join(process.cwd(), "prelude-build", "index.hpp"),
37
98
  ],
38
- stdout: "inherit",
39
- stderr: "inherit",
99
+ stdout: "pipe",
100
+ stderr: "pipe",
40
101
  });
41
- if (compile.exitCode !== 0) {
42
- console.error(`Compilation failed for ${cppFilePath}`);
102
+ const compileExitCode = await compile.exited;
103
+ if (compileExitCode !== 0) {
104
+ const stderr = await new Response(compile.stderr).text();
105
+ spinner.fail(`Compilation failed`);
106
+ console.error(stderr);
43
107
  process.exit(1);
44
108
  }
45
- console.log(`Running ${exeFilePath}...`);
46
- console.log("\x1b[32m\n------start------\x1b[0m");
47
- const run = Bun.spawnSync({
48
- cmd: [exeFilePath],
109
+ spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${path.basename(exeFilePath)}${COLORS.reset}`);
110
+ // Clean up C++ file if not requested to keep
111
+ if (!keepCpp) {
112
+ try {
113
+ await fs.unlink(cppFilePath);
114
+ }
115
+ catch (e) {
116
+ // Ignore error if file cannot be deleted
117
+ }
118
+ }
119
+ // 4. Execution Phase
120
+ console.log(`\n${COLORS.cyan}--- Running Output ---${COLORS.reset}`);
121
+ const run = Bun.spawn({
122
+ cmd: [exeFilePath, ...scriptArgs],
49
123
  stdout: "inherit",
50
124
  stderr: "inherit",
51
125
  });
52
- console.log("\x1b[32m\n------end--------\x1b[0m");
53
- if (run.exitCode !== 0) {
54
- console.error(`Execution failed for ${exeFilePath}`);
126
+ const runExitCode = await run.exited;
127
+ console.log(`${COLORS.cyan}----------------------${COLORS.reset}\n`);
128
+ if (runExitCode !== 0) {
129
+ console.error(`${COLORS.red}Execution failed with exit code ${runExitCode}${COLORS.reset}`);
55
130
  process.exit(1);
56
131
  }
57
- console.log(`Successfully ran ${exeFilePath}`);
58
132
  }
59
133
  catch (error) {
60
- console.error(`Error: ${error.message}`);
134
+ spinner.fail("An unexpected error occurred");
135
+ console.error(error);
61
136
  process.exit(1);
62
137
  }
63
138
  }
@@ -20,7 +20,7 @@ export function visitClassDeclaration(node, context) {
20
20
  const scope = this.getScopeForNode(extendsExpr);
21
21
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(extendsExpr.getText(), scope);
22
22
  if (typeInfo) {
23
- parentName = this.getDerefCode(parentName, this.getJsVarName(extendsExpr), typeInfo);
23
+ parentName = this.getDerefCode(parentName, this.getJsVarName(extendsExpr), context, typeInfo);
24
24
  }
25
25
  }
26
26
  }
@@ -42,16 +42,16 @@ export function visitClassDeclaration(node, context) {
42
42
  // Default constructor
43
43
  if (parentName) {
44
44
  constructorLambda =
45
- `jspp::AnyValue::make_class([=](const jspp::AnyValue& ${this.globalThisVar}, const std::vector<jspp::AnyValue>& args) mutable -> jspp::AnyValue {
45
+ `jspp::AnyValue::make_class([=](const jspp::AnyValue& ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
46
46
  auto __parent = ${parentName};
47
- __parent.as_function("super")->call(${this.globalThisVar}, args);
48
- return jspp::AnyValue::make_undefined();
47
+ __parent.call(${this.globalThisVar}, args, "super");
48
+ return jspp::Constants::UNDEFINED;
49
49
  }, "${className}")`;
50
50
  }
51
51
  else {
52
52
  constructorLambda =
53
- `jspp::AnyValue::make_class([=](const jspp::AnyValue& ${this.globalThisVar}, const std::vector<jspp::AnyValue>& args) mutable -> jspp::AnyValue {
54
- return jspp::AnyValue::make_undefined();
53
+ `jspp::AnyValue::make_class([=](const jspp::AnyValue& ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
54
+ return jspp::Constants::UNDEFINED;
55
55
  }, "${className}")`;
56
56
  }
57
57
  }
@@ -63,6 +63,10 @@ export function visitClassDeclaration(node, context) {
63
63
  code +=
64
64
  `${this.indent()}(*${className}).get_own_property("prototype").set_prototype(${parentName}.get_own_property("prototype"));\n`;
65
65
  }
66
+ else {
67
+ code +=
68
+ `${this.indent()}(*${className}).get_own_property("prototype").set_prototype(::Object.get_own_property("prototype"));\n`;
69
+ }
66
70
  // Members
67
71
  for (const member of node.members) {
68
72
  if (ts.isMethodDeclaration(member)) {
@@ -1,4 +1,5 @@
1
1
  import ts from "typescript";
2
+ import { DeclaredSymbols, DeclaredSymbolType } from "../../ast/symbols";
2
3
  import { CodeGenerator } from "./";
3
4
  export function visitForStatement(node, context) {
4
5
  const forStmt = node;
@@ -10,6 +11,10 @@ export function visitForStatement(node, context) {
10
11
  this.indentationLevel++; // Enter a new scope for the for loop
11
12
  // Handle initializer
12
13
  let initializerCode = "";
14
+ let conditionContext = {
15
+ ...context,
16
+ topLevelScopeSymbols: this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols),
17
+ };
13
18
  if (forStmt.initializer) {
14
19
  if (ts.isVariableDeclarationList(forStmt.initializer)) {
15
20
  const varDeclList = forStmt.initializer;
@@ -22,10 +27,15 @@ export function visitForStatement(node, context) {
22
27
  const name = decl.name.getText();
23
28
  const initValue = decl.initializer
24
29
  ? this.visit(decl.initializer, context)
25
- : "jspp::AnyValue::make_undefined()";
30
+ : "jspp::Constants::UNDEFINED";
26
31
  const scope = this.getScopeForNode(decl);
27
32
  const typeInfo = this.typeAnalyzer.scopeManager
28
33
  .lookupFromScope(name, scope);
34
+ conditionContext.localScopeSymbols = new DeclaredSymbols();
35
+ conditionContext.localScopeSymbols.set(name, {
36
+ type: DeclaredSymbolType.letOrConst,
37
+ checkedIfUninitialized: true,
38
+ });
29
39
  if (typeInfo.needsHeapAllocation) {
30
40
  initializerCode =
31
41
  `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
@@ -49,7 +59,7 @@ export function visitForStatement(node, context) {
49
59
  }
50
60
  code += `${this.indent()}for (${initializerCode}; `;
51
61
  if (forStmt.condition) {
52
- code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
62
+ code += `is_truthy(${this.visit(forStmt.condition, conditionContext)})`;
53
63
  }
54
64
  code += "; ";
55
65
  if (forStmt.incrementor) {
@@ -105,12 +115,12 @@ export function visitForInStatement(node, context) {
105
115
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
106
116
  if (typeInfo.needsHeapAllocation) {
107
117
  code +=
108
- `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
118
+ `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
109
119
  assignmentTarget = `*${varName}`;
110
120
  }
111
121
  else {
112
122
  code +=
113
- `${this.indent()}jspp::AnyValue ${varName} = jspp::AnyValue::make_undefined();\n`;
123
+ `${this.indent()}jspp::AnyValue ${varName} = jspp::Constants::UNDEFINED;\n`;
114
124
  assignmentTarget = varName;
115
125
  }
116
126
  }
@@ -129,7 +139,7 @@ export function visitForInStatement(node, context) {
129
139
  if (ts.isIdentifier(expr)) {
130
140
  const scope = this.getScopeForNode(expr);
131
141
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.getText(), scope);
132
- derefExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
142
+ derefExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
133
143
  }
134
144
  const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
135
145
  code +=
@@ -177,12 +187,12 @@ export function visitForOfStatement(node, context) {
177
187
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
178
188
  if (typeInfo.needsHeapAllocation) {
179
189
  code +=
180
- `${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
190
+ `${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
181
191
  assignmentTarget = `*${elemName}`;
182
192
  }
183
193
  else {
184
194
  code +=
185
- `${this.indent()}jspp::AnyValue ${elemName} = jspp::AnyValue::make_undefined();\n`;
195
+ `${this.indent()}jspp::AnyValue ${elemName} = jspp::Constants::UNDEFINED;\n`;
186
196
  assignmentTarget = elemName;
187
197
  }
188
198
  }
@@ -201,7 +211,7 @@ export function visitForOfStatement(node, context) {
201
211
  const scope = this.getScopeForNode(forOf.expression);
202
212
  const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(forOf.expression.getText(), scope);
203
213
  const varName = this.getJsVarName(forOf.expression);
204
- derefIterable = this.getDerefCode(iterableExpr, varName, typeInfo);
214
+ derefIterable = this.getDerefCode(iterableExpr, varName, context, typeInfo);
205
215
  }
206
216
  const declaredSymbols = this.getDeclaredSymbols(forOf.statement);
207
217
  const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
@@ -213,11 +223,11 @@ export function visitForOfStatement(node, context) {
213
223
  code +=
214
224
  `${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
215
225
  code +=
216
- `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next").as_function();\n`;
226
+ `${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
217
227
  code +=
218
- `${this.indent()}auto ${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
228
+ `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
219
229
  code +=
220
- `${this.indent()}while (!${nextRes}.get_own_property("done").is_truthy()) {\n`;
230
+ `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
221
231
  this.indentationLevel++;
222
232
  code +=
223
233
  `${this.indent()}${assignmentTarget} = ${nextRes}.get_own_property("value");\n`;
@@ -230,7 +240,7 @@ export function visitForOfStatement(node, context) {
230
240
  code += `${this.indent()}${context.currentLabel}_continue:;\n`;
231
241
  }
232
242
  code +=
233
- `${this.indent()}${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
243
+ `${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
234
244
  this.indentationLevel--;
235
245
  code += `${this.indent()}}\n`;
236
246
  this.indentationLevel--; // Exit the scope for the for-of loop
@@ -248,7 +258,7 @@ export function visitWhileStatement(node, context) {
248
258
  const conditionText = condition.kind === ts.SyntaxKind.TrueKeyword ||
249
259
  condition.kind === ts.SyntaxKind.FalseKeyword
250
260
  ? condition.getText()
251
- : `(${this.visit(condition, context)}).is_truthy()`;
261
+ : `is_truthy(${this.visit(condition, context)})`;
252
262
  let code = "";
253
263
  if (context.currentLabel) {
254
264
  code += `${this.indent()}${context.currentLabel}: {\n`;
@@ -288,7 +298,7 @@ export function visitWhileStatement(node, context) {
288
298
  }
289
299
  export function visitDoStatement(node, context) {
290
300
  const condition = node.expression;
291
- const conditionText = `(${this.visit(condition, context)}).is_truthy()`;
301
+ const conditionText = `is_truthy(${this.visit(condition, context)})`;
292
302
  let code = "";
293
303
  if (context.currentLabel) {
294
304
  code += `${this.indent()}${context.currentLabel}: {\n`;
@@ -346,7 +356,7 @@ export function visitSwitchStatement(node, context) {
346
356
  `${this.indent()}const jspp::AnyValue ${switchValueVar} = ${expressionCode};\n`;
347
357
  code += `${this.indent()}bool ${fallthroughVar} = false;\n`;
348
358
  // Hoist variable declarations
349
- const hoistedSymbols = new Map();
359
+ const hoistedSymbols = new DeclaredSymbols();
350
360
  for (const clause of switchStmt.caseBlock.clauses) {
351
361
  if (ts.isCaseClause(clause) || ts.isDefaultClause(clause)) {
352
362
  for (const stmt of clause.statements) {
@@ -363,7 +373,7 @@ export function visitSwitchStatement(node, context) {
363
373
  }
364
374
  }
365
375
  // Prepare scope symbols for the switch block
366
- const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
376
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
367
377
  let firstIf = true;
368
378
  for (const clause of switchStmt.caseBlock.clauses) {
369
379
  if (ts.isCaseClause(clause)) {
@@ -374,13 +384,13 @@ export function visitSwitchStatement(node, context) {
374
384
  let condition = "";
375
385
  if (firstIf) {
376
386
  condition =
377
- `(${fallthroughVar} || ${switchValueVar}.is_strictly_equal_to_primitive(${caseExprCode}))`;
387
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
378
388
  code += `${this.indent()}if ${condition} {\n`;
379
389
  firstIf = false;
380
390
  }
381
391
  else {
382
392
  condition =
383
- `(${fallthroughVar} || ${switchValueVar}.is_strictly_equal_to_primitive(${caseExprCode}))`;
393
+ `(${fallthroughVar} || jspp::is_strictly_equal_to_primitive(${switchValueVar}, ${caseExprCode}))`;
384
394
  code += `${this.indent()}if ${condition} {\n`;
385
395
  }
386
396
  this.indentationLevel++;
@@ -392,7 +402,7 @@ export function visitSwitchStatement(node, context) {
392
402
  const contextForFunction = {
393
403
  ...context,
394
404
  topLevelScopeSymbols,
395
- currentScopeSymbols: hoistedSymbols,
405
+ localScopeSymbols: hoistedSymbols,
396
406
  };
397
407
  const lambda = this.generateLambda(stmt, contextForFunction, { isAssignment: true });
398
408
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
@@ -404,7 +414,7 @@ export function visitSwitchStatement(node, context) {
404
414
  switchBreakLabel,
405
415
  currentLabel: undefined, // Clear currentLabel for nested visits
406
416
  topLevelScopeSymbols,
407
- currentScopeSymbols: hoistedSymbols,
417
+ localScopeSymbols: hoistedSymbols,
408
418
  derefBeforeAssignment: true,
409
419
  isAssignmentOnly: ts.isVariableStatement(stmt),
410
420
  });
@@ -431,7 +441,7 @@ export function visitSwitchStatement(node, context) {
431
441
  switchBreakLabel,
432
442
  currentLabel: undefined, // Clear currentLabel for nested visits
433
443
  topLevelScopeSymbols,
434
- currentScopeSymbols: hoistedSymbols,
444
+ localScopeSymbols: hoistedSymbols,
435
445
  derefBeforeAssignment: true,
436
446
  isAssignmentOnly: ts.isVariableStatement(stmt),
437
447
  });