@ugo-studio/jspp 0.3.1 → 0.3.2

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 (64) 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 +44 -61
  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 +63 -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 -2
  38. package/src/prelude/utils/assignment_operators.hpp +40 -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 +87 -87
  42. package/src/prelude/utils/operators_native.hpp +349 -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/number.cpp +137 -92
  47. package/src/prelude/values/object.cpp +163 -122
  48. package/src/prelude/values/prototypes/boolean.hpp +24 -0
  49. package/src/prelude/values/prototypes/number.hpp +8 -1
  50. package/dist/cli/file-utils.js +0 -20
  51. package/dist/cli-utils/args.js +0 -59
  52. package/dist/cli-utils/colors.js +0 -9
  53. package/dist/cli-utils/file-utils.js +0 -20
  54. package/dist/cli-utils/spinner.js +0 -55
  55. package/dist/cli.js +0 -153
  56. package/dist/core/codegen/index.js +0 -88
  57. package/src/prelude/utils/operators_primitive.hpp +0 -337
  58. /package/dist/{ast → interpreter/ast}/symbols.js +0 -0
  59. /package/dist/{ast → interpreter/ast}/types.js +0 -0
  60. /package/dist/{core → interpreter/core}/codegen/visitor.js +0 -0
  61. /package/dist/{core → interpreter/core}/constants.js +0 -0
  62. /package/dist/{core → interpreter/core}/error.js +0 -0
  63. /package/dist/{core → interpreter/core}/parser.js +0 -0
  64. /package/dist/{core → interpreter/core}/traverser.js +0 -0
