@xnoxs/flux-lang 3.1.1

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 (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,132 @@
1
+ // ── Flux stdlib ──
2
+
3
+ function map(arr, fn) { return arr.map(fn); }
4
+
5
+ function filter(arr, fn) { return arr.filter(fn); }
6
+
7
+ function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
8
+
9
+ function sort(arr, fn) { return arr.slice().sort(fn); }
10
+ // ── end stdlib ──
11
+
12
+ // Generated by Flux Transpiler v3.1.0
13
+ "use strict";
14
+
15
+ const Fs = require("fs");
16
+ const Path = require("path");
17
+ const Os = require("os");
18
+ const C = { reset: "\\x1b[0m", bold: "\\x1b[1m", red: "\\x1b[31m", green: "\\x1b[32m", yellow: "\\x1b[33m", cyan: "\\x1b[36m", gray: "\\x1b[90m", dim: "\\x1b[2m" };
19
+ function clr(c, s) {
20
+ return (process.env.NO_COLOR ? s : ((c + s) + C.reset));
21
+ }
22
+ const TEST_HELPERS = "\"use strict\";\n\nconst __results = [];\nlet __current = null;\n\nfunction assert(condition, message) {\n if (!condition) {\n throw new Error(message || 'Assertion failed: expected truthy');\n }\n}\n\nfunction assertEqual(actual, expected, message) {\n const ok = JSON.stringify(actual) === JSON.stringify(expected);\n if (!ok) {\n throw new Error(\n message ||\n 'Expected ' + JSON.stringify(expected) + ' but got ' + JSON.stringify(actual)\n );\n }\n}\n\nfunction assertNotEqual(actual, expected, message) {\n const ok = JSON.stringify(actual) !== JSON.stringify(expected);\n if (!ok) {\n throw new Error(message || 'Expected values to be different but both were ' + JSON.stringify(actual));\n }\n}\n\nfunction assertThrows(fn, message) {\n try { fn(); }\n catch (_) { return; }\n throw new Error(message || 'Expected function to throw, but it did not');\n}\n\nfunction assertClose(actual, expected, delta, message) {\n delta = delta || 1e-9;\n if (Math.abs(actual - expected) > delta) {\n throw new Error(message || ('Expected ' + actual + ' to be close to ' + expected + ' (delta=' + delta + ')'));\n }\n}\n\nfunction __runTest(name, fn) {\n try {\n fn();\n __results.push({ name: name, ok: true });\n } catch(e) {\n __results.push({ name: name, ok: false, error: e.message });\n }\n}";
23
+ function discoverTestFiles(target) {
24
+ const abs = Path.resolve(target);
25
+ if (!Fs.existsSync(abs)) {
26
+ throw new Error(("Not found: " + abs));
27
+ }
28
+ const stat = Fs.statSync(abs);
29
+ if (stat.isDirectory()) {
30
+ return Fs.readdirSync(abs).filter((f) => f.endsWith(".test.flux")).map((f) => Path.join(abs, f)).sort();
31
+ }
32
+ if (abs.endsWith(".flux")) {
33
+ return [abs];
34
+ }
35
+ throw new Error(("Expected a .flux file or directory, got: " + abs));
36
+ }
37
+ module.exports.discoverTestFiles = discoverTestFiles;
38
+ function runTestFile(filePath, transpile) {
39
+ const source = Fs.readFileSync(filePath, "utf8");
40
+ const result = transpile(source);
41
+ if (!result.success) {
42
+ return { file: Path.basename(filePath), errors: result.errors, tests: [], compile: false };
43
+ }
44
+ const fnNames = [];
45
+ const fnRe = /^function (test_[a-zA-Z0-9_]+)\s*\(/mg;
46
+ let m = fnRe.exec(result.output);
47
+ while (m) {
48
+ fnNames.push(m[1]);
49
+ m = fnRe.exec(result.output);
50
+ }
51
+ const runnerParts = [TEST_HELPERS, result.output, "", "// ── auto-generated test runner ──"];
52
+ for (const n of fnNames) {
53
+ runnerParts.push((((("__runTest(" + JSON.stringify(n.replace(/^test_/, "").replace(/_/g, " "))) + ", ") + n) + ");"));
54
+ }
55
+ runnerParts.push("module.exports = { __results };");
56
+ const runner = runnerParts.join("\n");
57
+ const tmpPath = Path.join(Os.tmpdir(), (("_flux_test_" + Date.now()) + ".js"));
58
+ try {
59
+ Fs.writeFileSync(tmpPath, runner, "utf8");
60
+ const mod = require(tmpPath);
61
+ const tests = ((mod && mod.__results) ? mod.__results : []);
62
+ return { file: Path.basename(filePath), tests, compile: true, errors: [] };
63
+ }
64
+ catch (e) {
65
+ return { file: Path.basename(filePath), tests: [], compile: false, errors: [{ message: ("Runtime error: " + e.message) }] };
66
+ }
67
+ finally {
68
+ try {
69
+ Fs.unlinkSync(tmpPath);
70
+ }
71
+ catch (ignored) {
72
+ null;
73
+ }
74
+ }
75
+ }
76
+ module.exports.runTestFile = runTestFile;
77
+ function runTests(target, transpile) {
78
+ let files = [];
79
+ try {
80
+ files = discoverTestFiles(target);
81
+ }
82
+ catch (e) {
83
+ console.error(clr(C.red, ("✗ " + e.message)));
84
+ process.exit(1);
85
+ }
86
+ if ((files.length == 0)) {
87
+ console.log(clr(C.yellow, ("No *.test.flux files found in: " + target)));
88
+ return;
89
+ }
90
+ let totalPass = 0;
91
+ let totalFail = 0;
92
+ let totalFiles = 0;
93
+ const t0 = Date.now();
94
+ for (const file of files) {
95
+ totalFiles = (totalFiles + 1);
96
+ console.log(clr(C.cyan, ("\n◈ " + Path.basename(file))));
97
+ const result = runTestFile(file, transpile);
98
+ if (!result.compile) {
99
+ console.log(clr(C.red, " ✗ Compile error:"));
100
+ for (const e of result.errors) {
101
+ console.log(clr(C.red, (" " + e.message)));
102
+ }
103
+ totalFail = (totalFail + 1);
104
+ continue;
105
+ }
106
+ if ((result.tests.length == 0)) {
107
+ console.log(clr(C.yellow, " (no test_ functions found)"));
108
+ continue;
109
+ }
110
+ for (const t of result.tests) {
111
+ if (t.ok) {
112
+ totalPass = (totalPass + 1);
113
+ console.log((clr(C.green, " ✓") + clr(C.gray, (" " + t.name))));
114
+ }
115
+ else {
116
+ totalFail = (totalFail + 1);
117
+ console.log(clr(C.red, (" ✗ " + t.name)));
118
+ console.log(clr(C.dim, (" " + t.error)));
119
+ }
120
+ }
121
+ }
122
+ const elapsed = (Date.now() - t0);
123
+ const total = (totalPass + totalFail);
124
+ console.log();
125
+ console.log(clr(C.bold, "─".repeat(50)));
126
+ console.log(((((clr(C.bold, "Results: ") + clr(C.green, (totalPass + " passed"))) + clr(C.gray, ", ")) + ((totalFail > 0) ? clr(C.red, (totalFail + " failed")) : clr(C.gray, (totalFail + " failed")))) + clr(C.gray, ((((" (" + total) + " total) in ") + elapsed) + "ms"))));
127
+ console.log();
128
+ if ((totalFail > 0)) {
129
+ process.exitCode = 1;
130
+ }
131
+ }
132
+ module.exports.runTests = runTests;
@@ -0,0 +1,123 @@
1
+ // ============================================================
2
+ // Flux Self-Hosted Transpiler — v3.0 (100% Self-Hosting)
3
+ // src/self/transpiler.flux — written in Flux, compiled by stage-0
4
+ //
5
+ // Full pipeline:
6
+ // CSS-preprocess → JSX-preprocess → Lex → Parse →
7
+ // Immutability-check → Type-check → Codegen
8
+ // ============================================================
9
+
10
+ import { Lexer, lexerize, T } from './lexer'
11
+ import { Parser, makeParser } from './parser'
12
+ import { CodeGenerator, makeCodeGen } from './codegen'
13
+ import { transformCss } from './css-preprocessor'
14
+ import { transformJsx, FLUX_H_BROWSER, FLUX_H_SERVER, FLUX_CSS_BROWSER, FLUX_CSS_SERVER } from './jsx'
15
+ import { Checker } from './checker'
16
+ import { FluxTypeChecker } from './type-checker'
17
+
18
+ // ── Version info ─────────────────────────────────────────────
19
+ export val FLUX_VERSION = "3.0.0"
20
+ export val FLUX_STAGE = "self-hosted"
21
+
22
+ // ── Public API ────────────────────────────────────────────────
23
+ export fn transpile(source, options):
24
+ val opts = options ?? {}
25
+
26
+ val result = {
27
+ success: false,
28
+ output: "",
29
+ ast: null,
30
+ tokens: null,
31
+ errors: [],
32
+ typeErrors: [],
33
+ typeWarnings: [],
34
+ stage: "",
35
+ }
36
+
37
+ try:
38
+ var src = source
39
+
40
+ // ── Stage 0a: CSS preprocessing ────────────────────────────
41
+ result.stage = "css"
42
+ if opts.css != false:
43
+ src = transformCss(src)
44
+
45
+ // ── Stage 0b: JSX preprocessing ────────────────────────────
46
+ result.stage = "jsx"
47
+ if opts.jsx:
48
+ val jsxTarget = opts.jsxTarget ?? "browser"
49
+ val jsxResult = transformJsx(src, { target: jsxTarget })
50
+ src = jsxResult.source
51
+ // JSX runtime helpers will be prepended to output if JSX detected
52
+ if jsxResult.hasJsx:
53
+ opts._jsxHelpers = jsxResult.runtimeHelpers
54
+
55
+ // ── Stage 1: Lex ────────────────────────────────────────────
56
+ result.stage = "lexer"
57
+ val lexer = lexerize(src)
58
+ val tokens = lexer.tokenize()
59
+ result.tokens = tokens
60
+
61
+ // ── Stage 2: Parse ──────────────────────────────────────────
62
+ result.stage = "parser"
63
+ val ast = makeParser(tokens).parse()
64
+ result.ast = ast
65
+
66
+ // ── Stage 3: Immutability check ─────────────────────────────
67
+ if opts.check != false:
68
+ result.stage = "checker"
69
+ val checker = new Checker([], [])
70
+ val checkResult = checker.check(ast)
71
+ if checkResult.errors.length > 0:
72
+ result.errors = checkResult.errors
73
+ result.stage = null
74
+ return result
75
+
76
+ // ── Stage 4: Type checking ──────────────────────────────────
77
+ if opts.typecheck:
78
+ result.stage = "type-checker"
79
+ val tc = new FluxTypeChecker([], [], new Map(), new Map(), new Map(), new Map())
80
+ val tcResult = tc.check(ast)
81
+ result.typeErrors = tcResult.errors
82
+ result.typeWarnings = tcResult.warnings
83
+ // Type errors are non-fatal warnings unless strict mode
84
+ if opts.strict and tcResult.errors.length > 0:
85
+ result.errors = tcResult.errors
86
+ result.stage = null
87
+ return result
88
+
89
+ // ── Stage 5: Code generation ────────────────────────────────
90
+ result.stage = "codegen"
91
+ val indent = opts.mangle ? "" : " "
92
+ val cg = makeCodeGen({ indent })
93
+ val genResult = cg.generate(ast)
94
+ var code = genResult.code
95
+
96
+ // Prepend JSX runtime helpers if needed
97
+ if opts._jsxHelpers:
98
+ code = opts._jsxHelpers + "\n" + code
99
+
100
+ result.output = code
101
+ result.success = true
102
+ result.stage = null
103
+
104
+ catch(e):
105
+ result.errors.push({
106
+ message: e.message,
107
+ name: e.name,
108
+ stage: result.stage,
109
+ })
110
+
111
+ return result
112
+
113
+ // ── Convenience: transpile with full options ──────────────────
114
+ export fn transpileFile(source, opts):
115
+ return transpile(source, {
116
+ check: true,
117
+ typecheck: opts?.typecheck ?? false,
118
+ jsx: opts?.jsx ?? false,
119
+ jsxTarget: opts?.jsxTarget ?? "browser",
120
+ mangle: opts?.mangle ?? false,
121
+ strict: opts?.strict ?? false,
122
+ ...(opts ?? {}),
123
+ })
@@ -0,0 +1,83 @@
1
+ // Generated by Flux Transpiler v3.1.0
2
+ "use strict";
3
+
4
+ const { Lexer, lexerize, T } = require("./lexer");
5
+ const { Parser, makeParser } = require("./parser");
6
+ const { CodeGenerator, makeCodeGen } = require("./codegen");
7
+ const { transformCss } = require("./css-preprocessor");
8
+ const { transformJsx, FLUX_H_BROWSER, FLUX_H_SERVER, FLUX_CSS_BROWSER, FLUX_CSS_SERVER } = require("./jsx");
9
+ const { Checker } = require("./checker");
10
+ const { FluxTypeChecker } = require("./type-checker");
11
+ const FLUX_VERSION = "3.0.0";
12
+ module.exports.FLUX_VERSION = FLUX_VERSION;
13
+ const FLUX_STAGE = "self-hosted";
14
+ module.exports.FLUX_STAGE = FLUX_STAGE;
15
+ function transpile(source, options) {
16
+ const opts = (options ?? { });
17
+ const result = { success: false, output: "", ast: null, tokens: null, errors: [], typeErrors: [], typeWarnings: [], stage: "" };
18
+ try {
19
+ let src = source;
20
+ result.stage = "css";
21
+ if ((opts.css != false)) {
22
+ src = transformCss(src);
23
+ }
24
+ result.stage = "jsx";
25
+ if (opts.jsx) {
26
+ const jsxTarget = (opts.jsxTarget ?? "browser");
27
+ const jsxResult = transformJsx(src, { target: jsxTarget });
28
+ src = jsxResult.source;
29
+ if (jsxResult.hasJsx) {
30
+ opts._jsxHelpers = jsxResult.runtimeHelpers;
31
+ }
32
+ }
33
+ result.stage = "lexer";
34
+ const lexer = lexerize(src);
35
+ const tokens = lexer.tokenize();
36
+ result.tokens = tokens;
37
+ result.stage = "parser";
38
+ const ast = makeParser(tokens).parse();
39
+ result.ast = ast;
40
+ if ((opts.check != false)) {
41
+ result.stage = "checker";
42
+ const checker = new Checker([], []);
43
+ const checkResult = checker.check(ast);
44
+ if ((checkResult.errors.length > 0)) {
45
+ result.errors = checkResult.errors;
46
+ result.stage = null;
47
+ return result;
48
+ }
49
+ }
50
+ if (opts.typecheck) {
51
+ result.stage = "type-checker";
52
+ const tc = new FluxTypeChecker([], [], new Map(), new Map(), new Map(), new Map());
53
+ const tcResult = tc.check(ast);
54
+ result.typeErrors = tcResult.errors;
55
+ result.typeWarnings = tcResult.warnings;
56
+ if ((opts.strict && (tcResult.errors.length > 0))) {
57
+ result.errors = tcResult.errors;
58
+ result.stage = null;
59
+ return result;
60
+ }
61
+ }
62
+ result.stage = "codegen";
63
+ const indent = (opts.mangle ? "" : " ");
64
+ const cg = makeCodeGen({ indent });
65
+ const genResult = cg.generate(ast);
66
+ let code = genResult.code;
67
+ if (opts._jsxHelpers) {
68
+ code = ((opts._jsxHelpers + "\n") + code);
69
+ }
70
+ result.output = code;
71
+ result.success = true;
72
+ result.stage = null;
73
+ }
74
+ catch (e) {
75
+ result.errors.push({ message: e.message, name: e.name, stage: result.stage });
76
+ }
77
+ return result;
78
+ }
79
+ module.exports.transpile = transpile;
80
+ function transpileFile(source, opts) {
81
+ return transpile(source, { check: true, typecheck: (opts?.typecheck ?? false), jsx: (opts?.jsx ?? false), jsxTarget: (opts?.jsxTarget ?? "browser"), mangle: (opts?.mangle ?? false), strict: (opts?.strict ?? false), ...(opts ?? { }) });
82
+ }
83
+ module.exports.transpileFile = transpileFile;