A Java/C#-flavored language that transpiles to Rust. No VM, no garbage collector at runtime. Your code compiles down to a native machine-code binary through rustc, and you get Rust's optimizer and safety guarantees for free.
β οΈ This is experimental. It is a hobby, a personal project, a work in progress. It will have bugs. The docs will sometimes contradict each other because there's a lot of them and I'm one person. Things will break, change, and get rewritten. If that scares you, come back in a year. If it sounds fun, keep reading.
I started sketching this idea back in 2019, right when COVID hit and the days stuck at home got long and boring. I wanted a language that felt like the ones I already knew, Java and C#, but that didn't drag a virtual machine around with it.
I've tried to build it more than once. First in Java. Then in Dart. Both times I learned a lot and both times I hit a wall. This is the third attempt, and this time I went with Rust as the foundation, because honestly it's one of the best tools out there right now for this kind of work. It compiles to fast native code, the borrow checker catches a whole category of bugs before they ship, and the crate ecosystem is enormous.
So instead of fighting Rust, Jux stands on top of it. Jux code is translated to
readable Rust source, and then rustc does the heavy lifting. That means Jux gets
the good parts of Rust under the hood while wearing a syntax that someone coming
from Java, C#, or even Rust itself can pick up without much friction.
A few things I want to be straight about:
- I'm not an expert. I'm a developer who's been at this a while and decided to stop wishing this language existed and actually build it.
- This is built by a solo dev (me) with help from AI. To be clear: the AI helps with research and grinding through steps, but it does not make the decisions. Every direction, every design call, every "no, do it this way" is mine. I drive; it assists.
- I'm not trying to replace Rust or compete with anything. This is a hobby that might turn into something useful for other people too. That's the whole ambition.
- It's made with love and a stupid amount of dedication, and even if it sucks right now, I'm genuinely happy it's at the stage it's at.
Write in a familiar, Java-shaped language. Get a native binary. Use Rust's standard
library as your standard library. Pull in any crate on crates.io and call it with
Jux syntax. Pull in other Jux libraries straight from GitHub as dependencies. Build
real frameworks with annotations. Talk to C and C++ through FFI. And let rustc
optimize all of it to the metal.
That's the goal. Some of it works today, some of it is half-built, some of it is still on paper. I'll be honest below about which is which.
On this page:
screenshots Β·
a taste of Jux Β·
the language tour Β·
friendly to Java hands Β·
what works today Β·
how Jux uses Rust Β·
how the lowering works Β·
how Jux is tested Β·
getting started Β·
jux.toml Β·
commands
This is one of those screenshots I'm going to keep forever. It isn't a benchmark or a clever language trick, it's a tiny empty window with a title bar, sitting on the desktop like it owns the place. And it was drawn by a program written in Jux.
Look at what's actually happening here. The editor on the left is plain Jux: a
package, a couple of imports, a public void main(). It builds a window, pumps an
event loop with while (window.is_open()), and listens for a keypress, printing
"Enter was pressed" every time you hit Enter. The console at the bottom is that loop
running for real, line after line, as a native executable. No VM warming up in the
background, no interpreter, no runtime stitched on. Jux lowered that source to Rust,
rustc turned it into machine code, and the operating system handed it a real
window.
What makes it click is how little it took. The window isn't some bespoke widget
toolkit I had to build into the language. It's a Rust crate doing the drawing,
the fltk GUI bindings, pulled in as a dependency
and called with ordinary Jux syntax. Those import rust.fltk.Window lines at the
top of the file are the whole trick: a crate from crates.io, surfaced as Jux types,
constructing a real native window. It's the exact same "Rust's ecosystem is your
ecosystem" promise from the pitch above, except now you can see it. The day a
Java-shaped language I'd been chasing since 2019 popped up its first native window
by borrowing the entire Rust crate world, the whole thing stopped feeling like a
toy and started feeling like a tool.
It's empty on purpose. Empty windows are where every UI story starts. This one just happens to be the first one that started in Jux.
That empty window was 2024's milestone. This is a fleet dashboard, written
entirely in Jux, running as a native binary: three views, a clickable host
table, hover highlighting, a switchable palette, and a live feed updating ten
times a second. It lives in the repo at
examples/metrics_console and you can run it now:
cd examples/metrics_console
jux run --release -p desktopNothing here is a widget toolkit. There is no UI framework, no layout engine,
and no image library. The console owns a Vec<int> of 0xRRGGBB pixels and
draws into it, one rectangle at a time, with a 5x7 bitmap font written out as
pictures in the source. rust.minifb from crates.io puts the buffer on screen
and reports the mouse; everything above that line is Jux.
Overview is the fleet at a glance. Click any row and it selects that host.
Hosts compares them: every host, all three readings side by side.
Detail is one host in full, each chart graded against its own ceiling.
And the palette is a chip in the tab bar, because a dashboard should not need a config file to be readable in daylight:
Input handling is the part worth looking at. A widget that can be clicked records the rectangles it drew into a shared hit map, tagged with what they mean, and knows nothing else:
this.hits.add(x - 4, ry - 2, w + 8, rowHeight, "host:" + host.name());The console asks the map what is under a point and turns the answer into state. So the window front end forwards two integers and no more:
var mouse = window.get_mouse_pos(MouseMode.Clamp);
if (mouse != null) {
var (fx, fy) = mouse!!;
console.hover((int) fx, (int) fy);
if (down && !wasDown && console.click((int) fx, (int) fy)) { ... }
}Because nothing under the renderer knows a window exists, the same console also runs headless, writing frames to a file. Which means the interaction is testable with no display and no mouse attached:
jux run -- --view hosts --snapshot frame.ppm
jux run -- --click 258 28Ten tests drive it that way on every build: every view, every click target, both palettes, and the windowed binary compiled.
demo.core containers and contracts, generic in the sample type
demo.model a fleet, how bad a reading is, and a fleet that moves
demo.render pixels, glyphs, widgets, hit regions, layout
demo.app arguments, and where the frame goes
demo.desktop the same frame, in a window
Each is its own package with its own jux.toml. The dependency edges run one
way, and jux build walks them in order. Aggregate<T> is a generic
interface with a default method; Ring<T>, Series<T> and Table<K, V> are
generic classes used across package boundaries; Severity and View are
enums with methods and a switch over themselves.
And it earned its place a second way. Writing it found nine compiler
bugs that no single-file test could reach: a package named demo.core
shadowing Rust's own core crate, interface default-method bodies that were
never type-checked, a ternary over two objects that moved both arms, a
constructor that could not hold a class. Every one is fixed. That is the real
argument for building something big in a young language: the program tells you
what is broken far better than a test suite you wrote from imagination.
The metrics console proved a Jux program can be an application. This one asks
the harder question: can it be a responsive one? Same rules as before. No UI
framework, no layout engine, no drawing library.
examples/dashboard is integer arithmetic writing
0x00RRGGBB pixels into a buffer: rounded panels, filled line charts, bar
tracks, gauge rings, and a 5x7 bitmap font that lives in the source as plain
numbers.
cd examples/dashboard
jux run # live window, resizable
jux run -- --snapshot frame.ppm --size 900x620 # one frame to a fileLayout is the only code in the whole program that decides where anything
goes. It reads the surface size, picks a breakpoint, and hands every widget the
rectangle it has to fit inside:
| Breakpoint | Width | Stat tiles | Charts | Side column |
|---|---|---|---|---|
| Wide | 1080+ | 4 across | 2 columns | yes |
| Medium | 720-1079 | 2 across | 1 column | yes, narrower |
| Compact | under 720 | stacked | 1 column | dropped |
Drag the window edge and the four stat tiles become two and then one, the
charts collapse into a single column, and the side rail drops away. No widget
knows the window resized; they are simply asked to draw somewhere else. The
header prints the breakpoint it chose beside the surface size and the frame
counter, the WIDE 1571X789 FRAME 2199 in the shot above, so the rearrangement
is something you can see rather than something I claim.
The dash package still knows nothing about windows. That is what lets the
same frame go to a resizable rust.minifb window or straight to a file, and it
is why a test can render three different sizes headless and check the layout
really does respond.
Writing it found seven more compiler bugs, none of them about dashboards. A
cast on the left of a shift did not compile, and r as u32 << 16 is every
pixel-packing routine ever written. char arithmetic skipped promotion when
one operand was a literal, so ch - '0' was wrong. A for-each variable had
no type unless it was a class. An early return; in a constructor returned
nothing. A static call moved its arguments, so Font.draw(text, Font.width(text))
failed over a value the program never gave away. All seven are fixed.
If you've written Java or C#, none of this needs a tutorial:
public abstract class Animal {
public String name;
public Animal(String name) { this.name = name; }
public abstract String sound();
}
public interface Tagged { String tag(); }
public class Dog extends Animal {
public Dog(String name) { super(name); }
public String sound() { return "Woof"; }
public String fetch() { return "fetched"; }
}
public class Cat extends Animal implements Tagged {
public Cat(String name) { super(name); }
public String sound() { return "Meow"; }
public String tag() { return "cat-tag"; }
}
public void main() {
Animal a = new Dog("Rex");
a.name = "Max"; // field access through a base reference
print(a.sound()); // virtual dispatch, prints "Woof"
Dog d = a as Dog; // explicit downcast
print(d.fetch());
Animal b = new Cat("Felix");
if (b => Cat) { // `=>` is the instanceof / type-test operator
Cat c = b as Cat;
print($"${c.name} says ${c.sound()} / ${c.tag()}");
}
}Familiar shape, but the semantics are Jux's own, and all of it compiles straight to
native code through Rust. (This is a real example;
see examples/downcast_typetest.jux.)
A tour of the stuff that makes Jux fun to write. Everything below is real,
compiling syntax (most of it lifted straight out of examples/).
public struct Vec2 {
public double x = 0.0; // a default value, used when a constructor omits it
public double y = 0.0;
public double lengthSquared() { return x * x + y * y; }
}
// A generic container, instantiated with a turbofish or by inference.
public class Box<T> {
private T value;
public Box(T value) { this.value = value; }
public T get() { return this.value; }
}
// Bounded type parameter: T must be an Animal AND implement Speaks.
public class Holder<T extends Animal & Speaks> {
public T pet;
public Holder(T pet) { this.pet = pet; }
public String describe() { return this.pet.voice(); }
}
var b = new Box<int>(42); // explicit type argument
var v = new Vec2(3.0, 4.0); // a struct's implicit constructor takes every fieldOne enum keyword covers three jobs: a C-style set of named constants, a
Java-style enum with methods and fields, and a Rust-style sum type whose variants
carry payloads. You destructure them with switch and exhaustiveness is checked,
so a forgotten variant is a compile error, not a runtime surprise.
public enum Shape {
Circle(double), // payload-carrying variants
Rect(double, double)
}
public double area(Shape s) {
switch (s) {
case Shape.Circle(var r) -> { return 3.14159 * r * r; }
case Shape.Rect(var w, var h) -> { return w * h; }
}
}Variants may name the enum itself, so the classic recursive trees (expressions, JSON, parse trees) just work. The self-referential slot is heap-indirected for you; there is no special syntax and no manual boxing:
public enum Expr {
Num(int),
Add(Expr, Expr), // self-referential payload
Mul(Expr, Expr)
}
public int eval(Expr e) {
switch (e) {
case Expr.Num(var n) -> { return n; }
case Expr.Add(var a, var b) -> { return eval(a) + eval(b); }
case Expr.Mul(var a, var b) -> { return eval(a) * eval(b); }
}
}
// (2 + 3) * 4 == 20
var tree = Expr.Mul(Expr.Add(Expr.Num(2), Expr.Num(3)), Expr.Num(4));
print(eval(tree));How Jux enums stack up against C and Java:
| Feature | C enum | Java enum | Jux enum |
|---|---|---|---|
| Named variants | β | β | β |
| Methods | β | β | β |
| Payloads on variants | β | β | β |
| Recursive variants (trees) | β | β | β |
| Pattern matching with destructuring | β | partial | β |
| Exhaustiveness check | β | β | β |
name(), ordinal(), values() |
β | β | β |
| FFI bit-compatible | native | β | via @layout(c) |
Auto-derived ==, hash, string |
n/a | identity | β structural |
| Sealed (closed set of variants) | β | β | β |
Same keyword does it all: you pick the pattern that fits the case, and the compiler picks the representation that fits the pattern.
Overload arithmetic, equality, hashing, and the string conversion. operator==
must be paired with operator hash (the compiler enforces it with E0931).
public class Money {
public int cents;
public Money(int cents) { this.cents = cents; }
public Money operator+(Money other) { return new Money(this.cents + other.cents); }
public Money operator-(Money other) { return new Money(this.cents - other.cents); }
public bool operator==(Money other) { return this.cents == other.cents; }
public int operator hash() { return this.cents; }
public String operator string() { return $"$${this.cents}"; }
}
var total = new Money(150) + new Money(50); // both operands stay usable afterward
print($"total=$total"); // total=$200You can also overload the subscript operator [], the same way C# has indexers
and C++ has operator[]. It comes as a read/write pair: operator[] defines
what w[i] returns, and operator[]= defines what w[i] = v does. That lets a
class expose clean array-style access while keeping its storage private.
import rust.std.Vec;
public class Wallet {
private Vec<int> slots; // private backing store
public Wallet() {
this.slots = new Vec<int>();
this.slots.push(10);
this.slots.push(20);
}
// index read: evaluated for w[i]
public int operator[](int i) { return this.slots[i]; }
// index write: evaluated for w[i] = v
public void operator[]=(int i, int v) { this.slots[i] = v; }
}
public void main() {
var w = new Wallet();
print(w[0]); // 10 calls operator[]
w[1] = 99; // calls operator[]=
w[0] += w[1]; // both! reads w[0] and w[1], then writes back
print(w[0]); // 109
}That last line is the fun one: a compound assignment through an indexer fires
both operators in a single statement, the getter to read and the setter to
write back. The operator[] / operator[]= bodies lower to inherent methods, and
w[i] at the call site maps onto Rust's Index/IndexMut shape, so the emitted
Rust still reads naturally.
public type UserId = long; // transparent alias
public type Predicate = (int) -> bool; // function-type alias
public void main() {
UserId id = 42;
print(sizeof(UserId)); // 8 (compile-time size query)
print(typeof(id)); // long
Predicate even = (n) -> n % 2 == 0;
print(even(10)); // true
}Pull several names from one package with brace syntax, just like the frameworks Jux is built to host:
import rust.std.{HashMap, HashSet};
import juxweb.{Server, Controller, Route, PathParam};Properties read and write like fields, but they can be computed, bound to one another, and observed. This is one of Jux's signature features.
public class Person {
public String First { get; set; } = "";
public String Last { get; set; } = "";
// Computed property: re-fires whenever First or Last changes.
public String FullName { get -> First + "/" + Last; };
}
public class Source { public int Value { get; set; } = 10; }
public class Mirror {
public int Shown { get; set; } = 0;
public Mirror(Source s) { this.Shown.bind(s.Value); } // one-way binding
}
var p = new Person();
p.FullName.observers.attach((old, now) -> print($"name: $old -> $now"));
p.First = "Ada"; // fires the observer
p.Last = "Lovelace"; // fires again
var s = new Source();
var m = new Mirror(s); // m.Shown starts synced to s.Value
s.Value = 42; // m.Shown now follows to 42
a.Value.bindBidirectional(b.Value); // two-way; either side updates the other
a.Value.unbind(); // break the bindingasync/await lower to real Rust futures; spawn launches a concurrent task and
Channel<T> gives you a bounded producer/consumer pipe.
async void pipeline() {
var ch = new Channel<int>(4);
spawn(async () -> {
for (var i : 1..=5) { await ch.send(i * 10); }
ch.close();
});
var total = 0;
while (true) {
var item = await ch.receive(); // null once closed and drained
if (item == null) { break; }
total = total + item!!; // `!!` unwraps a nullable
}
print(total); // 150
}
public void main() { block_on(pipeline()); }When you want genuine multi-core parallelism (not just cooperative tasks),
Worker.spawn runs a closure on a real OS thread and hands you back a Task you
can await. Shared state goes through atomics like AtomicInt, which are safe to
hand to several workers at once.
import jux.std.concurrent.AtomicInt;
public int crunch(String tag, int iters) {
var acc = 0;
for (var i : 0..iters) { acc = (acc + i) * 7 % 9973; }
print($" [worker $tag] acc=$acc");
return acc;
}
public async void main() {
// Fan CPU-bound work out across real threads, then gather the results.
final var a = Worker.spawn(() -> crunch("A", 1_000_000));
final var b = Worker.spawn(() -> crunch("B", 1_000_000));
// One shared, thread-safe counter that every worker bumps.
var hits = new AtomicInt(0);
final var c = Worker.spawn(() -> {
for (var i : 0..1000) { hits.fetchAdd(1); } // atomic increment
return 0;
});
final int ra = await a;
final int rb = await b;
await c;
print($"results: A=$ra B=$rb");
print($"hits = ${hits.load()}"); // 1000
}Worker.spawn is true preemptive parallelism backed by OS threads; AtomicInt
(and friends, with explicit MemoryOrder) lower to Arc<Atomic*>, so the handle
the workers share really is one counter.
Jux has no tracing garbage collector. Class instances are reference-counted (that
Rc<RefCell> lowering from earlier), and three constructs give you direct control
over lifetime and sharing.
drop { } is a deterministic destructor. It runs at scope exit for a local,
and for a class instance exactly once, when the last strong reference is released.
No finalizer queue, no nondeterminism.
public class Resource {
public String name;
public Resource(String name) { this.name = name; print("open " + name); }
drop { print("close " + name); } // `this` is in scope here
}
public void main() {
var a = new Resource("a");
var b = a; // a SECOND handle to the same resource
print("using " + b.name);
} // "close a" prints once here, when the last handle diesweak breaks reference cycles. A weak reference doesn't bump the refcount,
so it never keeps its target alive. Promote it to a strong reference with .get(),
which returns T? (null if the target is gone). This is how a Parent <-> Child
back-reference avoids leaking without a GC.
public class Child {
private weak Parent parent; // no refcount contribution
public void attach(Parent p) { this.parent = p; }
public void callUp() {
var p = this.parent.get(); // Parent? promote weak -> strong
if (p != null) { p.greet(); } else { print("(no parent)"); }
}
}ref gives you a shared, writable handle to a value type. Normally primitives
and value types copy; a ref binding (or ref parameter) aliases the same cell,
so a write is visible through every handle, including the caller's.
void bump(ref int n) { n += 5; } // mutates the CALLER's variable
public void main() {
ref int n = 10;
ref int m = n; // m aliases n's cell
bump(n);
print(m); // 15 (same cell throughout)
}Fields and structs work the same way. A ref field (including a static one)
hands its own cell to a ref parameter, so the callee writes the field itself,
and a write through a ref struct (q.x = 7) is seen by every alias. Passing a
plain value to a ref parameter is still a copy: sharing is something you opt
into on both sides. ref on a class, interface, array or collection is accepted
and does nothing, since those are already shared, and says so (W0490).
See examples/ref_semantics_full.jux.
unsafe { } blocks, unsafe functions, and the raw-pointer basics work today:
T* types, address-of a local (&n), and dereference (*p), all gated to an
unsafe context (the type checker raises E0506 outside one). The example below
compiles and runs:
public unsafe void store(int* p, int value) { *p = value; }
public void main() {
int n = 10;
unsafe {
int* p = &n;
store(p, *p * 2);
}
print($"n = $n"); // n = 20
}Taking the address of an object (&obj) works too, and pointers can live in
fields. Freeing memory needs no delete keyword by design: you call the foreign
deallocator inside unsafe, from a drop { } destructor (writing delete p;
is guided there by a diagnostic).
A record is an immutable data carrier. Equality, hashing and printing come for
free, a compact constructor validates or normalizes the components before they
are stored, and extra constructors chain to the canonical one with this(...).
record Range(int lo, int hi) {
Range { // compact constructor
if (lo > hi) { int t = lo; lo = hi; hi = t; } // normalize, don't reject
}
Range(int single) { this(single, single); }
public int length() { return hi - lo; }
}
record Temperature(double celsius) {
Temperature {
if (celsius < -273.15) {
throw new IllegalArgumentException($"below absolute zero: ${celsius}");
}
}
}
print(new Range(9, 2)); // Range(lo: 2, hi: 9)
print(new Range(3, 8).length()); // 5Need a changed copy? with(...) builds one, naming only the components that
change, and it nests:
record Address(String city, String country) {}
record User(String name, Address addr) {}
var u = new User("Alice", new Address("Paris", "FR"));
var u2 = u.with(addr: u.addr.with(city: "Lyon")); // u is untouchedA sealed interface closes its set of implementations, so a switch over it is
checked for exhaustiveness with no default. Patterns take records apart in the
same step that tests their type, can hold literals, and can carry a when guard.
sealed interface Shape permits Circle, Square, Triangle {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
record Triangle(double base, double height) implements Shape {}
String describe(Shape s) {
return switch (s) {
case Circle(0.0) -> "a point"; // literal inside a pattern
case Circle(var r) when r > 100.0 -> "a huge circle"; // guard
case Circle(var r) -> $"a circle of radius ${r}";
case Square sq -> $"a square of side ${sq.side}"; // type pattern with a binder
case Triangle t -> "a triangle";
}; // no default: the set is closed
}Or-patterns bind names too. When every alternative binds the same name with the same type, the arm body sees one variable, whichever alternative matched:
sealed interface Expr permits Num, Neg, Twice {}
record Num(int v) implements Expr {}
record Neg(int v) implements Expr {}
record Twice(int v) implements Expr {}
int magnitude(Expr e) {
return switch (e) {
case Num(var n) | Neg(var n) -> n;
case Twice(var n) -> 2 * n;
};
}
String pair((int, int) p) { // tuples match the same way
return switch (p) {
case (0, var y) | (var y, 0) -> "axis " + y;
case (var a, var b) when a == b -> "diag " + a;
default -> "other";
};
}Tuples are anonymous, fixed-size value groups: handy for returning two things
without inventing a class. A declaration can take tuples and records apart, at any
depth, with _ to skip a part:
(int, int) divmod(int a, int b) { return (a / b, a % b); }
record Pt(int x, int y) {}
record Line(Pt start, Pt end) {}
public void main() {
var (q, r) = divmod(17, 5); // 3, 2
var pair = divmod(22, 7);
print(pair.0 + pair.1);
var l = new Line(new Pt(1, 2), new Pt(3, 4));
var Line(Pt(ax, ay), Pt(bx, by)) = l; // nested, one line
var Line(Pt(_, var y1), var end) = l; // skip parts, keep a whole record
print((bx - ax) * (by - ay));
}A type is non-null unless it says otherwise. T? may be null, and the compiler
will not let you use it as a T until you have checked it. The checks you would
write anyway are the ones it understands:
String require(String? s) {
return s ?: throw new IllegalArgumentException("missing value"); // s is a String after this
}
int total(Node? head) {
Node? cur = head;
int sum = 0;
while (cur != null) { // cur is a Node inside the loop
sum += cur.value;
cur = cur.next;
}
return sum;
}
public void main() {
int? present = 5;
print(present ?? expensive()); // `??` short-circuits: expensive() never runs here
Node? gone = null;
var r = gone?.step(); // `?.` skips the whole call on null
Animal pet = new Dog();
if (pet => Dog) print(pet.bark()); // `=>` type test, then pet IS a Dog in the branch
String name = maybeName()!!; // `!!` asserts non-null (throws if it is null)
}Arithmetic, comparison, equality, hashing, indexing, ranges and text are all
operator overrides, never magic method names. There is no equals,
hashCode, compareTo or toString to remember: you override operator==,
operator hash, operator<=> and operator string.
One operator, several operand types. The right operand picks the overload the way a call picks a method, and each overload has its own return type:
class Vec2 {
public double x;
public double y;
public Vec2(double x, double y) { this.x = x; this.y = y; }
public Vec2 operator*(double k) { return new Vec2(x * k, y * k); } // scale
public double operator*(Vec2 other) { return x * other.x + y * other.y; } // dot product
public Vec2 operator+(Vec2 other) { return new Vec2(x + other.x, y + other.y); }
public String operator string() { return $"(${x}, ${y})"; }
}
var a = new Vec2(1.0, 2.0);
var b = new Vec2(3.0, 4.0);
Vec2 doubled = a * 2.0; // (2.0, 4.0)
double dot = a * b; // 11.0
a *= 10.0; // compound assignment picks the same way<=> gives you the whole ordering. Define one three-way comparison and
<, <=, >, >= all follow, for your own types and for sorting:
class Version {
private int major;
private int minor;
Version(int major, int minor) { this.major = major; this.minor = minor; }
public int operator <=>(Version other) {
if (this.major != other.major) { return this.major <=> other.major; }
return this.minor <=> other.minor;
}
}
print(new Version(1, 9) < new Version(2, 0)); // trueOperators in interfaces, used as generic bounds. An interface can declare an
operator as a contract, and generic code bounded by it can simply write a + b:
interface Addable<T> { T operator+(T other); }
interface Scalable<T> { T operator*(double k); }
<T extends Addable<T>> T sum(Vec<T> items, T zero) {
var total = zero;
for (var item : items) { total = total + item; }
return total;
}
<T extends Addable<T> & Scalable<T>> T midpoint(T a, T b) {
return (a + b) * 0.5; // two bounds, two operators, one expression
}Ranges of your own types. a..b and a..=b call operator.. / operator..=,
so a for-each can walk dates, versions or anything else you define:
record Date(int day) {
public Vec<Date> operator..=(Date end) {
var out = new Vec<Date>();
for (int d = day; d <= end.day; d++) { out.push(new Date(d)); }
return out;
}
}
for (var d : new Date(10)..=new Date(12)) { print(d.day); } // 10, 11, 12A function that yields returns an Iterator<T> and runs only as far as the
caller asks, so endless sequences are fine. yield* hands over to another
iterator, which makes a recursive tree walk a few lines long. An async
generator returns a Stream<T> you read with for await.
Iterator<long> fibonacci() {
long a = 0;
long b = 1;
while (true) { // endless, and that's fine
yield a;
var next = a + b;
a = b;
b = next;
}
}
Iterator<int> inOrder(Node? node) {
if (node == null) { return; } // `return;` ends the sequence
yield* inOrder(node.left);
yield node.value;
yield* inOrder(node.right);
}
Iterator<int> evens(Iterator<int> source) { // a lazy pipeline stage
for (var n : source) {
if (n % 2 == 0) { yield n; }
}
}Defaults are evaluated at the call site, fresh on every call (no shared mutable default trap), and any argument can be passed by name. It works on functions, methods and constructors alike:
void connect(String host, int port = 80, int timeout = 30) {
print($"$host:$port (t=$timeout)");
}
connect("example.com"); // example.com:80 (t=30)
connect("example.com", port: 443); // example.com:443 (t=30)
connect("example.com", timeout: 60, port: 443); // any order once namedOverloading by parameter type works for free functions and methods, and T...
varargs take any number of arguments (or an array).
try / catch / finally behave like Java's, including checked exceptions:
a body that can throw a checked exception must catch it or declare it with
throws (error E0711 otherwise). Multi-catch handles unrelated types with one
body, and finally always runs first, even when a catch body throws.
class ConfigError extends Exception { ConfigError(String m) { super(m); } }
void load() throws ConfigError { throw new ConfigError("bad host"); }
void risky(int kind) throws NetError, TimeoutError { ... }
public void main() {
try {
load();
} catch (ConfigError e) {
print("recovered: " + e.getMessage());
}
try {
risky(0);
} catch (NetError | TimeoutError e) { // multi-catch
print("transient: " + e.getMessage());
} finally {
print("always runs");
}
}When you'd rather return errors as values, Result<T, E> and the ? operator
propagate them, and ? works on nullables too:
Result<int, String> parsePort(String s) {
if (s == "80") { return Result.Ok(80); }
return Result.Err("bad port: " + s);
}
Result<String, String> describe(String s) {
var port = parsePort(s)?; // Ok unwraps, Err returns early
return Result.Ok("port is " + port);
}@cfg(...) keeps a declaration in or out of the build, and if cfg(...) does
the same inside a body. What a build leaves out is gone, so it may even call
functions that only exist on another target:
@cfg(os = "windows")
public String home() { return "C:\\Users"; }
@cfg(not(os = "windows"))
public String home() { return "/home"; }
public void main() {
if cfg(feature = "verbose") { // features are declared in jux.toml
print("verbose build");
}
}jux test finds every @Test function and runs it, with @BeforeAll,
@BeforeEach, @AfterEach and @AfterAll hooks and the usual assertions,
including assertThrows<E>:
import jux.std.testing.*;
int divide(int a, int b) { return a / b; }
@Test
void numbersAddUp() {
assertEqual(4, 2 + 2);
assertNear(0.3, 0.1 + 0.2, 1e-9);
}
@Test
void divisionByZeroThrows() {
assertThrows<ArithmeticException>(() -> divide(1, 0));
}Jux looks like Java on purpose, so Java habits are the most common mistakes. The compiler recognizes them and tells you the Jux way instead of just saying "no":
app.jux:3:5: [E0413] error: no method `add` on type `rust.std.Vec` -- Jux collections are the Rust std ones: use `push` or `insert`
app.jux:9:11: [E0413] error: no method `equals` on type `Point` -- equality is an operator: write `a == b` (override `operator==` to define it)
app.jux:12:11: [E0413] error: no method `toString` on type `Point` -- a value's text is its `operator string` (override it to define it): interpolate it, `$"${x}"`, or call `x.operator string()`
app.jux:13:11: [E0413] error: no method `getOwner` on type `Account` -- `Owner` is read directly: `x.Owner`, with no call
app.jux:2:5: [E0301] error: cannot find `System` in this scope -- Jux prints with `print(...)`; format with interpolation, `print($"${x}")`
app.jux:4:22: [E0200] error: an array literal is written with braces: `{a, b, c}`, not `[a, b, c]`
Every diagnostic has a code, a precise span and, where there is an obvious fix, a
hint. Errors are Jux's own: the goal is that a mistake in Jux code is reported by
juxc in Jux terms and never leaks through as a confusing rustc error about
code you did not write.
The pipeline runs end to end: lex, parse, resolve, typecheck, lower-to-Rust,
cargo build, run. Roughly anything in examples/ compiles and
runs. That currently includes:
- Classes: fields, constructors, methods,
static/final, visibility, bare field access (fisthis.f), and C#-style properties ({ get; set; }, expression-bodied with->). - Inheritance & polymorphism:
extends,super(...), overrides, abstract classes,sealed/non-sealed, virtual dispatch, downcasts (as/(T)), and the=>instanceof / type-test operator. Classes are shared references (Java semantics), not values. - Interfaces: default methods, static methods, constants. Single-class, multi-interface inheritance, like Java.
- Generics:
class A<T>, bounded type params, wildcards (? extends,? super), const generics (<int N>), explicit type arguments. - Enums +
switchwith exhaustiveness checking, payload binding, and recursive variants (Add(Expr, Expr)self-referential trees, auto-boxed). Enums getname(),ordinal(),fromName(),fromOrdinal()andcases(). - Pattern matching: record patterns, type patterns with binders, literal
patterns,
whenguards, ranges, or-patterns that bind names, tuple patterns, and nested destructuring declarations.sealedinterfaces make aswitchexhaustive with nodefault. - Records (compact and extra constructors,
with(...)), tuples, lambdas & method references (includingasynclambdas), generators (yield/yield*toIterator<T>, async generators toStream<T>). - Operators: arithmetic, comparison,
<=>(which derives<<=>>=),==+hash,string, indexers[]/[]=, compound assignment, free operators, overloads by operand type, operators in interfaces and generic bounds, and../..=on your own types. - Null safety:
T?,?.,??,?:(including?: throw),!!, and flow narrowing after!= null,=>type tests,assert, and guard clauses. - Parameters: defaults, named arguments,
T...varargs, overloading by type (free functions too),outparameters,final/weak/refparameters. - Exceptions:
try/catch/finally, checked exceptions withthrows, multi-catch, exception causes, plusResult<T, E>and the?operator. - Annotations declared in Jux (
public annotation Route { ... }), checked at compile time, with a generated registry for runtime-retention ones (this is how the HTTP example routes requests with no reflection). - Conditional compilation:
@cfg(...)on declarations andif cfg(...)in bodies, with features declared injux.toml. - String interpolation:
$"hello ${name}", plus raw strings. - Observable properties:
observer<T>, binding, bidirectional binding. - Async/streams, a testing framework, exceptions (
try/catch/finally). - Concurrency:
async/await,spawntasks,Channel<T>, real-threadWorker.spawn, and atomics (AtomicInt/AtomicLongwithMemoryOrder). - Memory control:
drop { }deterministic destructors,weakreferences (cycle-breaking,.get()to promote), andrefbindings/params (shared, writable handles to value types). No tracing GC. - Multi-file workspaces: cross-file
imports, package-private visibility, andjux.toml-driven multi-module project builds with per-module binary metadata (version, author, icon). - Optimized release builds:
--releaseemits a tuned[profile.release]by default (opt-level = 3, thin LTO,codegen-units = 1,strip), sojux build --releaseis fully optimized out of the box; any key you set injux.tomloverrides the default. Abenchmarks/harness tracks numeric / allocation / dispatch / object-graph / startup performance.
jux newscaffolds a project andjux testruns@Testfunctions today;jux benchis not added yet.rust.stdand crate coverage is wide but not total: construction, methods, free functions, statics and trait methods work (the HTTP and TCP examples run onstd::ioandtiny_http); the gaps are listed just below.- C FFI works for the common case: declare C functions in an
@extern(lib = "β¦") unsafe native { β¦ }block and call them insideunsafe. System libraries link via#[link]; custom libraries are configured with an[ffi.<name>]table injux.toml(lib_path,linkage,extra_libs), which drives the generatedbuild.rslink step. Strings cross as ordinaryString(the compiler marshals to/from Cconst char*automatically; there is noCString).outparameters work too (out long ticksβ the C function writes the place), and@layout(c)C-compatible value structs pass by value or get filled through a pointer (Win32GetCursorPos(out POINT)works). The reverse direction works too:@exportgives a Jux function C linkage (#[no_mangle] extern "C") so C can call it, includingStringparams/returns (marshalled to/fromconst char*by a generated wrapper).@layout(c, repr = "i32")C enums lower to a#[repr(i32)]integer enum with explicit discriminants (Ok = 200) for status-code style C APIs. C variadics work too (int printf(String fmt, ...)calls, with String literals marshalled toconst char*). Seeexamples/ffi_strings.jux,examples/ffi_struct.jux,examples/ffi_enum.jux,examples/ffi_variadic.jux, andexamples/ffi_export.jux. Still to come: headerbindgenand C++. - Raw-pointer basics work (
T*,void*,&local,&obj,*pinsideunsafe, pointer fields, function pointers). There is nodeletekeyword by design (delete p;is guided to thedrop { }+ foreign-freemodel). - Rust std discovery has gaps. Everything is discovered from rustdoc, never
hand-listed, and a few corners are not reached yet: the
f64math methods ondouble(powf,sin, ...), iterator adaptors, and trait methods that come from blanket or cross-crate impls. The Jux lessons track each one with a repro. - Real language gaps, logged and not invented yet:
union, inlineasm, custom allocators and arenas, and compile-time version/source-location queries. moveis reserved but not implemented; classes share by reference counting.- Expect bugs. This is experimental, one person is building it, and corners of the language will break, change, or get rewritten without warning. File issues.
There's no separate "Jux runtime library" to reinvent. Rust's std is the Jux
std, and any Rust crate is fair game, surfaced in Jux syntax.
import rust.std.PathBuf;
var p = new PathBuf(); // lowers to std::path::PathBuf::new()
p.reserve(16); // camelCase method maps to the real snake_case oneThe types Rust's own prelude puts in scope everywhere (Vec, String,
HashMap, Option) need no import in Jux either. Anything else is one
import rust.std.<Name>; away, or a group:
import rust.std.{HashMap, HashSet};Hover, autocomplete, and go-to-definition over std and your project's crates are
generated on demand from the installed toolchain's rustdoc JSON
(juxc-bindgen): nothing is hand-curated, so it tracks whatever Rust version you
actually have. Collections are Rust's collections under their real names: Vec,
HashMap, HashSet, VecDeque. There is no parallel Jux collection library and
no Java-style facade over Rust's: push, len and insert are the method names,
because they are Rust's.
- Rust crates from crates.io (or a path, or git), consumed and called with Jux
syntax. Add
"rust.tiny_http" = "0.12"tojux.tomlandimport rust.tiny_http.Server;just works: the compiler reads the crate's rustdoc, writes a Jux-syntax stub for type checking, and links the crate into the build. Member names stay exactly as the crate spells them (is_open,recv), so the crate's own docs apply. - Jux libraries straight from GitHub. Point at a repo (with branch / tag /
rev, or a bare-URL shorthand), and
juxresolves and caches it under~/.jux. Cross-compilation via--target <triple>is supported.
Annotations are declared in Jux, checked at compile time, and the compiler writes
every runtime-retention use into a generated registry. That is the Spring-Boot
shape without a reflection runtime. Here is the real HTTP server from
examples/http_server, a crates.io socket library plus an
annotation router:
import rust.tiny_http.Server;
import rust.tiny_http.Response;
import jux.meta.Registry;
import jux.std.meta.AnnotatedItem;
@Retention(RUNTIME)
public annotation Route {
String path();
String method() default "GET";
}
public class Api {
@Route(path = "/health")
public String health() { return "ok\n"; }
}
/** The handler whose `@Route` path matches `url`, or null. */
public AnnotatedItem? routeFor(String url) {
for (var r : Registry.annotated("Route")) {
if (r.getOr("path", "") == url) { return r; }
}
return null;
}
public void main() {
var server = Server.http("127.0.0.1:9091");
while (true) {
var request = server.recv();
var route = routeFor(request.url());
// ... dispatch to the handler, answer with a tiny_http Response
}
}Nothing in the program keeps a list of routes. Adding an endpoint is one annotated method, and the router finds it on the next build.
C interop works today. You can declare, link, and call C functions
(@extern unsafe native), with automatic String/char marshalling, out
parameters, @layout(c) value structs and repr C enums (including a C enum as
a struct field), C variadics (printf-style ...), custom-library linking via
[ffi.*] in jux.toml, and @export to call Jux from C (with String
marshalling). See the examples/ffi_*.jux programs and the consolidated FFI
guide (Architecture/JUX-POINTERS-REFERENCES-GUIDE.md, Β§5).
Still on the roadmap: header bindgen (juxc bindgen --header) and C++ via
autocxx. The path is known and it matters a lot to me; that's the next FFI
milestone.
This is the question I get most, so here's the honest mechanical answer.
Jux does not ask you to write lifetimes, &, &mut, or .clone() by hand.
You write Java-shaped code. The compiler's job is to translate that into Rust that
passes rustc's borrow checker on the first try, without you ever thinking
about ownership. So the "borrow checker" in Jux is really a lowering strategy:
an ownership analysis in the backend that decides, for every value, how it should
be represented and shared in the emitted Rust so that the program both means what
you wrote and compiles under Rust's rules.
The core decisions it makes:
- Class instances are shared, mutable references, exactly like objects in Java
or C#. They lower to
Rc<RefCell<...>>. When you pass an object around or store it in two places, the backend inserts anRc::clone(a cheap refcount bump, what I call a share-clone) so both sides hold the same live object, not a copy. Mutation goes throughRefCell, so two references see each other's changes. Java semantics, achieved with safe Rust. - Value types stay values. Primitives, small structs, and records lower to
plain Rust values and move/copy the way Rust naturally wants. No
Rcoverhead where it isn't needed. refbindings (shared references to value-typed locals, params, and fields) also lower toRc<RefCell<...>>when you explicitly ask for shared mutation.- The backend hoists and reshapes the emitted code to keep
rustchappy: receiver-mutation calls get hoisted so a&mutborrow doesn't overlap an argument evaluation; lambda captures are share-cloned;!Sendstatics becomethread_local!; recursive class shapes get wrapped; container and nullable fields share-clone on read. These are the kinds of borrow conflicts you'd normally hit by hand in Rust, and Jux resolves them for you at lowering time.
The result is human-readable Rust: it's meant to look like something a person
would have written, with sensible parentheses, no needless let mut or type
suffixes, rustfmt-style braces. You can open the emitted crate and follow it.
And because the final artifact is just Rust, you get the entire Rust optimization pipeline (LLVM, inlining, monomorphization, dead-code elimination, release-mode codegen) applied to your Jux program. Jux doesn't try to be a fast compiler-of-fast-code on its own; it hands a clean Rust crate to the best optimizing backend already out there and gets out of the way.
your.jux -> juxc -> readable .rs crate -> cargo / rustc -> native binary
| |
ownership lowering LLVM optimizes
(share-clones, RefCell, everything
hoists; no borrow errors)
A young language is only as good as the programs it has survived. Jux is held to
several independent checks, and all of them run locally with one command
(cargo xtask gate):
- Every example is a test. Each of the 360+ programs in
examples/is compiled, run, and compared byte for byte with its expected output. - Java differential twins. Around a hundred programs (a bank ledger, an LRU cache, a state machine, a binary search tree, generators, pattern matching...) exist twice, once in Jux and once in Java. Both are run and their output is diffed, so "behaves like Java" is measured, not claimed. The few places where Jux differs on purpose are declared, with the reason.
- The Jux lessons.
tests/lessons/holds 67 small teaching programs, one concept each (Hello, Variables, Generics, Ownership, Json, Files, Pointers, FFI...), each with its exact expected transcript. The results table records what passes, what a bug broke and which commit fixed it. - Blessed diagnostics. UI tests pin the exact text, code and position of every error for hundreds of wrong programs, so a message never degrades silently.
- Stability gates. A no-crash fuzz pass over the parser and checker, and a determinism check that the same program always produces the same diagnostics and the same Rust.
- The IDE plugin has its own suite (close to 900 tests), including sweeps that open every example and every lesson and require zero false errors.
In total that is well over 1,400 compiler tests on every change, and nothing is merged unless all of it is green.
A working Rust toolchain is required. Jux compiles through rustc, so
cargo and rustc must be on your PATH. Install from https://rustup.rs. The
repo pins a stable toolchain (rust-toolchain.toml), which rustup honors
automatically.
rustc --version
cargo --versiongit clone https://github.com/xdsswar/juxlang
cd juxlang
cargo build --release -p juxc -p jux -p juxc-lspYou get three binaries in target/release/:
| Component | What it is |
|---|---|
juxc |
The compiler (file-level: compile / build / run) |
jux |
The project tool (reads jux.toml, resolves deps) |
juxc-lsp |
The language server (IDE diagnostics / hover / completion) |
The tools and the IntelliJ plugin look for juxc / juxc-lsp in this order:
an explicitly configured path, then $JUX_HOME, then your PATH. The
recommended setup is to drop the executables straight into one folder and point
JUX_HOME at it (no bin/ subfolder; the binaries sit directly in JUX_HOME).
Windows (PowerShell):
$JuxHome = "C:\Tools\jux"
New-Item -ItemType Directory -Force -Path "$JuxHome" | Out-Null
Copy-Item target\release\juxc.exe,target\release\jux.exe,target\release\juxc-lsp.exe "$JuxHome"
setx JUX_HOME $JuxHome
setx PATH "$env:PATH;$JuxHome"macOS / Linux (bash/zsh):
JUX_HOME="$HOME/.jux"
mkdir -p "$JUX_HOME"
cp target/release/juxc target/release/jux target/release/juxc-lsp "$JUX_HOME/"
echo 'export JUX_HOME="$HOME/.jux"' >> ~/.zshrc
echo 'export PATH="$JUX_HOME:$PATH"' >> ~/.zshrcOpen a new terminal afterward so the variables take effect (and restart
IntelliJ so it inherits JUX_HOME).
public void main() {
print("Hello, world!");
}juxc hello.jux --run # lowers to Rust, cargo-builds, runs, forwards exit codeThe first run compiles a small Rust crate under the hood; later runs reuse the cached build.
The IDE plugin is a thin client: all the smart features come from juxc-lsp, so
install the binaries first.
cd ide/intellij-plugin
./gradlew buildPlugin # Windows: .\gradlew.bat buildPluginThe first build downloads the IntelliJ Platform and a JDK 21 toolchain
automatically. The result is build/distributions/jux-intellij-0.0.9.zip. Then in
your IDE:
Settings/Preferences > Plugins > gear icon > Install Plugin from Disk..., pick the zip, then restart.
The goal is to feel like IntelliJ's Java support, and it gets closer with every release. What you get today:
- Editing: a real PSI parser for all of Jux (records, sealed types, patterns,
generators, operators,
@export { }blocks, properties and observers), semantic highlighting, folding with Java's regions and defaults, smart enter, surround-with, move statement, live templates, brace matching, and a formatter that honors the Wrapping and Braces and Blank Lines code-style tabs. - Completion ranked the way Java's is, most relevant first: locals and
members in scope, smart type-aware suggestions, name suggestions, enum helpers,
members of every Rust crate and Jux library in the project (re-indexed when a
dependency changes), and override completion that writes
@overrideand the whole member for you. - Navigation: go to declaration, implementation and type declaration, Find
Usages, Quick Definition, Type Info (Ctrl+Shift+P), type hierarchy, File
Structure with filters, breadcrumbs, Go to Test / Create Test, and Ctrl+B on an
operator (
a * 2.0,a..b) lands on the exact overload the compiler picks. - Inspections and quick-fixes: unresolved symbols, unused variables,
Java-parity checks ("if can be simplified", "indexed loop can be a for-each"),
generator and interface-operator rules, and "Create missing case branches" for a
switchover an enum or sealed type. Diagnostics fromjuxc-lspshow inline. - Refactoring: rename (with conflict detection), extract and inline variable/method, safe delete.
- Docs and running: Java-style Quick Documentation (signature, inferred
vartypes,@param/@return/@throws, inherited docs), New > Jux File templates, a Run button for anymain, and a test runner with gutter icons for@Testfunctions.
On IntelliJ Community, the native LSP client is inert, so install LSP4IJ from the Marketplace and register
juxc-lspfor the Jux file type. On Ultimate / paid IDEs it's automatic.
π Full step-by-step install (with troubleshooting): INSTALL.md.
jux is the cargo-equivalent project tool, and jux.toml is its manifest. It names
your binary, carries the metadata that gets baked into the executable (version,
author, company, icon, copyright), and lists dependencies, whether they're other
Jux packages, GitHub repos, or Rust crates.
[package]
name = "it.xss.myapp" # reverse-DNS package name
version = "0.1.0"
edition = "2026"
description = "A little Jux app."
authors = ["XDSSWAR <[email protected]>"]
license = "Apache-2.0"
# Baked into the produced executable's resource block (Windows version-info, icon).
icon = "assets/app.ico"
company = "XTREME SOFTWARE SOLUTIONS"
copyright = "Β© 2026 XSS"
[[bin]]
name = "myapp" # the output binary name -> myapp.exe
main = "it.xss.Main" # entry file by dotted path: src/it/xss/Main.jux
[dependencies]
# Another Jux package, straight from GitHub (tracks the default branch):
"it.xss.toolkit" = "https://github.com/xdsswar/toolkit"
# ...or pinned to a tag / branch / rev:
"com.acme.json" = { git = "https://github.com/acme/json", tag = "v1.4.2" }
# A Rust crate from crates.io, used with Jux syntax:
"rust.serde_json" = "1.0"
# Features for `@cfg(feature = "...")` / `if cfg(feature = "...")`,
# switched on with `jux build --features verbose`:
[features]
default = []
verbose = []
# Linking a C library works today via an [ffi.*] table (you declare the
# functions yourself in an `@extern unsafe native` block):
[ffi.sqlite3]
lib = "sqlite3"
lib_path = "vendor/sqlite/lib"
linkage = "dynamic"
# Auto-binding from a header (`c.sqlite3 = { header = "sqlite3.h" }`, via bindgen)
# is the next FFI milestone, not in yet.Then jux run builds the whole thing and produces myapp.exe with your icon and
version metadata embedded.
juxcis the compiler. Works on individual files or directories. Doesn't readjux.tomlor resolve dependencies. Exposes--run,--build,--name,--release,--emit-dir.juxis the project tool (the cargo-equivalent). Readsjux.toml, resolves dependencies, and dispatchesjuxc. This is what you use day to day;juxcis invoked byjux, by the language server, and by foreign build systems.
juxc examples/hello.jux # lower to Rust only
juxc examples/hello.jux --run # compile + build + run
juxc examples/multifile --run # compile a whole directory as one workspace
jux run examples/hello.jux # via the project tool
jux run --release examples/hello.jux # optimized emitted programjux resolves the nearest jux.toml walking up from the current directory
(like Cargo), or acts on an explicit project with the global --manifest-path
flag (a jux.toml or its directory). The build/run/check commands all accept
--release and --target <triple>; in a workspace, -p, --package <name>
selects a member, and --bin <name> / --lib select a target within a package.
jux build # build the project (every workspace member)
jux build -p server # build only the `server` member
jux build --bin tool # build only the `tool` binary of a package
jux build --lib # build only the `[lib]` target
jux build --target x86_64-pc-windows-gnu # cross-compile
jux run --bin tool # run a specific binary (multi-bin packages)
jux run -p client --release # run a member, optimized
jux check -p server # type-check one member, no codegen
jux test -p server # run a member's tests
jux --manifest-path ../app/jux.toml run # act on another project without cd
jux target list # list cross-compile triples (via rustup)
jux metadata --format json # machine-readable project model (for IDEs)jux metadata emits the workspace's members, each package's targets (with their
resolved artifact paths), dependencies (tagged by source), and profiles as JSON.
It is what the IntelliJ "Jux Project" tool window reads to build its module/target
tree, so the IDE never has to hand-parse jux.toml.
juxlang/
βββ Architecture/ # the language specification (the contract)
β βββ JUX-LANG-V1.md # consolidated dossier
β βββ JUX-*-ADDENDUM.md # 20+ normative addenda
βββ examples/ # .jux programs; every one compiles, runs, and is output-checked
βββ tests/
β βββ lessons/ # 67 Jux teaching lessons, each with its exact transcript
β βββ ui/ # wrong programs + their blessed diagnostics
β βββ expected/ # expected output of every example
βββ tools/java-differential/ # Jux/Java twin programs, run and diffed
βββ benchmarks/ # self-timing perf workloads + runner (run.ps1)
βββ crates/
β βββ juxc-source/ # source files, spans, positions
β βββ juxc-diagnostics/ # diagnostic types, E-codes, JSON output
β βββ juxc-lex/ # lexer
β βββ juxc-ast/ # AST types
β βββ juxc-parse/ # parser
β βββ juxc-resolve/ # name resolution
β βββ juxc-tycheck/ # type checking
β βββ juxc-backend-rust/ # lowering to Rust (the ownership analysis lives here)
β βββ juxc-bindgen/ # rustdoc JSON to Jux-syntax stubs
β βββ juxc-lsp/ # language server
β βββ juxc-driver/ # phase orchestration + project/workspace builds
βββ ide/intellij-plugin/ # IntelliJ plugin (Java-style PSI)
βββ bin/{juxc,jux}/ # the two binary entry points
There's a full specification under Architecture/: JUX-LANG-V1.md
plus 20+ addenda covering grammar, the type system, the ABI, diagnostics, the build
system, async, exceptions, annotations, class representation, and more. The spec is
authoritative: behavior should trace to a clause. If something isn't decided
yet, the spec gets updated before the code does. Because there's so much of it, you
will find inconsistencies here and there. That's the cost of one person
maintaining a large design surface, and I clean them up as I find them.
The code is free and open for everyone: open source, use it, learn from it,
build on it. It is distributed under Apache-2.0 (LICENSE) which
means you can use, modify and redistribute it, including commercially.
What the license does not cover is the name. I am the sole owner of the Jux
language itself: the design, the direction, the "Jux" name and the logos. Fork
the code freely, but please don't ship a fork called "Jux" or use the logos in a
way that suggests it's the official project. "Based on Jux" or "compatible with
Jux" is welcome. The full wording is in NOTICE.
Built solo, with love, by XDSSWAR, XTREME SOFTWARE SOLUTIONS. Third time's the charm. π