@@ -1,59 +0,0 @@
1
- import path from "path";
2
- import pkg from "../../package.json" with { type: "json" };
3
- import { COLORS } from "./colors.js";
4
- export function parseArgs(rawArgs) {
5
- let jsFilePathArg = null;
6
- let isRelease = false;
7
- let keepCpp = false;
8
- let outputExePath = null;
9
- let scriptArgs = [];
10
- for (let i = 0; i < rawArgs.length; i++) {
11
- const arg = rawArgs[i];
12
- if (!arg)
13
- continue;
14
- if (arg === "--") {
15
- scriptArgs = rawArgs.slice(i + 1);
16
- break;
17
- }
18
- if (arg === "--release") {
19
- isRelease = true;
20
- }
21
- else if (arg === "--keep-cpp") {
22
- keepCpp = true;
23
- }
24
- else if (arg === "-o" || arg === "--output") {
25
- if (i + 1 < rawArgs.length) {
26
- outputExePath = rawArgs[i + 1] ?? null;
27
- i++;
28
- }
29
- else {
30
- console.error(`${COLORS.red}Error: --output requires a file path argument.${COLORS.reset}`);
31
- process.exit(1);
32
- }
33
- }
34
- else if (arg.startsWith("-")) {
35
- console.warn(`${COLORS.yellow}Warning: Unknown argument '${arg}'${COLORS.reset}`);
36
- }
37
- else {
38
- if (jsFilePathArg) {
39
- console.error(`${COLORS.red}Error: Multiple input files specified.${COLORS.reset}`);
40
- process.exit(1);
41
- }
42
- jsFilePathArg = arg;
43
- }
44
- }
45
- if (!jsFilePathArg) {
46
- console.log(`${COLORS.bold}JSPP Compiler${COLORS.reset} ${COLORS.dim}v${pkg.version}${COLORS.reset}`);
47
- console.log(`${COLORS.bold}Usage:${COLORS.reset} jspp <path-to-js-file> [--release] [--keep-cpp] [-o <output-path>] [-- <args...>]`);
48
- process.exit(1);
49
- }
50
- return {
51
- jsFilePath: path.resolve(process.cwd(), jsFilePathArg),
52
- isRelease,
53
- keepCpp,
54
- outputExePath: outputExePath
55
- ? path.resolve(process.cwd(), outputExePath)
56
- : null,
57
- scriptArgs,
58
- };
59
- }
@@ -1,9 +0,0 @@
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
- };
@@ -1,20 +0,0 @@
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
- }
@@ -1,55 +0,0 @@
1
- import { COLORS } from "./colors.js";
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 DELETED
@@ -1,153 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawn } from "child_process";
3
- import fs from "fs/promises";
4
- import path from "path";
5
- import pkg from "../package.json" with { type: "json" };
6
- import { parseArgs } from "./cli-utils/args.js";
7
- import { COLORS } from "./cli-utils/colors.js";
8
- import { getLatestMtime } from "./cli-utils/file-utils.js";
9
- import { Spinner } from "./cli-utils/spinner.js";
10
- import { Interpreter } from "./index.js";
11
- const pkgDir = path.dirname(import.meta.dirname);
12
- async function main() {
13
- const { jsFilePath, isRelease, keepCpp, outputExePath, scriptArgs } = parseArgs(process.argv.slice(2));
14
- const jsFileName = path.basename(jsFilePath, ".js");
15
- const sourceDir = path.dirname(jsFilePath);
16
- // Intermediate C++ file goes alongside the source JS file
17
- const cppFilePath = path.join(sourceDir, `${jsFileName}.cpp`);
18
- // Determine output executable path
19
- let exeFilePath;
20
- if (outputExePath) {
21
- exeFilePath = outputExePath;
22
- }
23
- else {
24
- const ext = process.platform === "win32" ? ".exe" : "";
25
- exeFilePath = path.join(sourceDir, `${jsFileName}${ext}`);
26
- }
27
- // Mode Configuration
28
- const mode = isRelease ? "release" : "debug";
29
- console.log(`${COLORS.bold}JSPP Compiler${COLORS.reset} ${COLORS.dim}v${pkg.version}${COLORS.reset}`);
30
- console.log(`Mode: ${isRelease ? COLORS.green : COLORS.yellow}${mode.toUpperCase()}${COLORS.reset}\n`);
31
- const flags = isRelease ? ["-O3", "-DNDEBUG"] : ["-O0"];
32
- if (process.platform === "win32") {
33
- flags.push("-Wa,-mbig-obj");
34
- }
35
- const pchDir = path.resolve(pkgDir, "prelude-build", mode);
36
- const spinner = new Spinner("Initializing...");
37
- try {
38
- spinner.start();
39
- // 1. Interpreter Phase
40
- spinner.update(`Reading ${path.basename(jsFilePath)}...`);
41
- const jsCode = await fs.readFile(jsFilePath, "utf-8");
42
- spinner.update("Transpiling to C++...");
43
- const interpreter = new Interpreter();
44
- const { cppCode, preludePath } = interpreter.interpret(jsCode, jsFilePath);
45
- // Ensure directory for cpp file exists (should exist as it's source dir, but for safety if we change logic)
46
- await fs.mkdir(path.dirname(cppFilePath), { recursive: true });
47
- await fs.writeFile(cppFilePath, cppCode);
48
- spinner.succeed(`Generated cpp`);
49
- // 2. Precompiled Header Check
50
- spinner.text = "Checking precompiled headers...";
51
- spinner.start();
52
- const pchFile = path.join(pchDir, "index.hpp.gch");
53
- let shouldRebuild = false;
54
- try {
55
- const pchStats = await fs.stat(pchFile);
56
- const sourceMtime = await getLatestMtime(preludePath);
57
- if (sourceMtime > pchStats.mtimeMs) {
58
- shouldRebuild = true;
59
- }
60
- }
61
- catch (e) {
62
- shouldRebuild = true;
63
- }
64
- if (shouldRebuild) {
65
- spinner.update("Rebuilding precompiled headers (this may take a while)...");
66
- // Use spawn (async) instead of spawnSync to keep spinner alive
67
- const rebuild = spawn("bun", [
68
- "run",
69
- "scripts/precompile-headers.ts",
70
- ], {
71
- cwd: pkgDir,
72
- stdio: ["ignore", "pipe", "pipe"],
73
- });
74
- const stderrChunks = [];
75
- if (rebuild.stderr) {
76
- rebuild.stderr.on("data", (chunk) => stderrChunks.push(chunk));
77
- }
78
- const exitCode = await new Promise((resolve) => {
79
- rebuild.on("close", (code) => resolve(code ?? 1));
80
- });
81
- if (exitCode !== 0) {
82
- const stderr = Buffer.concat(stderrChunks).toString();
83
- spinner.fail("Failed to rebuild precompiled headers");
84
- console.error(stderr);
85
- process.exit(1);
86
- }
87
- spinner.succeed("Precompiled headers updated");
88
- }
89
- else {
90
- spinner.succeed("Precompiled headers");
91
- }
92
- // 3. Compilation Phase
93
- spinner.text = `Compiling binary...`;
94
- spinner.start();
95
- // Ensure output directory exists
96
- await fs.mkdir(path.dirname(exeFilePath), { recursive: true });
97
- const compile = spawn("g++", [
98
- "-std=c++23",
99
- ...flags,
100
- cppFilePath,
101
- "-o",
102
- exeFilePath,
103
- "-I",
104
- pchDir,
105
- "-I",
106
- preludePath,
107
- ], {
108
- stdio: ["ignore", "pipe", "pipe"],
109
- });
110
- const compileStderrChunks = [];
111
- if (compile.stderr) {
112
- compile.stderr.on("data", (chunk) => compileStderrChunks.push(chunk));
113
- }
114
- const compileExitCode = await new Promise((resolve) => {
115
- compile.on("close", (code) => resolve(code ?? 1));
116
- });
117
- if (compileExitCode !== 0) {
118
- const stderr = Buffer.concat(compileStderrChunks).toString();
119
- spinner.fail(`Compilation failed`);
120
- console.error(stderr);
121
- process.exit(1);
122
- }
123
- spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${path.basename(exeFilePath)}${COLORS.reset}`);
124
- // Clean up C++ file if not requested to keep
125
- if (!keepCpp) {
126
- try {
127
- await fs.unlink(cppFilePath);
128
- }
129
- catch (e) {
130
- // Ignore error if file cannot be deleted
131
- }
132
- }
133
- // 4. Execution Phase
134
- console.log(`\n${COLORS.cyan}--- Running Output ---${COLORS.reset}`);
135
- const run = spawn(exeFilePath, scriptArgs, {
136
- stdio: "inherit",
137
- });
138
- const runExitCode = await new Promise((resolve) => {
139
- run.on("close", (code) => resolve(code ?? 1));
140
- });
141
- console.log(`${COLORS.cyan}----------------------${COLORS.reset}\n`);
142
- if (runExitCode !== 0) {
143
- console.error(`${COLORS.red}Execution failed with exit code ${runExitCode}${COLORS.reset}`);
144
- process.exit(1);
145
- }
146
- }
147
- catch (error) {
148
- spinner.fail("An unexpected error occurred");
149
- console.error(error);
150
- process.exit(1);
151
- }
152
- }
153
- main();
@@ -1,88 +0,0 @@
1
- import { DeclaredSymbols } from "../../ast/symbols.js";
2
- import { generateLambdaComponents, generateNativeLambda, generateWrappedLambda, } from "./function-handlers.js";
3
- import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isDeclarationCalledAsFunction, isDeclarationUsedAsValue, isDeclarationUsedBeforeInitialization, isVariableUsedWithoutDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
4
- import { generateDestructuring } from "./destructuring-handlers.js";
5
- import { visit } from "./visitor.js";
6
- const MODULE_NAME = "__entry_point__";
7
- export class CodeGenerator {
8
- indentationLevel = 0;
9
- typeAnalyzer;
10
- isTypescript = false;
11
- globalThisVar;
12
- uniqueNameCounter = 0;
13
- // visitor
14
- visit = visit;
15
- // helpers
16
- getDeclaredSymbols = getDeclaredSymbols;
17
- generateUniqueName = generateUniqueName;
18
- generateUniqueExceptionName = generateUniqueExceptionName;
19
- hoistDeclaration = hoistDeclaration;
20
- getScopeForNode = getScopeForNode;
21
- indent = indent;
22
- escapeString = escapeString;
23
- getJsVarName = getJsVarName;
24
- getDerefCode = getDerefCode;
25
- getReturnCommand = getReturnCommand;
26
- isBuiltinObject = isBuiltinObject;
27
- isGeneratorFunction = isGeneratorFunction;
28
- isAsyncFunction = isAsyncFunction;
29
- prepareScopeSymbolsForVisit = prepareScopeSymbolsForVisit;
30
- markSymbolAsInitialized = markSymbolAsInitialized;
31
- isDeclarationCalledAsFunction = isDeclarationCalledAsFunction;
32
- isDeclarationUsedAsValue = isDeclarationUsedAsValue;
33
- isDeclarationUsedBeforeInitialization = isDeclarationUsedBeforeInitialization;
34
- isVariableUsedWithoutDeclaration = isVariableUsedWithoutDeclaration;
35
- validateFunctionParams = validateFunctionParams;
36
- generateDestructuring = generateDestructuring;
37
- // function handlers
38
- generateLambdaComponents = generateLambdaComponents;
39
- generateNativeLambda = generateNativeLambda;
40
- generateWrappedLambda = generateWrappedLambda;
41
- /**
42
- * Main entry point for the code generation process.
43
- */
44
- generate(ast, analyzer, isTypescript) {
45
- this.typeAnalyzer = analyzer;
46
- this.isTypescript = isTypescript;
47
- this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
48
- const declarations = `#include "jspp.hpp"\n#include "library/global_usings.hpp"\n\n`;
49
- let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
50
- this.indentationLevel++;
51
- containerCode +=
52
- `${this.indent()}jspp::AnyValue ${this.globalThisVar} = global;\n`;
53
- containerCode += this.visit(ast, {
54
- currentScopeNode: ast,
55
- isMainContext: true,
56
- isInsideFunction: true,
57
- isFunctionBody: true,
58
- isInsideAsyncFunction: true,
59
- globalScopeSymbols: new DeclaredSymbols(),
60
- localScopeSymbols: new DeclaredSymbols(),
61
- });
62
- this.indentationLevel--;
63
- containerCode += " co_return jspp::Constants::UNDEFINED;\n";
64
- containerCode += "}\n\n";
65
- let mainCode = "int main(int argc, char** argv) {\n";
66
- mainCode += ` try {\n`;
67
- mainCode += ` jspp::initialize_runtime();\n`;
68
- mainCode += ` jspp::setup_process_argv(argc, argv);\n`;
69
- mainCode += ` auto p = ${MODULE_NAME}();\n`;
70
- mainCode += ` p.then(nullptr, [](jspp::AnyValue err) {\n`;
71
- mainCode +=
72
- ` auto error = std::make_shared<jspp::AnyValue>(err);\n`;
73
- mainCode +=
74
- ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
75
- mainCode += ` std::exit(1);\n`;
76
- mainCode += ` });\n`;
77
- mainCode += ` jspp::Scheduler::instance().run();\n`;
78
- mainCode += ` } catch (const std::exception& ex) {\n`;
79
- mainCode +=
80
- " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n";
81
- mainCode +=
82
- ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
83
- mainCode += ` return 1;\n`;
84
- mainCode += ` }\n`;
85
- mainCode += " return 0;\n}";
86
- return declarations + containerCode + mainCode;
87
- }
88
- }