Skip to content

Repository files navigation

Kirito

🚨 We've moved!

Kirito has migrated to a new home: github.com/kiritolang/kiritolang.github.io.

Please update your bookmarks, remotes, and CI configuration. The old AzethMeron/KiritoLang repository 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 then kpm 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))

Disclaimer

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.

Contents

Why Kirito

  • 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 .ki express 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. Integer and Float are 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 on dispatcher.mainVM(), read results back. No build step beyond adding src/ 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.

Limitations

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 int64 with 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.3 is False, NaN never 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, ComplexMatrix all 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 complex module.)
  • A single KiritoVM is single-threaded — one VM is one fully-encapsulated, serializable process touched by exactly one OS thread. True parallelism is therefore multiprocessing: the parallel module runs many fully-isolated VMs that share nothing and communicate only by passing serialized values through thread-safe queues and primitives.

Benchmarks

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

Embedding Kirito in C++

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 threads

Extending Kirito from C++

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

Installing

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 | sh

Installs 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 | iex

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

Packages (kpm)

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 binary

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

Repository structure

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.

Building and running

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. If g++ is older than 13, apt-get install g++-13 and configure with CXX=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)

Sanitizers and the full test matrix

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 -j

ASan's recursion-guard frames are larger, so give it a roomier stack if a deep-nesting test trips the guard early: ulimit -s 262144 before the ctest line. Each sanitizer build is a separate build-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--j build can be OOM-killed (a bare Terminated; dmesg shows oom-kill … cc1plus). Peak RAM is jobs × ~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] then memory=48GB), wsl --shutdown, reopen. post_work_check.sh auto-caps the asan/tsan build jobs by available RAM (~3 GB/job); override with PW_SANITIZER_JOBS=N.

ThreadSanitizer on a recent kernel (Ubuntu 24.04 / WSL2): if every tsan test fails at once (even trivial ones like test_arena) with FATAL: 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 via echo '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 summary

It does not touch git — it only builds and tests, then reports which variants are green so you decide whether to commit.

Building the release binaries (static, TLS)

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

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

Testing the built executables

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 interpreter

Documentation

Full 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

License

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.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages