tripon[github]

A compiled language for low-resource systems.Built to be read by agents.

Tripon is a small, statically typed language for machines with very little room: firmware, microcontrollers, bootloaders. There is no null and no implicit conversion here, and the mistakes that are undefined behavior in C become traps that name their code and their line. The whole toolchain is one static binary, and every diagnostic it prints is also valid JSON.

git clone https://github.com/TriponOSS/tripon && cd tripon && make

Needs a C99 toolchain and nothing else.

Red to green in one round trip

This is a real session against the compiler in this repository. Line 7 of main.tp multiplies an i32 by an f64, and ralt reports the exact span, so the fix is a computation rather than a guess.

ralt-session — an ascii.rest piece written for this siteoutput copied from a live run

Machine-readable by default

ralt check --json writes one array, in lex → parse → typecheck order. Nothing is batched behind the first real error.

Codes that never move

E0202 means the same thing today and in ten releases. The table is append-only: never renumbered, never reused.

A fix hint in the payload

Every diagnostic carries its healer's hint — or run ralt explain E0202.

the same check, machine format
ralt check main.tp --json
[
  { "code": "E0202", "kind": "error", "name": "type-mismatch", "file": "main.tp", "line": 7, "col": 25, "len": 7, "msg": "i32 and f64 do not share an operator; no implicit conversion exists", "hint": "types must match exactly; no implicit conversion exists; use cast<T>(x)" }
]

No undefined behavior, and not as a slogan

None of this is a promise in the documentation. The code generator emits these checks on both backends, so every program built by ralt carries them.

the classicin Triponoutcome
integer overflowtraps, exit 101, E9001 — or wraps with -fastdefined
division and modulo by zerotraps, E9003defined
index out of boundsbounds-checked on every access, traps E9002defined
read of an uninitialized locallocals are zero-initializeddefined
an ignored errora discarded result is a compile error, E0205won't build
a silent narrowing or wideningcast<T>(x) or nothing; E0207 otherwisewon't build
what a trap looks like, and what it exits with
ralt run overflow.tp
tripon trap E9001 integer-overflow-trap at overflow.tp:4
echo $?
101

A language you already know the shape of

Braces, semicolons, let and fn: there is nothing to relearn, and nothing underneath that an agent could reasonably read two ways.

examples/fib.tp
use tier core;

fn fib(n: i32) -> i32 {
    let a = 0;
    let b = 1;
    let idx = 0;
    while idx < n {
        let next = a + b;
        a = b;
        b = next;
        idx += 1;
    }
    return a;
}

fn main() -> i32 {
    let n = 0;
    while n < 10 {
        print_i32(fib(n));
        n += 1;
    }
    return 0;
}
ralt run examples/fib.tp
ralt run examples/fib.tp
0
1
1
2
3
5
8
13
21
34

Data lives by value, variants carry payloads, and match is exhaustive — a missing arm is E0206. Lossy conversions are explicit: cast<i16>(x) truncates by definition.

  • i8 through u64, isize, usize, f32, f64, bool, str, void
  • result<T, E> with ? propagation; an ignored result will not build
  • opt<T> with some and none; passing a null is E0204
  • fixed arrays are bounds-checked by construction, and len is compile-time

Pick how much runtime you can afford

Firmware is not a server, so you declare what each file is allowed to use. Reach for something above your tier and the build fails with E0301 — never “kinda works, probably”.

0use tier kernel;ints, bools, structs, enums, fn, fixed arrays, extern C, constfirmware, bootloaders, bare metal
1use tier core;+ arena, slices, result / opt / ?, match, modulesmicrocontrollers, tiny CLIs
2use tier std;+ references, String / Vec / Map, closures, genericsnormal applications
3use tier full;+ coroutines and async, the self-host runtimeeverything else
ascii.rest/boot-log — the tier-0 targetno allocator, no runtime, no libc needed

The kernel tier has no allocator at all, so malloc cannot exist there. A stdlib module declares its own tier, and a use raises your requirement rather than degrading it.

a tier violation reads like any other diagnostic
ralt check firmware.tp
firmware.tp:12:9: error: feature [arena] requires tier core; current tier is kernel
  [E0301 tier-violation] feature requires a higher tier; raise the tier target or rewrite the call

Milliseconds to build, kilobytes to ship

We optimize build speed first, because it is what you pay on every iteration. The default pipeline runs no optimizer at all; -O fast turns the passes on once the types prove them safe.

sh bench/build_bench.sh
build full pipeline : 333 ms
check only          : 4 ms
emit c only         : 5 ms
binary size         : 17728 bytes

Measured cold-cache by bench/build_bench.sh, which runs on any box. ralt check is the loop an agent lives in — four milliseconds.

P0
Spec — grammar, contract, frozen error table
P1
Bootstrap compiler in C99 — lex, parse, typecheck, codegen
P2
Agent tooling — fmt, doc, lsp, deps, tokens oracle
P3
Self-host — lexer and parser ports replay their oracles byte for byte; the typechecker port is next
P4
Native x86-64 — scalars and floats at byte parity; aggregates next

Phases are tracked in PLAN.md, not a roadmap slide. Determinism is tested: the same source compiles byte-identically on every build, with gcc and clang.

One binary, six directories

spec/ is the source of truth, not the C code. Each pass is one C file with no dependencies, and selfhost/ holds the same passes written in Tripon.

ascii.rest/file-treeentries are the real repository
the contract, tested
make test
42 checks passing, gcc and clang
sh tests/determinism.sh
byte-identical on every build

Changing a semantic means changing the spec first, then the code, then the goldens in tests/expected/ — and the suite must pass under both compilers.

Read on

Tripon is MIT licensed and developed in the open at TriponOSS/tripon. Four bands of diagnostics, 23 codes in total, and 4 exit codes to branch on.