@ugo-studio/jspp 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +2 -2
  2. package/dist/analysis/scope.js +33 -4
  3. package/dist/analysis/typeAnalyzer.js +260 -21
  4. package/dist/ast/symbols.js +29 -0
  5. package/dist/cli-utils/args.js +57 -0
  6. package/dist/cli-utils/colors.js +9 -0
  7. package/dist/cli-utils/file-utils.js +20 -0
  8. package/dist/cli-utils/spinner.js +55 -0
  9. package/dist/cli.js +105 -31
  10. package/dist/core/codegen/class-handlers.js +131 -0
  11. package/dist/core/codegen/control-flow-handlers.js +474 -0
  12. package/dist/core/codegen/declaration-handlers.js +36 -15
  13. package/dist/core/codegen/expression-handlers.js +579 -125
  14. package/dist/core/codegen/function-handlers.js +222 -37
  15. package/dist/core/codegen/helpers.js +158 -4
  16. package/dist/core/codegen/index.js +20 -8
  17. package/dist/core/codegen/literal-handlers.js +18 -6
  18. package/dist/core/codegen/statement-handlers.js +171 -228
  19. package/dist/core/codegen/visitor.js +31 -3
  20. package/package.json +3 -3
  21. package/src/prelude/any_value.hpp +510 -633
  22. package/src/prelude/any_value_access.hpp +151 -0
  23. package/src/prelude/any_value_defines.hpp +190 -0
  24. package/src/prelude/any_value_helpers.hpp +139 -225
  25. package/src/prelude/exception.hpp +32 -0
  26. package/src/prelude/exception_helpers.hpp +49 -0
  27. package/src/prelude/index.hpp +25 -9
  28. package/src/prelude/library/array.hpp +190 -0
  29. package/src/prelude/library/console.hpp +14 -13
  30. package/src/prelude/library/error.hpp +113 -0
  31. package/src/prelude/library/function.hpp +10 -0
  32. package/src/prelude/library/global.hpp +35 -4
  33. package/src/prelude/library/math.hpp +308 -0
  34. package/src/prelude/library/object.hpp +288 -0
  35. package/src/prelude/library/performance.hpp +2 -2
  36. package/src/prelude/library/process.hpp +39 -0
  37. package/src/prelude/library/promise.hpp +131 -0
  38. package/src/prelude/library/symbol.hpp +46 -59
  39. package/src/prelude/library/timer.hpp +92 -0
  40. package/src/prelude/scheduler.hpp +145 -0
  41. package/src/prelude/types.hpp +58 -1
  42. package/src/prelude/utils/access.hpp +345 -0
  43. package/src/prelude/utils/assignment_operators.hpp +99 -0
  44. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  45. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  46. package/src/prelude/utils/log_any_value/function.hpp +39 -0
  47. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  48. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  49. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  50. package/src/prelude/utils/log_any_value/object.hpp +136 -0
  51. package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
  52. package/src/prelude/utils/operators.hpp +751 -0
  53. package/src/prelude/utils/well_known_symbols.hpp +25 -0
  54. package/src/prelude/values/array.hpp +10 -7
  55. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  56. package/src/prelude/values/function.hpp +85 -51
  57. package/src/prelude/values/helpers/array.hpp +80 -35
  58. package/src/prelude/values/helpers/function.hpp +110 -77
  59. package/src/prelude/values/helpers/iterator.hpp +16 -10
  60. package/src/prelude/values/helpers/object.hpp +85 -10
  61. package/src/prelude/values/helpers/promise.hpp +181 -0
  62. package/src/prelude/values/helpers/string.hpp +3 -3
  63. package/src/prelude/values/helpers/symbol.hpp +2 -2
  64. package/src/prelude/values/iterator.hpp +14 -6
  65. package/src/prelude/values/object.hpp +14 -3
  66. package/src/prelude/values/promise.hpp +73 -0
  67. package/src/prelude/values/prototypes/array.hpp +855 -16
  68. package/src/prelude/values/prototypes/function.hpp +4 -4
  69. package/src/prelude/values/prototypes/iterator.hpp +11 -10
  70. package/src/prelude/values/prototypes/number.hpp +153 -0
  71. package/src/prelude/values/prototypes/object.hpp +26 -0
  72. package/src/prelude/values/prototypes/promise.hpp +134 -0
  73. package/src/prelude/values/prototypes/string.hpp +29 -29
  74. package/src/prelude/values/prototypes/symbol.hpp +22 -3
  75. package/src/prelude/values/shape.hpp +52 -0
  76. package/src/prelude/values/string.hpp +1 -1
  77. package/src/prelude/values/symbol.hpp +1 -1
  78. package/src/prelude/access.hpp +0 -91
  79. package/src/prelude/error.hpp +0 -31
  80. package/src/prelude/error_helpers.hpp +0 -59
  81. package/src/prelude/log_string.hpp +0 -407
  82. package/src/prelude/operators.hpp +0 -256
  83. package/src/prelude/well_known_symbols.hpp +0 -14
