Skip to content

Repository files navigation

Xag coding language

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. 😂

Building

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/xagc

CMake 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.

Installing

cmake --install build --prefix /usr/local

That 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.

The oracle

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 100

It 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.

Unicode

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.

Reporting a problem

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.

Credits

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.

What this repository is written in

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

What it leans on

  • LLVM, 23 or newer — the native backend is written against its C++ API, and xagc build goes 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.

License

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.

Contribution

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.

About

Xag — a compiled language with Rust-style ownership, licensed Apache-2.0 WITH LLVM-exception.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages