Official domains: xag-lang.com, xag-lang.org, xag-lang.dev only
A programming language built around high performance, helpful error messages,
and Rust-style memory management. A size is always written: there is no int
on its own, because there is no size to assume. Programs are compiled ahead of time,
either to native code or to a form run by an AOT interpreter.
Early work in progress — nothing here is stable yet.
The code written is mostly or 100% AI-made. Why? Because, I'm more of a designer, not a C++ stroke-inducing syntax reader 🤣. No offense.
"Why would anyone ever use Xag?" Honestly, I don't know. 😂
The compiler is written in C++20 against LLVM's native C++ API.
Requirements: LLVM 23 or newer, CMake, Ninja.
brew install llvm cmake ninja
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
ninja -C build
./build/xagcCMake finds Homebrew's keg-only LLVM on its own. Build with Apple's clang++
(the default) rather than Homebrew's — Homebrew's driver ships its own libc++
and collides with the macOS SDK headers once LLVM's include directory is added.
Source files are .xag. xagc lex <file> prints its tokens, xagc parse <file>
prints its tree, xagc check <file> checks it, xagc mir <file> prints the
mid-level IR, and xagc llvm-smoke proves the LLVM backend is reachable. Run the
tests with ctest --test-dir build.
cmake --install build --prefix /usr/localThat lays out bin/xagc, lib/xag/libxagrt.a, include/xag/xag_runtime.h and
share/xag/examples. xagc finds the runtime relative to its own executable
— following symlinks first, so a link in a bin somewhere else still works —
and XAG_RUNTIME overrides it if you keep the runtime elsewhere.
xagc run <file> runs a program on the test interpreter — the engine built to be
obviously correct rather than fast, which walks the IR as written and does nothing
clever anywhere. When the engines disagree it is the one to look at first, which is
not the same as the one to believe: on 2026-09-07 it and the native backend shared
a two-day-old mistake and the fast interpreter was the only one right. Three engines
say that they disagree and which one stands apart. Which is correct is a
question for whoever reads the program.
xagc fast <file> runs it on the fast interpreter, which turns the graph into
flat code once and then runs it without looking anything up again. It shares
nothing with the test interpreter but the runtime, on purpose: two engines that
borrow from each other agree about what they borrowed, and a vote between them
proves nothing.
A program reads with read.stdin, which answers a line or nothing, and is given
its arguments by whatever follows --: xagc run adder.xag -- 3 4.
xagc build <file> compiles a program ahead of time through LLVM at -O3 and
writes an executable beside it. xagc ir <file> prints the LLVM IR, and
xagc ir <file> --raw prints it before the optimiser sees it.
All three engines exist: the test interpreter, the fast interpreter, and AOT native. Two engines can say that something is wrong; three can say which.
All three engines call one runtime, so none of them can disagree about what
joining or counting means. Xag-Config.toml holds what this project has decided
once for every file in it.
It holds decimal, which picks between IEEE 754 decimal written out in
software and IBM's decimal floating-point unit — z/Architecture has one from z9,
POWER from POWER6. Both must answer every operation identically, because IEEE 754
decimal is specified that closely, so the second one is a second opinion on the
arithmetic rather than a preference. Which a build has is settled when the
runtime is compiled (cmake -DXAG_DECIMAL=hardware), and asking for one it has
not got is refused rather than quietly answered by the other.
Decimal has a second check that the oracle cannot give it. Three engines calling
one runtime agree about everything inside that runtime, so a mistake in the
arithmetic itself is a mistake all three make together and no vote will find it.
tests/decimal_cases.cpp writes down every decimal operation and our answer, and
tests/decimal_reference.py disagrees with them using Python's decimal —
libmpdec, written by someone else from the same IBM specification IEEE 754
decimal is drawn from, and derived from nothing here. It is a test and never goes
near the language. Four hundred thousand cases agree exactly, apart from raising
to a power, which the specification itself allows to be out by one in the last
place.
tests/power/ asks a real decimal unit the same questions. No machine here has
one, so it cross-compiles a program with no operating system under it, hands it
to QEMU where a kernel would go, and reads the answers back over the console —
no disk image, no distribution, nothing downloaded. Twenty thousand sums,
differences, products and quotients agree exactly, cohorts included.
What runs in there is the runtime itself, built with XAG_DECIMAL=hardware: its
arithmetic is the unit's instructions and its numbers are laid out the way the
unit lays them out. Everything above the encoding seam is the same code that
runs anywhere. Thirty thousand sums, differences, products and quotients at all
three widths, against the software build, agreeing exactly — cohorts included.
Neither build has to know the other's encoding: a case crosses as text, and a
decimal is a sign, a coefficient and a power of ten whatever holds it. Ours is
BID, with the coefficient an ordinary binary integer; IBM's unit is DPD, with it
packed three digits to ten bits. It runs when qemu-system-ppc64 and ld.lld
are present and stays out of the way otherwise.
generator/ writes Xag programs and asks every engine what they say — and when
they differ, which one is out of step with the other two.
cd generator && cargo build --release
./target/release/xag-oracle --xagc ../build/xagc --cases 100It has no dependencies. The random numbers and the threading are a few lines each, and a fuzzer is the last place to want a supply chain. Work is handed out by an atomic counter rather than split in advance, because cases differ in cost and a thread that finishes early should take the next one.
The thing that decides its speed is not compiling or running. macOS spends about
170ms scanning every freshly linked binary before it will run it once, which is
four times what everything else costs put together — and it is paid per binary,
not per statement. So --size matters more than --cases: bigger programs
amortise it. Past about 400 statements the reference interpreter, which is slow
on purpose, stops finishing inside the timeout and cases start being skipped
rather than tested.
count counts grapheme clusters, which is what a person counting characters
means. The rules are UAX #29 in full — Hangul syllables, Indic vowel signs and
conjuncts, prepends, zero-width-joiner sequences and flags — against tables
generated from the Unicode Character Database, version 17.0.0.
All 766 of Unicode's own conformance cases run as part of the test suite, so the
claim is checkable rather than asserted. Regenerate runtime/xag_unicode.h and
tests/unicode_cases.h from the UCD when moving to a new Unicode version.
Wrong diagnostics, confusing ones, and anything Xag accepts that it should not: https://github.com/Artificial-IntelligenceAI/Xag-lang/issues
A diagnostic that points at the wrong thing, or names a rule the program did not break, is a bug of the same kind as miscompiling — the compiler is telling the reader something untrue either way.
Tankun Sriket designs Xag. Claude writes the code. The language, the syntax and what the project is for are his; the compiler, the runtime, the engines, the oracle and the tests are written by Anthropic's Claude under his direction. That is the arrangement as it stands, and this line changes when it does.
| C++ | 35 .cpp and 21 .h — the front end, the middle layer, both interpreters, the native backend and the runtime |
| Xag | 11 files — the examples, and programs the tests run |
| C | 5 files — three benchmarks written for comparison, and two for the POWER decimal test |
| Rust | 3 files — the generator and the oracle, which share no code with the compiler on purpose |
| Python | 2 files — the benchmark runner, and the decimal reference the tests are checked against |
| Assembly | 2 .S — POWER's decimal instructions, and a freestanding entry point for testing them |
| Shell | tests/power/run.sh, which drives that test |
| plus CMake, a linker script, TOML and Markdown |
- LLVM, 23 or newer — the native backend is written against its C++ API, and
xagc buildgoes through it at-O3. Its licence is where this project's came from. - CMake (3.20+) and Ninja to build.
- The Unicode Character Database, version 17.0.0, which the character tables are generated from and whose 766 conformance cases the tests run.
- libmpdec, by Stefan Krah, reached through Python's
decimal— an independent implementation of the same IBM specification, used to disagree with our decimal rather than to agree with it. - QEMU (
qemu-system-ppc64) and ld.lld, when they are there, to run the decimal tests against POWER's own hardware unit. - IEEE 754 and IBM's decimal arithmetic specification, which the arithmetic is written against rather than borrowed from.
Python and QEMU are needed only by tests; a build wants none of them.
The website is a separate repository with credits of its own, and it is licensed differently — see it rather than assuming it matches this.
Copyright 2026 Tankun Sriket
Apache License, Version 2.0, with the LLVM exception (LICENSE).
SPDX: Apache-2.0 WITH LLVM-exception.
Writing programs in Xag costs you nothing. Every program xagc builds has
the Xag runtime linked into it, so a piece of this project ends up inside what
you ship. The exception is there for exactly that: those embedded portions may
be redistributed without carrying any notice, so a program built with Xag owes
nothing to anybody and needs no mention of this project anywhere.
Changing Xag itself is where credit is due. Distribute a modified compiler, runtime or tool and the Apache terms apply in full — keep the notices, and say plainly which files you changed.
That split is the whole intent: use it freely, and say so if you alter it.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.