Skip to content
Rawframe-ProjectPublic

About

Kest: a statically typed language for games and simulations. Bytecode VM in C11, zero dependencies.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Kest

A small, fast, statically typed language for the gameplay half of a game, run by a bytecode machine a native engine embeds.

CI License: MIT Release OpenSSF Scorecard

Kest sits where Luau, daslang, Lua and AngelScript sit: inside an engine, running the rules, the AI, the quests and the simulation a designer changes every day. It is one C11 library with nothing beneath it but libc, and a compiler that proves what the code promises before it runs.

module quest

import std.io

enum Task {
    Idle
    Fetch(i32)
    Wait(i32)
}

struct Actor {
    name: text
    hp: i32
    task: Task
}

// The compiler proves both promises: nothing here touches the heap, and
// it answers the same on every machine.
fn next(one: Actor) -> Task no.alloc deterministic {
    return match one.task {
        Idle -> if one.hp < 50 -> Task.Wait(3) else -> Task.Fetch(1)
        Fetch(item) -> Task.Wait(item + 1)
        Wait(left) -> if left <= 1 -> Task.Idle else -> Task.Wait(left - 1)
    }
}

fn main() -> i32 {
    let world: store<Actor> = store(16)
    let hero = add(world, Actor("Ada", 80, Task.Idle))
    for turn in 0..5 {
        if let one = get(world, hero) {
            one.task = next(one)
            set(world, hero, one)
            io.print("turn {turn}: {one.name} is {one.task}")
        }
    }
    return 0
}
turn 0: Ada is Task.Fetch(1)
turn 1: Ada is Task.Wait(2)
turn 2: Ada is Task.Wait(1)
turn 3: Ada is Task.Idle
turn 4: Ada is Task.Fetch(1)

Why Kest

  • Fast where a frame is spent. A stack machine with fused instructions, and a second engine that writes the program as C for a release build.
  • Promises the compiler proves. no.alloc: the body never touches the heap. no.host: it never calls into the engine. deterministic: it computes the same bits on every machine, for lockstep, rollback and replays. Each is checked over every body a function reaches.
  • Identity that cannot dangle. store<T> hands out ref<T>, and a reference to something removed is refused at the line that used it instead of reading whatever took its place.
  • Diagnostics for people and tools. Every mistake in a file in one pass, each with a stable code, the place and a fix where one is known, and --json for editors and agents.
  • Made to be embedded. One header and one static library, no allocator or thread the host did not hand it, and ceilings on stack, call depth, heap and steps that are refused in words.
  • Reload while the game runs. A changed program is rebuilt under the world the host holds, or refused with the old one untouched.

Quick start

From the source, with a C compiler, CMake and Ninja:

git clone https://github.com/Rawframe-Project/kest.git
cd kest
cmake --preset dev && cmake --build --preset dev
out/dev/kest run examples/math.kest
out/dev/kest new game && cd game && ../out/dev/kest run

On Windows, with Visual Studio, use the windows-msvc preset and out\windows-msvc\kest.exe. Each release on the releases page has an archive for every platform below, checksummed in SHA256SUMS and attested; unpack it anywhere and put its bin on the path. The playground runs the command line in the browser.

A host builds a program, starts a machine and calls into it:

#include <kest.h>

KestBuild *build = kest_build("rules.kest", NULL, stderr, KEST_FORM_TEXT, 0);
KestRuntime *vm = build ? kest_start(build, NULL, NULL) : NULL;
int32_t damage = kest_entry(vm, "damage");
KestValue frame[4] = {{.integer = 80}, {.integer = 25}, {.integer = 5}};
kest_call(vm, damage, frame, 4); // frame[0] holds the answer

Getting started and Embedding go through both.

Platforms

Linux on x86-64 and arm64 with GCC or Clang, macOS on arm64, Windows on x86-64 with MSVC, and WebAssembly (WASI). CI builds and runs every example on the first four and holds them to the same output byte for byte.

Performance

Measured on one 8-processor machine against Luau, Lua 5.4, LuaJIT, QuickJS-ng and daslang. Kest's interpreter is ahead of Luau's on all five workloads, and its release engine ahead of Luau's native code on all five; a host calling into Kest is cheaper than into any other engine measured. Performance has the rest and what it does not win, and Benchmarks says how to take the numbers again.

Interpreters: Kest, Luau, Lua 5.4, LuaJIT's interpreter and daslang, time as a share of Luau's

Every engine in every mode, milliseconds per workload

Nanoseconds for one crossing each way through each engine's C API

The same simulation's answer on four platforms in Kest, Lua 5.4, LuaJIT, Luau and JavaScript

Status

Before 1.0.0 nothing is frozen: a release can change the language, and the changelog says what a program has to do about it. It is not a systems language, an engine or a hard real-time system. It is being built into a sandbox for code nobody trusts, and Running untrusted code says how far each promise is kept today and how to report a vulnerability.

Documentation

Contributing

Issues are welcome. Pull requests are accepted for issues a maintainer has approved; see CONTRIBUTING.md. The project is developed with AI coding agents, directed and reviewed by its maintainers; outside contributions follow the AI policy.

License

MIT; see LICENSE. bench/hosts/tetromino.lua is a copy of a third party's game under its author's MIT license, which the file carries.

About

Kest: a statically typed language for games and simulations. Bytecode VM in C11, zero dependencies.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages