Kirito has migrated to a new home: github.com/kiritolang/kiritolang.github.io.
Please update your bookmarks, remotes, and CI configuration. The old
AzethMeron/KiritoLangrepository is now a redirect stub — all new development, releases, and issues live at the new location.To sync an existing install: run
kpm update-kpm(and thenkpm update-ki) to pull the updated package manager + interpreter from the new repository.
Kirito is a small, dynamically-typed yet strongly-typed scripting language built from scratch in modern C++20. It is designed first and foremost as an extension language for C++ applications: a high-level, ergonomic surface for writing logic, with a runtime that drops cleanly into a host program and lets that program reach back in.
Source files use the .ki extension. The whole interpreter is header-only — a single
#include "kirito.hpp" — so Kirito runs both as a standalone interpreter (ki) and as a library
embedded in any C++ project. One KiritoVM object is one fully isolated interpreter "process".
Execution engine — bytecode, not a tree-walker. Kirito compiles each body to bytecode and runs it on a stack VM behind the stable AST boundary. It is no longer a tree-walking interpreter — the original tree-walking evaluator was removed and the bytecode compiler + VM is now the sole engine. The last release built on the tree-walking evaluator is v1.6.2; everything from v1.7.0 onward runs the bytecode VM.
var io = import("io")
var greet = Function(name : String) -> String:
return f"Hello, {name}!"
for who in ["world", "Kirito"]:
io.print(greet(who))
Kirito is 100% generated by Claude Code (Opus 4.8, 1M Context, Max effort). It was implemented with careful prompting and under human control, guidance, hand-holding to fulfill human requirements, but it is fully implemented and tested by AI agent. Entire development took about 3 days. It's provided in hope of being useful tool, but also as a test of AI competency — which, given the scope and time, seems unreal to me as the person guiding it.
Documentation (and this markdown file, except for disclaimer) is written by AI aswell.
And why did i call it after MC of SAO? Dunno, just thought it's funny. Also I do have only warm memories of that anime.
- Why Kirito
- Limitations
- Benchmarks
- Embedding Kirito in C++
- Extending Kirito from C++
- Installing
- Repository structure
- Building and running
- Documentation
- License
-
A high level of abstraction, with no boilerplate. Significant indentation, first-class functions and closures, classes with inheritance and operator overloading, exceptions, context managers, rich built-in collections (
List/Set/Dict), f-strings, packing/unpacking, and a batteries-included standard library — so a few lines of.kiexpress a lot. -
Memory safety by construction. Every value lives in a VM-owned arena and is referred to through lightweight handles, never raw pointers. A precise mark-and-sweep garbage collector reclaims values automatically (the test suite runs clean under AddressSanitizer and UBSan). Recursion depth, huge allocations, and deeply nested data are all guarded so hostile or runaway scripts throw a catchable error instead of crashing the host.
-
Strong typing without ceremony.
IntegerandFloatare distinct, there are no silent coercions across incompatible types, and type annotations are enforced at runtime when you want them (Function(d : Dict) -> Float:actually checks the argument and the return value) — yet they are entirely optional. -
Easy integration, both directions.
- Embed Kirito in C++ — construct a
KiritoDispatcher, run source ondispatcher.mainVM(), read results back. No build step beyond addingsrc/to your include path and compiling as C++20; there is no library to link. - Extend Kirito from C++ — register your own functions, modules, and object types. Anything you add flows through the same object protocol as the built-ins, so to a Kirito program your C++ type is indistinguishable from a native one.
- Embed Kirito in C++ — construct a
Kirito is young and makes deliberate trade-offs. The notable current limits:
- Compute-bound code is still slower than a native VM. Kirito runs a bytecode VM (no longer a tree-walker), but interpreter-bound tight loops remain a few × slower than CPython and Lua 5.1, and far slower than C++; work that delegates to the C++ standard library (sorting, hashing, string ops) closes most of the gap and can match or beat Lua 5.1. See Benchmarks.
- Integers are fixed-width
int64with well-defined two's-complement wraparound on overflow; arbitrary-precision integers are a future enrichment. - Equality (
==) is always exact; tolerance is only ever via.compare. Float/Integer==is exact IEEE-754:0.1 + 0.2 == 0.3isFalse,NaNnever equals anything (not even itself), an infinity equals only an identical infinity — so==/!=agree with</>and with hashing. The same rule holds for every native numeric type —Complex,Matrix,Tensor,ComplexMatrixall compare bit-exactly with==. For approximate comparison they each carry.compare(other, rel_tol = 1e-9, abs_tol = 0.0)(close when|a - b| <= max(rel_tol * max(|a|, |b|), abs_tol)) — the single, explicit way to ask "are these close?". - Unicode case mapping (
upper/lower) covers ASCII, Latin-1 and Latin Extended-A, not the full Unicode case-folding tables. - Not yet implemented: comprehensions, generators, and variadic parameters. (Complex numbers
are supported — the native
complexmodule.) - A single
KiritoVMis single-threaded — one VM is one fully-encapsulated, serializable process touched by exactly one OS thread. True parallelism is therefore multiprocessing: theparallelmodule runs many fully-isolated VMs that share nothing and communicate only by passing serialized values through thread-safe queues and primitives.
Kirito (bytecode VM) vs C++ (-O2) vs CPython 3.11 vs Lua 5.1 on identical algorithms over identical
(LCG-generated) data, release build — mean time per repetition, lower is better, median of three runs
(tools/tests/bench/compare.py):
| Workload | C++ (-O2) | Python 3.11 | Lua 5.1 | Kirito | Ki / C++ | Ki / Py | Ki / Lua |
|---|---|---|---|---|---|---|---|
| pessimistic — interpreter-bound tight loops | |||||||
sum_loop (arithmetic loop) |
0.29 µs | 44 µs | 13.2 µs | 152 µs | ~525× | 3.5× | 11.5× |
fib (recursive calls) |
3.0 µs | 215 µs | 170 µs | 1.58 ms | ~525× | 7.3× | 9.3× |
sieve (nested loops + indexed writes) |
1.6 µs | 113 µs | 110 µs | 1.14 ms | ~700× | 10× | 10× |
| optimistic — work delegated to C++ builtins | |||||||
sort (builtin sort) |
13.3 µs | 91 µs | 314 µs | 253 µs | 19× | 2.8× | 0.8× |
dict_ops (hash insert/lookup) |
46 µs | 117 µs | 121 µs | 336 µs | 7× | 2.9× | 2.8× |
string_ops (split/join) |
32 µs | 41 µs | 200 µs | 107 µs | 3× | 2.6× | 0.5× |
Geometric-mean slowdown: pessimistic ≈ 580× C++ / 6.3× Python / 10× Lua 5.1, optimistic ≈ 8×
C++ / 2.8× Python / 1.0× Lua 5.1. The shape is what a bytecode VM with a fast C++ standard library
should show: it still pays per-operation dispatch on tight loops — where Lua 5.1's register VM is
~10× quicker — but amortizes that away once work lands in native builtins, where Kirito is on par with
or faster than Lua 5.1 (sort, string_ops delegate to std::sort / std::string).
Slot-addressed locals (resolving each function's non-captured locals to a frame-slot index at
compile time) plus a numeric binary fast path landed in 1.9 and roughly halved the tight-loop gap —
sum_loop went 7.1×→3.5× and dict_ops 4.7×→2.9× versus Python. The C++ baseline runs in tens to hundreds of nanoseconds,
where timer jitter dominates, so the Ki / C++ column is approximate and swings run-to-run; the
Ki / Py and Ki / Lua ratios are the stable, meaningful ones. Lua 5.1 has no integer type, so its
column uses doubles; the benchmark's 31-bit LCG is computed with an exact split-multiply so every
language runs on byte-identical data.
Reproduce: cmake --build build-release --target ki && python3 tools/tests/bench/compare.py --ki build-release/ki
(the Lua column appears automatically when lua5.1 is on your PATH).
Construct a KiritoDispatcher — the recommended entry point — and run source on the VM it gives you.
It owns a fully-configured KiritoVM (with the parallel module enabled) plus the worker machinery,
exactly like the ki CLI, and costs nothing until you use it. (For the bare minimum without
parallel, you can construct a KiritoVM directly — see the docs.)
#include "kirito.hpp"
using namespace kirito;
int main() {
KiritoDispatcher dispatcher; // the embedding entry point
KiritoVM& vm = dispatcher.mainVM(); // a fully-configured interpreter
Handle result = vm.runSource("var x = 6 * 7\nx\n");
std::printf("%s\n", vm.stringify(result).c_str()); // 42
} // ~KiritoDispatcher cleanly joins any worker threadsExpose a C++ function to scripts with a single registration. The Value / Args helpers read and
build Kirito values, turning a bad argument into a clear error instead of a crash:
vm.registerGlobal("repeat", vm.alloc(std::make_unique<NativeFunction>(
"repeat", [](KiritoVM& vm, std::span<const Handle> raw) -> Handle {
Args a(vm, raw, "repeat");
std::string s = a.at(0).asString("s");
int64_t n = a.at(1).asInt("n");
std::string out;
for (int64_t i = 0; i < n; ++i) out += s;
return val(vm, out);
})));
// Kirito: repeat("ab", 3) -> "ababab"Whole modules and brand-new object types (with their own methods and operators) are added the same way — see the documentation for complete, worked examples.
Prebuilt 64-bit ki binaries for Windows and Linux are attached to each
GitHub Release: optimized builds with HTTPS (TLS) support, linked as statically as
possible (OpenSSL and the C/C++ runtime are bundled in), so they run with no other dependencies.
The one-line installers download ki and the kpm package manager, drop launchers on your PATH,
and create the package directory ~/.kirito/packages (which ki searches automatically):
Linux / macOS
curl -fsSL https://github.com/ghraw/kiritolang/kiritolang.github.io/main/tools/scripts/install.sh | shInstalls to ~/.local/bin (no root). Pass options after -s --, e.g.
| sh -s -- --bin-dir ~/bin, or --from-source to build locally instead of downloading. On a
platform without a prebuilt binary the script builds from source automatically (needs git +
cmake; install libssl-dev so kpm's HTTPS works).
Windows (PowerShell)
irm https://github.com/ghraw/kiritolang/kiritolang.github.io/main/tools/scripts/install.ps1 | iexInstalls ki.exe + kpm.cmd under %LOCALAPPDATA%\Programs\Kirito and adds it to your user PATH.
Manual — download ki-linux-x64 / ki-windows-x64.exe from the latest release, put it on your
PATH (rename to ki/ki.exe), and chmod +x it on Unix.
Kirito's package manager installs packages — collections of .ki modules — straight from a git
repository (GitHub by default, GitLab too). There is no central index: you name an owner/repo.
kpm install owner/repo # install the package (and its dependencies) from GitHub
kpm install owner/repo@^1.2.0 # pin a tag/branch/commit, or a semver constraint
kpm install gitlab.com/owner/repo # install from GitLab (also: gitlab:owner/repo, or a full URL)
kpm list # what's installed
kpm update --all # re-resolve + reinstall (or `kpm update <name>...`)
kpm remove name # uninstall
kpm update-kpm # update kpm itself; `kpm update-ki` updates the interpreter binaryInstalled packages land in ~/.kirito/packages/<name>/ and are importable directly —
import("name") — because ki puts that directory (and each package sub-directory, plus anything in
the KIRITO_PATH environment variable) on its module import path.
A package repository carries a kirito.json manifest at its root:
{
"name": "mypkg",
"version": "1.0.0",
"modules": ["mypkg.ki", "extra/util.ki"],
"dependencies": ["someone/dep"]
}modules are repo-relative .ki paths; dependencies are other owner/repo packages installed
first. kpm is itself written in Kirito (kpm/kpm.ki) — it just uses the net, json, io, sys,
and semver modules — so it doubles as a worked example.
src/kirito/ The interpreter — a header-only C++20 core (~50 headers). One umbrella header,
kirito.hpp pulls in everything: lexer, parser, AST, the bytecode compiler + stack VM, the
value/object model, the arena + mark-sweep GC, and the standard library
(the stdlib_*.hpp modules: io, math, complex, json, net, time, hash, …).
main.cpp The standalone `ki` CLI (REPL + file runner) — the only `main()`.
CMakeLists.txt Thin CMake: an INTERFACE target for the header-only core, the `ki` executable,
CMakePresets.json and the test executables. Presets: debug / release / asan / tsan.
examples/ Sample `.ki` programs (RPN calculator, word count, todo, stats, …), plus:
big_projects/ Large pure-Kirito programs that double as interpreter stress tests:
kgrad (tensor/autodiff/neural nets), imaging (a Pillow-style image + video
library), sqldb (a concurrent networked SQL database), webserver (a concurrent
HTTP/1.1 server + routing framework), and selfhost (a Kirito interpreter in Kirito).
http_client/ A `net` HTTP-client app + server + Python test harness.
data/ Sample datasets (e.g. iris.csv) used by the examples.
kpm/ kpm.ki — the package manager, written in Kirito (installs packages from GitHub).
tools/ Project tooling:
tests/ The CTest suite (every feature gets a test):
unit/ C++ unit tests (one executable per area).
scripts/ Golden `.ki` programs — each `*.ki` checked against its `*.expected` stdout.
errors/ `.ki` programs that must fail, with required diagnostics in `*.experr`.
lang/ integration/ End-to-end language and C++-embedding tests.
fuzz/ bench/ Stability fuzzers; timing + cross-language C++/Python comparison.
scripts/ build_all.sh (release binaries), test_release.sh (run the `.ki` suites against
a built binary), post_work_check.sh (clean-build every variant + full CTest),
install.sh / install.ps1 (the Linux/macOS + Windows installers).
docs/ The documentation site: hand-authored Markdown in `docs/pages/`, rendered by
the dependency-free `docs/build_docs.py` into `docs/site/`.
editors/ Syntax-highlighting definitions for `.ki` files — Notepad++ (UDL), VS Code
(TextMate grammar + extension), and Vim. See `docs/editors/README.md`.
CLAUDE.md The project charter: what Kirito is, how it's built, and the working rules.
Archive/ Two prior incomplete attempts (V1/V2) — reference only; not built.
The everyday build needs a C++20 compiler (GCC 13+ / Clang 18+ / MSVC), CMake ≥ 3.28, and Ninja. Install the toolchain for your platform:
sudo apt-get install -y build-essential g++ cmake ninja-build git # Debian/Ubuntu/WSL
sudo dnf install -y gcc-c++ cmake ninja-build git # Fedora/RHEL
sudo pacman -S --needed base-devel cmake ninja git # Arch
xcode-select --install && brew install cmake ninja # macOS (Apple Clang + Homebrew)
# Windows: install Visual Studio 2022 with the "Desktop development with C++" workload (MSVC + CMake + Ninja).CMake version: Ubuntu 24.04 ships 3.28 (fine); on 22.04 or older, apt's CMake is too old — use
pip install --user cmake ninja, or the Kitware APT repo. Ifg++is older than 13,apt-get install g++-13and configure withCXX=g++-13 cmake --preset debug.
Then build and run (same commands on every platform):
cmake --preset debug # configures into build-debug/ (presets: debug / release / asan / tsan)
cmake --build build-debug -j
./build-debug/ki path/to/program.ki # run a script
./build-debug/ki # start the REPL
ctest --test-dir build-debug -j # run the C++ unit + golden-script test suite (316 tests)Every variant uses the same preset / build / test commands — just swap the name. The two safety
gates are asan (AddressSanitizer + UBSan: memory and undefined-behaviour) and tsan
(ThreadSanitizer: data races and lock-order inversions in the parallel dispatcher, the only
concurrent code):
# AddressSanitizer + UBSan
cmake --preset asan && cmake --build build-asan -j
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
ctest --test-dir build-asan -j
# ThreadSanitizer
cmake --preset tsan && cmake --build build-tsan -j
TSAN_OPTIONS=halt_on_error=1:second_deadlock_stack=1 ctest --test-dir build-tsan -jASan's recursion-guard frames are larger, so give it a roomier stack if a deep-nesting test trips the guard early:
ulimit -s 262144before thectestline. Each sanitizer build is a separatebuild-asan//build-tsan/directory (~8–13 GB of objects — make sure you have the disk).Memory: the sanitizer-instrumented compiles are RAM-hungry (~1.5–2 GB per parallel job). On a memory-capped box — notably WSL2, whose default cap is a fraction of host RAM — a full-
-jbuild can be OOM-killed (a bareTerminated;dmesgshowsoom-kill … cc1plus). Peak RAM isjobs × ~2.5 GB, so it scales with core count — a 24-core box launches 24 such compiles at once (~50 GB). Either build with fewer jobs (cmake --build build-asan -j8) or throw WSL2's cap in%UserProfile%\.wslconfig([wsl2]thenmemory=48GB),wsl --shutdown, reopen.post_work_check.shauto-caps theasan/tsanbuild jobs by available RAM (~3 GB/job); override withPW_SANITIZER_JOBS=N.ThreadSanitizer on a recent kernel (Ubuntu 24.04 / WSL2): if every
tsantest fails at once (even trivial ones liketest_arena) withFATAL: ThreadSanitizer: unexpected memory mapping, the kernel's ASLR entropy is too high for TSan's shadow layout. Lower it once:sudo sysctl -w vm.mmap_rnd_bits=28(persist viaecho 'vm.mmap_rnd_bits=28' | sudo tee /etc/sysctl.d/99-tsan.conf), then re-run — no rebuild needed. This is a kernel/sanitizer knob, not a Kirito issue (debug/release/asan are unaffected).
To build and test all four variants in sequence (debug → release → asan → tsan, each a clean
build of the whole auto-discovered CTest suite) with one command:
tools/scripts/post_work_check.sh # debug+release first, then asan+tsan; prints a green/red summaryIt does not touch git — it only builds and tests, then reports which variants are green so you decide whether to commit.
tools/scripts/build_all.sh builds the shippable 64-bit binaries into dist/ — ki-linux-x64 and, when
the mingw cross compiler is installed, ki-windows-x64.exe (it builds a static OpenSSL for the
Windows target from source, cached under .deps/). On Debian/Ubuntu/WSL:
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build git perl libssl-dev mingw-w64
tools/scripts/build_all.shEach binary is a Release build with TLS on and linked as statically as possible (the Linux binary
keeps only glibc dynamic; the Windows .exe is fully static). To cut a release, bump the version in
src/kirito/version.hpp, build with build_all.sh, and upload dist/ki-linux-x64 and
dist/ki-windows-x64.exe to a GitHub Release tagged with the bare version (e.g. 1.7.0). The
project does not use CI.
tools/scripts/test_release.sh runs the end-to-end .ki test suites (golden-output + error-diagnostic
scripts) against the binaries in dist/, so you can verify each shipped interpreter directly. Linux
binaries run natively; Windows .exe binaries run under Wine:
sudo apt-get install -y wine64 # only needed to test the .exe
tools/scripts/test_release.sh # tests every dist/ki-* binary
tools/scripts/test_release.sh ./build/ki # or test one specific interpreterFull documentation lives in docs/ as a small, dependency-free static site. Open
docs/site/index.html in a browser, or read the Markdown sources in
docs/pages/. It covers:
- Getting started, the language guide, and a built-in types & operator-overloading reference
- The built-in functions and a per-function standard-library reference
- Embedding Kirito in a C++ program and extending it with C++ functions, modules, and types
- Recipes and a multi-part course with worked sample projects
Kirito is released under the MIT License — all of its own source
(src/kirito/, tools/, kpm/, examples/, docs/) is original work.
Incorporated third-party software and its licenses are recorded in
THIRD_PARTY_LICENSES.md: the bundled fum hash-map
library (MIT), and OpenSSL (Apache License 2.0), which is linked only into TLS
builds (-DKIRITO_ENABLE_TLS=ON). The compression, hashing, and regex modules are
from-scratch implementations of public standards, not third-party code.