@xnoxs/flux-lang 3.5.3 → 4.0.0
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.
- package/CHANGELOG.md +31 -0
- package/README.md +3 -1
- package/dist/flux-cli.js +171 -11
- package/dist/flux.cjs.js +64 -1
- package/dist/flux.esm.js +64 -1
- package/dist/flux.min.js +20 -20
- package/package.json +1 -1
- package/src/self/codegen.flux +5 -2
- package/src/self/codegen.js +1 -794
- package/src/self/lexer.flux +1 -1
- package/src/self/lexer.js +1 -713
- package/src/self/lexer.stage2.js +700 -0
- package/src/self/parser.flux +41 -0
- package/src/self/parser.js +1 -1571
- package/src/self/pkg.flux +76 -0
- package/src/self/pkg.js +82 -0
- package/src/self/transpiler.flux +14 -4
- package/src/self/transpiler.js +1 -83
- package/src/self/type-checker.flux +12 -0
- package/src/self/type-checker.js +1 -1114
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [4.0.0] — 2026-06-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`declare` keyword** — ambient type declarations, zero JS output
|
|
14
|
+
- `declare fn name(params): RetType` — declare external function signature
|
|
15
|
+
- `declare async fn name(params): RetType` — declare async external function
|
|
16
|
+
- `declare fn name(params) -> RetType` — alternative arrow return type syntax
|
|
17
|
+
- `declare val name: Type` — declare typed global constant
|
|
18
|
+
- `declare var name: Type` — declare typed mutable global
|
|
19
|
+
- `declare class Name` — declare external class
|
|
20
|
+
- Type checker registers all declared names in scope with their types
|
|
21
|
+
- Codegen emits **zero JavaScript** for all `declare` nodes (ambient only)
|
|
22
|
+
- Self-hosted compiler (lexer, parser, codegen, type-checker) fully updated
|
|
23
|
+
- 11 new tests in `tests/14_declare.test.flux`
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- **`flux fmt <directory>`** — previously crashed with `EISDIR`; now formats all `.flux` files in the given directory recursively
|
|
27
|
+
- **`flux publish`** — previously crashed with "package.json not found" in Flux projects using `flux.json`; now falls back correctly to `flux.json`
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Version bumped from `3.5.3` → `4.0.0`
|
|
31
|
+
- All version references in webapp (`app/ecosystem.flux`) updated to `v4.0.0`
|
|
32
|
+
- README updated with v4.0.0 release notes
|
|
33
|
+
- Docs: new **Ambient Declarations** section (`/docs#declare`) with full reference table, two code examples, callouts
|
|
34
|
+
- Docs sidebar: `Ambient Declarations` added under Type System and v3 Features
|
|
35
|
+
- Examples page: new **📣 declare** card
|
|
36
|
+
- Playground: new **declare** entry in Load Example dropdown
|
|
37
|
+
- Total tests: **113 passed** (102 language + 11 declare)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
10
41
|
## [3.4.4] — 2026-06-26
|
|
11
42
|
|
|
12
43
|
### Added
|
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# ⚡ Flux Lang
|
|
1
|
+
# ⚡ Flux Lang v4.0.0
|
|
2
2
|
|
|
3
3
|
**Flux** is a modern programming language that transpiles to clean JavaScript — combining Python-style indentation, TypeScript-grade type safety, Rust-inspired pattern matching, and a rich standard library — all with **zero runtime overhead**.
|
|
4
4
|
|
|
5
|
+
> **v4.0.0 — `declare` keyword + full ambient declarations:** Flux now supports TypeScript-style ambient declarations. Declare external functions, variables, and classes without implementation — the type checker registers them in scope, codegen emits zero JS. Self-hosted compiler fully updated.
|
|
6
|
+
|
|
5
7
|
> **v3.4.0 — Fully Self-Hosted:** Every component of the Flux compiler — lexer, parser, type checker, code generator, formatter, linter, bundler, package manager, and CLI — is now written entirely in Flux and compiled by itself. `node scripts/bootstrap.js` → "Bootstrap complete — Flux is self-hosting."
|
|
6
8
|
|
|
7
9
|
```
|
package/dist/flux-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*!
|
|
3
|
-
* flux-lang
|
|
3
|
+
* flux-lang v4.0.0
|
|
4
4
|
* Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.
|
|
5
5
|
* (c) 2026 Flux Lang Contributors
|
|
6
6
|
* Released under the MIT License
|
|
@@ -101,6 +101,7 @@ var require_lexer = __commonJS({
|
|
|
101
101
|
VAL: "VAL",
|
|
102
102
|
FN: "FN",
|
|
103
103
|
RETURN: "RETURN",
|
|
104
|
+
DECLARE: "DECLARE",
|
|
104
105
|
// Keywords — control flow
|
|
105
106
|
IF: "IF",
|
|
106
107
|
ELSE: "ELSE",
|
|
@@ -237,6 +238,7 @@ var require_lexer = __commonJS({
|
|
|
237
238
|
val: T.VAL,
|
|
238
239
|
fn: T.FN,
|
|
239
240
|
return: T.RETURN,
|
|
241
|
+
declare: T.DECLARE,
|
|
240
242
|
if: T.IF,
|
|
241
243
|
else: T.ELSE,
|
|
242
244
|
for: T.FOR,
|
|
@@ -1112,6 +1114,8 @@ var require_parser = __commonJS({
|
|
|
1112
1114
|
if (decorators.length) n.decorators = decorators;
|
|
1113
1115
|
return n;
|
|
1114
1116
|
}
|
|
1117
|
+
case T.DECLARE:
|
|
1118
|
+
return this.parseDeclareDecl();
|
|
1115
1119
|
case T.IF:
|
|
1116
1120
|
return this.parseIf();
|
|
1117
1121
|
case T.FOR:
|
|
@@ -1582,6 +1586,48 @@ var require_parser = __commonJS({
|
|
|
1582
1586
|
}
|
|
1583
1587
|
this.err("Expected -> or : after function signature");
|
|
1584
1588
|
}
|
|
1589
|
+
// ── declare fn/val/var/class ───────────────────────────────────
|
|
1590
|
+
parseDeclareDecl() {
|
|
1591
|
+
const loc = this.eat(T.DECLARE);
|
|
1592
|
+
const tok = this.peek();
|
|
1593
|
+
if (tok.type === T.FN || tok.type === T.ASYNC) {
|
|
1594
|
+
const isAsync = tok.type === T.ASYNC;
|
|
1595
|
+
this.skip();
|
|
1596
|
+
if (isAsync) this.eat(T.FN);
|
|
1597
|
+
const KEYWORD_AS_NAME = /* @__PURE__ */ new Set([T.NEW, T.DELETE, T.FROM, T.AS, T.DEFAULT, T.IS, T.IN, T.TYPE]);
|
|
1598
|
+
const name = this.check(T.IDENT) || KEYWORD_AS_NAME.has(this.peek().type) ? this.skip().value : null;
|
|
1599
|
+
const params = this.parseParamList();
|
|
1600
|
+
let retType = null;
|
|
1601
|
+
if (this.check(T.ARROW)) {
|
|
1602
|
+
this.pos++;
|
|
1603
|
+
retType = this.parseTypeAnn();
|
|
1604
|
+
} else if (this.check(T.COLON)) {
|
|
1605
|
+
this.pos++;
|
|
1606
|
+
retType = this.parseTypeAnn();
|
|
1607
|
+
}
|
|
1608
|
+
this.skipNewlines();
|
|
1609
|
+
const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: isAsync, loc: tok };
|
|
1610
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1611
|
+
}
|
|
1612
|
+
if (tok.type === T.VAL || tok.type === T.VAR) {
|
|
1613
|
+
const kind = tok.type === T.VAR ? "var" : "val";
|
|
1614
|
+
this.skip();
|
|
1615
|
+
const name = this.eat(T.IDENT).value;
|
|
1616
|
+
let typeAnn = null;
|
|
1617
|
+
if (this.maybe(T.COLON)) typeAnn = this.parseTypeAnn();
|
|
1618
|
+
this.skipNewlines();
|
|
1619
|
+
const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: tok };
|
|
1620
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1621
|
+
}
|
|
1622
|
+
if (tok.type === T.CLASS) {
|
|
1623
|
+
this.skip();
|
|
1624
|
+
const name = this.eat(T.IDENT).value;
|
|
1625
|
+
this.skipNewlines();
|
|
1626
|
+
const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: tok };
|
|
1627
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1628
|
+
}
|
|
1629
|
+
this.err("Expected fn, async fn, val, var, or class after declare");
|
|
1630
|
+
}
|
|
1585
1631
|
// ── async fn ──────────────────────────────────────────────────
|
|
1586
1632
|
parseAsyncFn() {
|
|
1587
1633
|
this.eat(T.ASYNC);
|
|
@@ -2693,6 +2739,9 @@ function _fmt(v, s) {
|
|
|
2693
2739
|
return this.genInterfaceDecl(node);
|
|
2694
2740
|
case "EnumDecl":
|
|
2695
2741
|
return this.genEnumDecl(node);
|
|
2742
|
+
case "DeclareDecl":
|
|
2743
|
+
return;
|
|
2744
|
+
// ambient declaration — no JS output
|
|
2696
2745
|
case "ExprStmt":
|
|
2697
2746
|
return this.emit(this.genExpr(node.expr) + ";");
|
|
2698
2747
|
default:
|
|
@@ -5695,6 +5744,20 @@ var require_type_checker = __commonJS({
|
|
|
5695
5744
|
}
|
|
5696
5745
|
break;
|
|
5697
5746
|
}
|
|
5747
|
+
case "DeclareDecl": {
|
|
5748
|
+
const d = node.decl;
|
|
5749
|
+
if (d.type === "FnDecl") {
|
|
5750
|
+
const retType = d.retType ? parseAnnotation(d.retType) : null;
|
|
5751
|
+
const paramTypes = d.params.map((p) => p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN);
|
|
5752
|
+
if (d.name) env.set(d.name, T_FN(paramTypes, retType || T_UNKNOWN));
|
|
5753
|
+
} else if (d.type === "VarDecl") {
|
|
5754
|
+
const t = d.typeAnn ? parseAnnotation(d.typeAnn) : T_UNKNOWN;
|
|
5755
|
+
env.set(d.name, t);
|
|
5756
|
+
} else if (d.type === "ClassDecl") {
|
|
5757
|
+
env.set(d.name, T_NAMED(d.name));
|
|
5758
|
+
}
|
|
5759
|
+
break;
|
|
5760
|
+
}
|
|
5698
5761
|
case "TypeDecl":
|
|
5699
5762
|
for (const v of node.variants) {
|
|
5700
5763
|
if (v.fields.length === 0) env.set(v.name, T_NAMED(node.name));
|
|
@@ -7243,7 +7306,7 @@ var require_package = __commonJS({
|
|
|
7243
7306
|
"package.json"(exports2, module2) {
|
|
7244
7307
|
module2.exports = {
|
|
7245
7308
|
name: "@xnoxs/flux-lang",
|
|
7246
|
-
version: "
|
|
7309
|
+
version: "4.0.0",
|
|
7247
7310
|
description: "Flux \u2014 A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.",
|
|
7248
7311
|
main: "dist/flux.cjs.js",
|
|
7249
7312
|
module: "dist/flux.esm.js",
|
|
@@ -8885,6 +8948,82 @@ var require_pkg = __commonJS({
|
|
|
8885
8948
|
}
|
|
8886
8949
|
}
|
|
8887
8950
|
module2.exports.cmdInfo = cmdInfo;
|
|
8951
|
+
async function cmdUpgrade(opts) {
|
|
8952
|
+
var _a;
|
|
8953
|
+
const isCheck = (opts == null ? void 0 : opts.check) ?? false;
|
|
8954
|
+
const cwd = process.cwd();
|
|
8955
|
+
const pkg = readPackage(cwd);
|
|
8956
|
+
if (!pkg) {
|
|
8957
|
+
err("No flux.json found. Run: flux init");
|
|
8958
|
+
return;
|
|
8959
|
+
}
|
|
8960
|
+
const deps = pkg.dependencies ?? {};
|
|
8961
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
8962
|
+
const all = { ...deps, ...devDeps };
|
|
8963
|
+
const names = Object.keys(all);
|
|
8964
|
+
if (names.length == 0) {
|
|
8965
|
+
info("No dependencies to upgrade");
|
|
8966
|
+
console.log();
|
|
8967
|
+
return;
|
|
8968
|
+
}
|
|
8969
|
+
if (isCheck) {
|
|
8970
|
+
console.log(clr2(C2.cyan, "\n Checking " + names.length + " package(s) for updates...\n"));
|
|
8971
|
+
} else {
|
|
8972
|
+
console.log(clr2(C2.cyan, "\n Upgrading " + names.length + " package(s) to latest...\n"));
|
|
8973
|
+
}
|
|
8974
|
+
let outdated = 0;
|
|
8975
|
+
let updated = 0;
|
|
8976
|
+
for (const name of names) {
|
|
8977
|
+
const current = all[name];
|
|
8978
|
+
const spinner = startSpinner("Checking " + clr2(C2.bold, name) + " ...");
|
|
8979
|
+
try {
|
|
8980
|
+
const info_ = await fetchJson(REGISTRY_URL + "/" + name);
|
|
8981
|
+
const latest = ((_a = info_["dist-tags"]) == null ? void 0 : _a.latest) ?? null;
|
|
8982
|
+
stopSpinner(spinner);
|
|
8983
|
+
if (!latest) {
|
|
8984
|
+
console.log(" " + clr2(C2.gray, "? ") + clr2(C2.bold, name) + clr2(C2.gray, " \u2014 could not resolve latest"));
|
|
8985
|
+
continue;
|
|
8986
|
+
}
|
|
8987
|
+
const currentClean = current.replace("^", "").replace("~", "");
|
|
8988
|
+
if (currentClean == latest) {
|
|
8989
|
+
console.log(" " + clr2(C2.green, "\u2713 ") + clr2(C2.bold, name) + clr2(C2.gray, " " + current + " (up to date)"));
|
|
8990
|
+
} else {
|
|
8991
|
+
outdated = outdated + 1;
|
|
8992
|
+
if (isCheck) {
|
|
8993
|
+
console.log(" " + clr2(C2.yellow, "\u2191 ") + clr2(C2.bold, name) + clr2(C2.gray, " " + current + " \u2192 ") + clr2(C2.green, "^" + latest));
|
|
8994
|
+
} else {
|
|
8995
|
+
const isDev = devDeps[name] != null;
|
|
8996
|
+
const depKey = isDev ? "devDependencies" : "dependencies";
|
|
8997
|
+
pkg[depKey][name] = "^" + latest;
|
|
8998
|
+
updated = updated + 1;
|
|
8999
|
+
console.log(" " + clr2(C2.green, "\u2713 ") + clr2(C2.bold, name) + clr2(C2.gray, " " + current + " \u2192 ") + clr2(C2.green, "^" + latest));
|
|
9000
|
+
}
|
|
9001
|
+
}
|
|
9002
|
+
} catch (e) {
|
|
9003
|
+
stopSpinner(spinner);
|
|
9004
|
+
console.log(" " + clr2(C2.red, "\u2717 ") + clr2(C2.bold, name) + clr2(C2.gray, " \u2014 " + e.message));
|
|
9005
|
+
}
|
|
9006
|
+
}
|
|
9007
|
+
console.log();
|
|
9008
|
+
if (isCheck) {
|
|
9009
|
+
if (outdated == 0) {
|
|
9010
|
+
ok("All packages are up to date");
|
|
9011
|
+
} else {
|
|
9012
|
+
console.log(clr2(C2.yellow, " " + outdated + " package(s) can be upgraded"));
|
|
9013
|
+
console.log(clr2(C2.gray, " Run ") + clr2(C2.yellow, "flux upgrade") + clr2(C2.gray, " to apply updates"));
|
|
9014
|
+
}
|
|
9015
|
+
} else {
|
|
9016
|
+
if (updated == 0) {
|
|
9017
|
+
ok("All packages are already up to date");
|
|
9018
|
+
} else {
|
|
9019
|
+
saveFluxJson(pkg, cwd);
|
|
9020
|
+
ok(updated + " package(s) updated in flux.json");
|
|
9021
|
+
console.log(clr2(C2.gray, " Run ") + clr2(C2.yellow, "flux install") + clr2(C2.gray, " to install updated versions"));
|
|
9022
|
+
}
|
|
9023
|
+
}
|
|
9024
|
+
console.log();
|
|
9025
|
+
}
|
|
9026
|
+
module2.exports.cmdUpgrade = cmdUpgrade;
|
|
8888
9027
|
function cmdPublish2(opts) {
|
|
8889
9028
|
const cwd = process.cwd();
|
|
8890
9029
|
const pkg = readPackage(cwd);
|
|
@@ -9024,7 +9163,7 @@ function showHelp() {
|
|
|
9024
9163
|
console.log(clr(C.gray, " flux watch app.flux"));
|
|
9025
9164
|
console.log(clr(C.gray, " flux repl"));
|
|
9026
9165
|
console.log();
|
|
9027
|
-
console.log(clr(C.bold,
|
|
9166
|
+
console.log(clr(C.bold, `WHAT'S NEW in v${VERSION}:`));
|
|
9028
9167
|
const news = [
|
|
9029
9168
|
"Return type annotations \u2014 fn greet(name: String) -> String:",
|
|
9030
9169
|
"Union types \u2014 val x: Int | String | Null",
|
|
@@ -9671,7 +9810,18 @@ function cmdLint(filePath) {
|
|
|
9671
9810
|
if (exitCode !== 0) process.exit(exitCode);
|
|
9672
9811
|
}
|
|
9673
9812
|
function cmdFmt(filePath, opts) {
|
|
9674
|
-
const
|
|
9813
|
+
const abs = path.resolve(filePath);
|
|
9814
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
|
|
9815
|
+
const entries = fs.readdirSync(abs).filter((f) => f.endsWith(".flux"));
|
|
9816
|
+
if (entries.length === 0) {
|
|
9817
|
+
console.log(clr(C.gray, ` No .flux files found in ${filePath}`));
|
|
9818
|
+
return;
|
|
9819
|
+
}
|
|
9820
|
+
let changed = 0;
|
|
9821
|
+
for (const entry of entries) cmdFmt(path.join(filePath, entry), opts);
|
|
9822
|
+
return;
|
|
9823
|
+
}
|
|
9824
|
+
const { source } = readFluxFile(filePath);
|
|
9675
9825
|
const { format } = require_formatter();
|
|
9676
9826
|
const output = format(source);
|
|
9677
9827
|
if (output === source) {
|
|
@@ -9938,9 +10088,19 @@ ${e.message}`);
|
|
|
9938
10088
|
banner();
|
|
9939
10089
|
console.log(clr(C.bold, ` Release Workflow${isDry ? clr(C.yellow, " [DRY RUN]") : ""}
|
|
9940
10090
|
`));
|
|
9941
|
-
const pkgPath = path.resolve("package.json");
|
|
9942
10091
|
const changelogPath = path.resolve("CHANGELOG.md");
|
|
9943
|
-
|
|
10092
|
+
const npmPkgPath = path.resolve("package.json");
|
|
10093
|
+
const fluxPkgPath = path.resolve("flux.json");
|
|
10094
|
+
let pkgPath, pkgFile;
|
|
10095
|
+
if (fs.existsSync(npmPkgPath)) {
|
|
10096
|
+
pkgPath = npmPkgPath;
|
|
10097
|
+
pkgFile = "package.json";
|
|
10098
|
+
} else if (fs.existsSync(fluxPkgPath)) {
|
|
10099
|
+
pkgPath = fluxPkgPath;
|
|
10100
|
+
pkgFile = "flux.json";
|
|
10101
|
+
} else {
|
|
10102
|
+
fail("No package.json or flux.json found \u2014 run: flux init");
|
|
10103
|
+
}
|
|
9944
10104
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
9945
10105
|
const oldVersion = pkg.version;
|
|
9946
10106
|
const newVersion = bumpVersion(oldVersion, bumpType);
|
|
@@ -10002,19 +10162,19 @@ ${e.message}`);
|
|
|
10002
10162
|
step("Confirm release");
|
|
10003
10163
|
const go = await confirm(`Release ${clr(C.bold, pkg.name + "@" + newVersion)} (${tagName})?`);
|
|
10004
10164
|
if (!go) fail("Release cancelled");
|
|
10005
|
-
step(`Bumping
|
|
10165
|
+
step(`Bumping ${pkgFile}: ${oldVersion} \u2192 ${newVersion}`);
|
|
10006
10166
|
if (!isDry) {
|
|
10007
10167
|
pkg.version = newVersion;
|
|
10008
10168
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
10009
10169
|
} else {
|
|
10010
|
-
dry(
|
|
10170
|
+
dry(`${pkgFile}: "version": "${oldVersion}" \u2192 "${newVersion}"`);
|
|
10011
10171
|
}
|
|
10012
|
-
ok(
|
|
10172
|
+
ok(`${pkgFile} updated`);
|
|
10013
10173
|
step("Updating CHANGELOG.md");
|
|
10014
10174
|
const clUpdated = updateChangelog(newVersion, changelogPath);
|
|
10015
10175
|
if (clUpdated) ok(`CHANGELOG.md updated \u2014 [${newVersion}] section created`);
|
|
10016
10176
|
step("Creating git commit");
|
|
10017
|
-
run(
|
|
10177
|
+
run(`git add ${pkgFile} CHANGELOG.md`);
|
|
10018
10178
|
run(`git commit -m "chore: release ${tagName}"`);
|
|
10019
10179
|
ok(`Committed: chore: release ${tagName}`);
|
|
10020
10180
|
step(`Creating git tag: ${tagName}`);
|
|
@@ -10046,7 +10206,7 @@ ${e.message}`);
|
|
|
10046
10206
|
console.log(clr(C.green, C.bold + ` \u2713 Released: ${pkg.name}@${newVersion}` + C.reset));
|
|
10047
10207
|
console.log();
|
|
10048
10208
|
console.log(clr(C.gray, " Summary:"));
|
|
10049
|
-
console.log(` ${clr(C.cyan,
|
|
10209
|
+
console.log(` ${clr(C.cyan, pkgFile)} version \u2192 ${newVersion}`);
|
|
10050
10210
|
if (clUpdated)
|
|
10051
10211
|
console.log(` ${clr(C.cyan, `CHANGELOG.md`)} [${newVersion}] \u2014 ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
|
|
10052
10212
|
console.log(` ${clr(C.cyan, `git tag`)} ${tagName}`);
|
package/dist/flux.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* flux-lang
|
|
2
|
+
* flux-lang v4.0.0
|
|
3
3
|
* Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.
|
|
4
4
|
* (c) 2026 Flux Lang Contributors
|
|
5
5
|
* Released under the MIT License
|
|
@@ -30,6 +30,7 @@ var require_lexer = __commonJS({
|
|
|
30
30
|
VAL: "VAL",
|
|
31
31
|
FN: "FN",
|
|
32
32
|
RETURN: "RETURN",
|
|
33
|
+
DECLARE: "DECLARE",
|
|
33
34
|
// Keywords — control flow
|
|
34
35
|
IF: "IF",
|
|
35
36
|
ELSE: "ELSE",
|
|
@@ -166,6 +167,7 @@ var require_lexer = __commonJS({
|
|
|
166
167
|
val: T.VAL,
|
|
167
168
|
fn: T.FN,
|
|
168
169
|
return: T.RETURN,
|
|
170
|
+
declare: T.DECLARE,
|
|
169
171
|
if: T.IF,
|
|
170
172
|
else: T.ELSE,
|
|
171
173
|
for: T.FOR,
|
|
@@ -1041,6 +1043,8 @@ var require_parser = __commonJS({
|
|
|
1041
1043
|
if (decorators.length) n.decorators = decorators;
|
|
1042
1044
|
return n;
|
|
1043
1045
|
}
|
|
1046
|
+
case T.DECLARE:
|
|
1047
|
+
return this.parseDeclareDecl();
|
|
1044
1048
|
case T.IF:
|
|
1045
1049
|
return this.parseIf();
|
|
1046
1050
|
case T.FOR:
|
|
@@ -1511,6 +1515,48 @@ var require_parser = __commonJS({
|
|
|
1511
1515
|
}
|
|
1512
1516
|
this.err("Expected -> or : after function signature");
|
|
1513
1517
|
}
|
|
1518
|
+
// ── declare fn/val/var/class ───────────────────────────────────
|
|
1519
|
+
parseDeclareDecl() {
|
|
1520
|
+
const loc = this.eat(T.DECLARE);
|
|
1521
|
+
const tok = this.peek();
|
|
1522
|
+
if (tok.type === T.FN || tok.type === T.ASYNC) {
|
|
1523
|
+
const isAsync = tok.type === T.ASYNC;
|
|
1524
|
+
this.skip();
|
|
1525
|
+
if (isAsync) this.eat(T.FN);
|
|
1526
|
+
const KEYWORD_AS_NAME = /* @__PURE__ */ new Set([T.NEW, T.DELETE, T.FROM, T.AS, T.DEFAULT, T.IS, T.IN, T.TYPE]);
|
|
1527
|
+
const name = this.check(T.IDENT) || KEYWORD_AS_NAME.has(this.peek().type) ? this.skip().value : null;
|
|
1528
|
+
const params = this.parseParamList();
|
|
1529
|
+
let retType = null;
|
|
1530
|
+
if (this.check(T.ARROW)) {
|
|
1531
|
+
this.pos++;
|
|
1532
|
+
retType = this.parseTypeAnn();
|
|
1533
|
+
} else if (this.check(T.COLON)) {
|
|
1534
|
+
this.pos++;
|
|
1535
|
+
retType = this.parseTypeAnn();
|
|
1536
|
+
}
|
|
1537
|
+
this.skipNewlines();
|
|
1538
|
+
const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: isAsync, loc: tok };
|
|
1539
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1540
|
+
}
|
|
1541
|
+
if (tok.type === T.VAL || tok.type === T.VAR) {
|
|
1542
|
+
const kind = tok.type === T.VAR ? "var" : "val";
|
|
1543
|
+
this.skip();
|
|
1544
|
+
const name = this.eat(T.IDENT).value;
|
|
1545
|
+
let typeAnn = null;
|
|
1546
|
+
if (this.maybe(T.COLON)) typeAnn = this.parseTypeAnn();
|
|
1547
|
+
this.skipNewlines();
|
|
1548
|
+
const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: tok };
|
|
1549
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1550
|
+
}
|
|
1551
|
+
if (tok.type === T.CLASS) {
|
|
1552
|
+
this.skip();
|
|
1553
|
+
const name = this.eat(T.IDENT).value;
|
|
1554
|
+
this.skipNewlines();
|
|
1555
|
+
const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: tok };
|
|
1556
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1557
|
+
}
|
|
1558
|
+
this.err("Expected fn, async fn, val, var, or class after declare");
|
|
1559
|
+
}
|
|
1514
1560
|
// ── async fn ──────────────────────────────────────────────────
|
|
1515
1561
|
parseAsyncFn() {
|
|
1516
1562
|
this.eat(T.ASYNC);
|
|
@@ -2622,6 +2668,9 @@ function _fmt(v, s) {
|
|
|
2622
2668
|
return this.genInterfaceDecl(node);
|
|
2623
2669
|
case "EnumDecl":
|
|
2624
2670
|
return this.genEnumDecl(node);
|
|
2671
|
+
case "DeclareDecl":
|
|
2672
|
+
return;
|
|
2673
|
+
// ambient declaration — no JS output
|
|
2625
2674
|
case "ExprStmt":
|
|
2626
2675
|
return this.emit(this.genExpr(node.expr) + ";");
|
|
2627
2676
|
default:
|
|
@@ -5624,6 +5673,20 @@ var require_type_checker = __commonJS({
|
|
|
5624
5673
|
}
|
|
5625
5674
|
break;
|
|
5626
5675
|
}
|
|
5676
|
+
case "DeclareDecl": {
|
|
5677
|
+
const d = node.decl;
|
|
5678
|
+
if (d.type === "FnDecl") {
|
|
5679
|
+
const retType = d.retType ? parseAnnotation(d.retType) : null;
|
|
5680
|
+
const paramTypes = d.params.map((p) => p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN);
|
|
5681
|
+
if (d.name) env.set(d.name, T_FN(paramTypes, retType || T_UNKNOWN));
|
|
5682
|
+
} else if (d.type === "VarDecl") {
|
|
5683
|
+
const t = d.typeAnn ? parseAnnotation(d.typeAnn) : T_UNKNOWN;
|
|
5684
|
+
env.set(d.name, t);
|
|
5685
|
+
} else if (d.type === "ClassDecl") {
|
|
5686
|
+
env.set(d.name, T_NAMED(d.name));
|
|
5687
|
+
}
|
|
5688
|
+
break;
|
|
5689
|
+
}
|
|
5627
5690
|
case "TypeDecl":
|
|
5628
5691
|
for (const v of node.variants) {
|
|
5629
5692
|
if (v.fields.length === 0) env.set(v.name, T_NAMED(node.name));
|
package/dist/flux.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* flux-lang
|
|
2
|
+
* flux-lang v4.0.0
|
|
3
3
|
* Flux — A modern language that transpiles to JavaScript. Python-clean syntax, TypeScript-level safety, Rust-inspired pattern matching.
|
|
4
4
|
* (c) 2026 Flux Lang Contributors
|
|
5
5
|
* Released under the MIT License
|
|
@@ -35,6 +35,7 @@ var require_lexer = __commonJS({
|
|
|
35
35
|
VAL: "VAL",
|
|
36
36
|
FN: "FN",
|
|
37
37
|
RETURN: "RETURN",
|
|
38
|
+
DECLARE: "DECLARE",
|
|
38
39
|
// Keywords — control flow
|
|
39
40
|
IF: "IF",
|
|
40
41
|
ELSE: "ELSE",
|
|
@@ -171,6 +172,7 @@ var require_lexer = __commonJS({
|
|
|
171
172
|
val: T.VAL,
|
|
172
173
|
fn: T.FN,
|
|
173
174
|
return: T.RETURN,
|
|
175
|
+
declare: T.DECLARE,
|
|
174
176
|
if: T.IF,
|
|
175
177
|
else: T.ELSE,
|
|
176
178
|
for: T.FOR,
|
|
@@ -1046,6 +1048,8 @@ var require_parser = __commonJS({
|
|
|
1046
1048
|
if (decorators.length) n.decorators = decorators;
|
|
1047
1049
|
return n;
|
|
1048
1050
|
}
|
|
1051
|
+
case T.DECLARE:
|
|
1052
|
+
return this.parseDeclareDecl();
|
|
1049
1053
|
case T.IF:
|
|
1050
1054
|
return this.parseIf();
|
|
1051
1055
|
case T.FOR:
|
|
@@ -1516,6 +1520,48 @@ var require_parser = __commonJS({
|
|
|
1516
1520
|
}
|
|
1517
1521
|
this.err("Expected -> or : after function signature");
|
|
1518
1522
|
}
|
|
1523
|
+
// ── declare fn/val/var/class ───────────────────────────────────
|
|
1524
|
+
parseDeclareDecl() {
|
|
1525
|
+
const loc = this.eat(T.DECLARE);
|
|
1526
|
+
const tok = this.peek();
|
|
1527
|
+
if (tok.type === T.FN || tok.type === T.ASYNC) {
|
|
1528
|
+
const isAsync = tok.type === T.ASYNC;
|
|
1529
|
+
this.skip();
|
|
1530
|
+
if (isAsync) this.eat(T.FN);
|
|
1531
|
+
const KEYWORD_AS_NAME = /* @__PURE__ */ new Set([T.NEW, T.DELETE, T.FROM, T.AS, T.DEFAULT, T.IS, T.IN, T.TYPE]);
|
|
1532
|
+
const name = this.check(T.IDENT) || KEYWORD_AS_NAME.has(this.peek().type) ? this.skip().value : null;
|
|
1533
|
+
const params = this.parseParamList();
|
|
1534
|
+
let retType = null;
|
|
1535
|
+
if (this.check(T.ARROW)) {
|
|
1536
|
+
this.pos++;
|
|
1537
|
+
retType = this.parseTypeAnn();
|
|
1538
|
+
} else if (this.check(T.COLON)) {
|
|
1539
|
+
this.pos++;
|
|
1540
|
+
retType = this.parseTypeAnn();
|
|
1541
|
+
}
|
|
1542
|
+
this.skipNewlines();
|
|
1543
|
+
const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: isAsync, loc: tok };
|
|
1544
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1545
|
+
}
|
|
1546
|
+
if (tok.type === T.VAL || tok.type === T.VAR) {
|
|
1547
|
+
const kind = tok.type === T.VAR ? "var" : "val";
|
|
1548
|
+
this.skip();
|
|
1549
|
+
const name = this.eat(T.IDENT).value;
|
|
1550
|
+
let typeAnn = null;
|
|
1551
|
+
if (this.maybe(T.COLON)) typeAnn = this.parseTypeAnn();
|
|
1552
|
+
this.skipNewlines();
|
|
1553
|
+
const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: tok };
|
|
1554
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1555
|
+
}
|
|
1556
|
+
if (tok.type === T.CLASS) {
|
|
1557
|
+
this.skip();
|
|
1558
|
+
const name = this.eat(T.IDENT).value;
|
|
1559
|
+
this.skipNewlines();
|
|
1560
|
+
const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: tok };
|
|
1561
|
+
return { type: "DeclareDecl", decl, loc };
|
|
1562
|
+
}
|
|
1563
|
+
this.err("Expected fn, async fn, val, var, or class after declare");
|
|
1564
|
+
}
|
|
1519
1565
|
// ── async fn ──────────────────────────────────────────────────
|
|
1520
1566
|
parseAsyncFn() {
|
|
1521
1567
|
this.eat(T.ASYNC);
|
|
@@ -2627,6 +2673,9 @@ function _fmt(v, s) {
|
|
|
2627
2673
|
return this.genInterfaceDecl(node);
|
|
2628
2674
|
case "EnumDecl":
|
|
2629
2675
|
return this.genEnumDecl(node);
|
|
2676
|
+
case "DeclareDecl":
|
|
2677
|
+
return;
|
|
2678
|
+
// ambient declaration — no JS output
|
|
2630
2679
|
case "ExprStmt":
|
|
2631
2680
|
return this.emit(this.genExpr(node.expr) + ";");
|
|
2632
2681
|
default:
|
|
@@ -5629,6 +5678,20 @@ var require_type_checker = __commonJS({
|
|
|
5629
5678
|
}
|
|
5630
5679
|
break;
|
|
5631
5680
|
}
|
|
5681
|
+
case "DeclareDecl": {
|
|
5682
|
+
const d = node.decl;
|
|
5683
|
+
if (d.type === "FnDecl") {
|
|
5684
|
+
const retType = d.retType ? parseAnnotation(d.retType) : null;
|
|
5685
|
+
const paramTypes = d.params.map((p) => p.typeAnn ? parseAnnotation(p.typeAnn) : T_UNKNOWN);
|
|
5686
|
+
if (d.name) env.set(d.name, T_FN(paramTypes, retType || T_UNKNOWN));
|
|
5687
|
+
} else if (d.type === "VarDecl") {
|
|
5688
|
+
const t = d.typeAnn ? parseAnnotation(d.typeAnn) : T_UNKNOWN;
|
|
5689
|
+
env.set(d.name, t);
|
|
5690
|
+
} else if (d.type === "ClassDecl") {
|
|
5691
|
+
env.set(d.name, T_NAMED(d.name));
|
|
5692
|
+
}
|
|
5693
|
+
break;
|
|
5694
|
+
}
|
|
5632
5695
|
case "TypeDecl":
|
|
5633
5696
|
for (const v of node.variants) {
|
|
5634
5697
|
if (v.fields.length === 0) env.set(v.name, T_NAMED(node.name));
|