package/dist/cli.js CHANGED
@@ -1,64 +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
- console.time(`Generated C++ code ${cppFilePath}...`);
20
41
  const { cppCode, preludePath } = interpreter.interpret(jsCode);
21
- console.timeEnd(`Generated C++ code ${cppFilePath}...`);
22
- 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 });
23
44
  await fs.writeFile(cppFilePath, cppCode);
24
- console.log(`Compiling ${cppFilePath}...`);
25
- 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({
26
87
  cmd: [
27
88
  "g++",
28
89
  "-std=c++23",
90
+ ...flags,
29
91
  cppFilePath,
30
92
  "-o",
31
93
  exeFilePath,
32
94
  "-I",
95
+ pchDir,
96
+ "-I",
33
97
  preludePath,
34
- "-O3",
35
- "-DNDEBUG",
36
- // "-include",
37
- // path.join(process.cwd(), "prelude-build", "index.hpp"),
38
98
  ],
39
- stdout: "inherit",
40
- stderr: "inherit",
99
+ stdout: "pipe",
100
+ stderr: "pipe",
41
101
  });
42
- if (compile.exitCode !== 0) {
43
- 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);
44
107
  process.exit(1);
45
108
  }
46
- console.log(`Running ${exeFilePath}...`);
47
- console.log("\x1b[32m\n------start------\x1b[0m");
48
- const run = Bun.spawnSync({
49
- 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],
50
123
  stdout: "inherit",
51
124
  stderr: "inherit",
52
125
  });
53
- console.log("\x1b[32m\n------end--------\x1b[0m");
54
- if (run.exitCode !== 0) {
55
- 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}`);
56
130
  process.exit(1);
57
131
  }
58
- console.log(`Successfully ran ${exeFilePath}`);
59
132
  }
60
133
  catch (error) {
61
- console.error(`Error: ${error.message}`);
134
+ spinner.fail("An unexpected error occurred");
135
+ console.error(error);
62
136
  process.exit(1);
63
137
  }
64
138
  }
@@ -0,0 +1,131 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ import { visitObjectPropertyName } from "./expression-handlers";
4
+ export function visitClassDeclaration(node, context) {
5
+ const className = node.name.getText();
6
+ // Check extends
7
+ let extendsExpr = null;
8
+ if (node.heritageClauses) {
9
+ for (const clause of node.heritageClauses) {
10
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword &&
11
+ clause.types.length > 0) {
12
+ extendsExpr = clause.types[0].expression;
13
+ }
14
+ }
15
+ }
16
+ let parentName = "";
17
+ if (extendsExpr) {
18
+ parentName = this.visit(extendsExpr, context);
19
+ if (ts.isIdentifier(extendsExpr)) {
20
+ const scope = this.getScopeForNode(extendsExpr);
21
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(extendsExpr.getText(), scope);
22
+ if (typeInfo) {
23
+ parentName = this.getDerefCode(parentName, this.getJsVarName(extendsExpr), context, typeInfo);
24
+ }
25
+ }
26
+ }
27
+ const classContext = {
28
+ ...context,
29
+ superClassVar: parentName,
30
+ };
31
+ // 1. Constructor
32
+ const constructor = node.members.find(ts.isConstructorDeclaration);
33
+ let constructorLambda = "";
34
+ if (constructor) {
35
+ constructorLambda = this.generateLambda(constructor, {
36
+ ...classContext,
37
+ isInsideFunction: true,
38
+ lambdaName: className,
39
+ }, { isClass: true });
40
+ }
41
+ else {
42
+ // Default constructor
43
+ if (parentName) {
44
+ constructorLambda =
45
+ `jspp::AnyValue::make_class([=](const jspp::AnyValue& ${this.globalThisVar}, std::span<const jspp::AnyValue> args) mutable -> jspp::AnyValue {
46
+ auto __parent = ${parentName};
47
+ __parent.call(${this.globalThisVar}, args, "super");
48
+ return jspp::Constants::UNDEFINED;
49
+ }, "${className}")`;
50
+ }
51
+ else {
52
+ constructorLambda =
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
+ }, "${className}")`;
56
+ }
57
+ }
58
+ let code = `${this.indent()}*${className} = ${constructorLambda};\n`;
59
+ // Set prototype of class (static inheritance) and prototype object (instance inheritance)
60
+ if (parentName) {
61
+ code +=
62
+ `${this.indent()}(*${className}).set_prototype(${parentName});\n`;
63
+ code +=
64
+ `${this.indent()}(*${className}).get_own_property("prototype").set_prototype(${parentName}.get_own_property("prototype"));\n`;
65
+ }
66
+ else {
67
+ code +=
68
+ `${this.indent()}(*${className}).get_own_property("prototype").set_prototype(::Object.get_own_property("prototype"));\n`;
69
+ }
70
+ // Members
71
+ for (const member of node.members) {
72
+ if (ts.isMethodDeclaration(member)) {
73
+ const methodName = visitObjectPropertyName.call(this, member.name, {
74
+ ...context,
75
+ isObjectLiteralExpression: true, // Reuse this flag to handle computed properties
76
+ });
77
+ const isStatic = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword);
78
+ const methodLambda = this.generateLambda(member, {
79
+ ...classContext,
80
+ isInsideFunction: true,
81
+ });
82
+ if (isStatic) {
83
+ code +=
84
+ `${this.indent()}(*${className}).set_own_property(${methodName}, ${methodLambda});\n`;
85
+ }
86
+ else {
87
+ code +=
88
+ `${this.indent()}(*${className}).get_own_property("prototype").set_own_property(${methodName}, ${methodLambda});\n`;
89
+ }
90
+ }
91
+ else if (ts.isGetAccessor(member)) {
92
+ const methodName = visitObjectPropertyName.call(this, member.name, {
93
+ ...context,
94
+ isObjectLiteralExpression: true,
95
+ });
96
+ const isStatic = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword);
97
+ const lambda = this.generateLambda(member, {
98
+ ...classContext,
99
+ isInsideFunction: true,
100
+ });
101
+ if (isStatic) {
102
+ code +=
103
+ `${this.indent()}(*${className}).define_getter(${methodName}, ${lambda});\n`;
104
+ }
105
+ else {
106
+ code +=
107
+ `${this.indent()}(*${className}).get_own_property("prototype").define_getter(${methodName}, ${lambda});\n`;
108
+ }
109
+ }
110
+ else if (ts.isSetAccessor(member)) {
111
+ const methodName = visitObjectPropertyName.call(this, member.name, {
112
+ ...context,
113
+ isObjectLiteralExpression: true,
114
+ });
115
+ const isStatic = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword);
116
+ const lambda = this.generateLambda(member, {
117
+ ...classContext,
118
+ isInsideFunction: true,
119
+ });
120
+ if (isStatic) {
121
+ code +=
122
+ `${this.indent()}(*${className}).define_setter(${methodName}, ${lambda});\n`;
123
+ }
124
+ else {
125
+ code +=
126
+ `${this.indent()}(*${className}).get_own_property("prototype").define_setter(${methodName}, ${lambda});\n`;
127
+ }
128
+ }
129
+ }
130
+ return code;
131
+ }