# Brevi
> Brevi is a programming language for AI models to write. It reads like Python, compiles to Rust for native speed, and reports each error as one short JSON line with a suggested fix. On its benchmark, models finish tasks in Brevi with 5 to 21% fewer tokens than in Python, counting retries.
## Docs
- [Language guide](https://brevi-lang.org/guide.md): the core language in one page, written for models. Read it once before writing Brevi.
- [Reference](https://brevi-lang.org/reference.md): details for string braces and format specs, list methods and Python spellings. Look up one name with `brevi guide WORD` instead of reading it all.
- [Libraries](https://brevi-lang.org/libraries.md): using other .bv files, Rust crates (`use sha2 "0.10"` plus `rust fn`) and Python (`py fn`) from Brevi.
- [Agent skill](https://brevi-lang.org/skill.md): setup and the write, check, fix loop for coding agents.
- [Benchmarks](https://brevi-lang.org/bench.md): tokens per finished task against Python, with method and raw logs.
## Optional
- [Full text](https://brevi-lang.org/llms-full.txt): the guide, reference, libraries and skill in one file.
# Brevi language guide (for models)
Brevi compiles to Rust. Write it like Python: one statement per line, indented blocks after a line ending in `:` (a short block can follow the `:`), `#` comments. Only function parameters need types.
A file can be just statements or define `fn main()`. Top-level names are shared by every function, like Python globals without `global`; a function whose body starts by setting a name (`i = 0`) has its own local.
`use "util.bv"` brings in another file's definitions. This guide is the core: `brevi guide WORD` prints every line about a name, and `brevi guide Strings`, `Lists`, `Python` or `Libraries` a section of details.
```
nums = stdin().ints()
print("total {nums.sum()}, mean {(nums.sum().float() / nums.len().float()).fmt(2)}")
```
## Types
`int` (64-bit), `f64`, `bool`, `str`, `[T]` list, `{K: V}` map (sorted by key), `(A, B)` tuple, `dec` exact decimal, `big` exact integer, `Date`, `Json`, `heap[T]` min-heap.
Values act as copies. A `mut` parameter edits the caller's variable; other parameters are local copies.
Tuples, lists and strings compare element by element: `pairs.sort_by(p => (-p.1, p.0))`. `p.0` is a tuple field (`p[0]` also works).
```
type Point:
x: f64
y: f64
label: str = "" # default
enum Shape:
Circle(r: f64)
Rect(w: f64, h: f64)
Empty
p = Point(x: 1.0, y: 2.0) # or Point(1.0, 2.0)
s = Rect(w: 2.0, h: 3.0)
```
## Functions
```
fn area(s: Shape) -> f64 = match s:
Circle(r) => 3.14159 * r * r
Rect(w, h) => w * h
Empty => 0.0
fn load(path: str) -> [str]:
text = read(path) # can fail; the error passes up
if text.is_empty(): fail("empty file")
text.lines() # last line is returned
fn bump(mut xs: [int]):
for x in xs: x += 1 # edits caller's list
```
Errors pass up automatically, no `?`: a function using `fail(msg)` (like Python's raise), `read` or `s.int()` can fail, and so can its callers. Handle a failure with `.or(default)` (`s.int().or(0)`) or `match f(x):` with arms `Ok(v) => ...` and `Err(msg) => ...`.
## Statements
`x = 1` creates or changes a variable (`x: [str] = []` gives a type), `a, b = b, a + b`, `x += 1`, `return v`, `break`, `continue`, `assert cond`.
`if c:` / `elif d:` / `else:` with Python truth (`if xs:`, `if not s:`), `for x in xs:`, `for k, v in m.pairs():`, `for i in 0..n:`, `while c:`.
`if` and `match` are values: `y = if a > b: a else: b`. A variable set in every branch of an `if`/`else` or `match` can be used after it; one first set inside a loop ends with the loop, so set it before the loop.
Match arms are one expression or statement, or an indented block after `=>`. Patterns: literals, `_`, names, `Variant(a, b)`, `(a, b)`.
## Operators
`+ - * / // %` (int `/` and `%` round toward zero, `//` rounds down), `== != < > <= >=`, `x in xs` and `x not in xs` (list item, map key or substring), `and` `or` `not`, `a ~= b` (float equal within 1e-6). `+` joins strings and lists.
int and f64 never mix: convert with `.float()` or `.int()` (a whole-number literal next to an f64 is an f64: `x * 2`).
Strings interpolate `"{name} is {age}, {xs.len()} items"` and take Python format specs: `"{x:.2f} {n:03d} {s:<10}"`. In double quotes `{{` is ONE brace. For JSON or template text use `'single quotes'`: no `\"` needed and `{` stays plain unless a name follows (`'{"id":{i}}'`, `'{{#each '`). Details: `brevi guide Strings`.
## Collections
`xs[-1]`, slices `xs[a:b]` `s[1:]`, `xs[i] = v`, `m[k] += 1`, `m[k].push(x)`, `books[id].shelf -= 1` change the stored item (`b = books[id]` is a copy). A missing map key reads as the type's zero; `m.has(k)` tells.
Empty values `xs = []`, `m = {}`, `h = heap()` need no type.
Comprehensions (`[*xs for xs in xss]` joins lists), `len sum min max sorted any all` and tuple map keys (`dist[(x, y)]`) work as in Python.
Lambdas: `x => x * 2`, `(a, b) => a + b` (also unpacks a tuple item).
## Methods
Every call has parentheses: `xs.first()`.
- str: `len words lines chars split(sep) slice(a, b) find(s) lower upper trim contains starts_with ends_with replace(a, b) repeat(n) is_empty is_digit is_alpha is_space code partition(sep)`, and `int() float() ints() big() dec()`, which can fail
- list: `len is_empty push(x) pop insert(i, x) remove(i) first last slice(a, b) map(f) filter(f) any(f) all(f) count(f) fold(init, f) sort sort_by(f) rev take(n) skip(n) enumerate zip(ys) sum min max contains(x) index_of(x) join(sep) unique repeat(n) step(n)`
- map: `len is_empty has(k) get_or(k, default) remove(k) keys values pairs`
- int and f64: `abs float int fmt(decimals)`; f64 also `sqrt round floor ceil`; int also `big char`
Most methods return a new value (`ys = xs.sort()`); as a statement `xs.sort()` sorts xs, and `push pop insert remove` change it in place. `xs.max()` can fail (`.or(0)`); `find` and `index_of` give -1 when absent; `a..b` excludes b.
Python spellings work too (`range()`, `.append()`, `.items()`, `", ".join(xs)`, `lambda x:`, `sorted(xs, key=f)`): `brevi guide Python`.
## Built-in functions
`print(a, b)`, `str(x)`, `read(path)`, `write(path, text)`, `args()`, `input()` (one line), `stdin()` (all text), `lines()` (stdin lines), `fail(msg)`, `abs(x)`, `min(a, b)`, `max(a, b)`.
## Data formats
- `json(text)` gives a `Json`. Read it directly, no match needed: `j["key"][0]` (missing gives `Null`), `j.text()` (a string's text, `""` for null, other values as JSON), `j.int()`, `j.dec()`, `j.float()` (0 unless a JSON number, so `"7"` gives 0), `j.is_null()`, `j.has(k)`, `j.keys()`, `j.len()`, `for x in j["items"]:`, `if j["ok"]:` (null, false, 0, "" and [] are false), `j["tier"] == "gold"`, `m[j["id"]]`. To tell kinds apart, match on `Null`, `Bool(b)`, `Num(n)` (f64), `Str(s)`, `Arr(xs)`, `Obj(m)`. `str(j)` writes compact JSON. `x.json()` turns any value into Json. A map literal whose values differ in type is a Json object with keys in written order: `str({"id": 1, "name": n})` gives `{"id":1,"name":"ann"}`.
- Regexes (Python syntax: `\d \w \s . [a-z] [^x] * + ? {2,4} *? ( ) (?: ) | ^ $ \b`): `s.re_full(p)` (whole string) and `s.re_find(p)` (first match) give `[whole, group1, ...]`, or `[]` when there is no match; `s.re_all(p)` gives one such list per match; `s.re_list(p)` gives each match's text (a tokenizer: `s.re_list("\d+|[A-Z]+\d*|\S")`); `s.re_sub(p, "\1")` or `s.re_sub(p, g => g[1].upper())`; `s.re_split(p)`. Write `"\d+"` (unknown escapes keep their backslash).
- `csv(text)` gives `[[str]]`, handling quoted fields and `""`.
- `date("2024-02-29")` gives a `Date` with `days` (since 1970-01-01), `add_days(n)`, `year month day weekday` (1 = Monday); `str(d)` is `YYYY-MM-DD`.
- `big` is an exact integer of any size: `"123...".big()`, `n.big()`, with `+ - *` and comparisons.
- `dec` is an exact decimal (9 places) for money: `"19.99".dec()`, `0.dec()`, `+ - * /`, unary `-`, and ints mix in (`0 - p`, `p * 2`, `t += 1`, `p > 0`), `sum`, `fmt(2)` and `round(2)` (round half to even), `float int round`.
- `heap()` is a min-heap of any comparable values: `h.push(x)`, `h.pop()` (smallest; tuples compare in order), `first len is_empty`.
## Tests
```
test "area of unit circle":
assert area(Circle(1.0)) ~= 3.14159
```
## Commands
`brevi run|test|check f.bv`, `brevi build f.bv -o app`, `brevi patch f.bv p.bv` (replaces each `fn`/`type`/`test` by name), `brevi fix f.bv` (applies suggested renames).
Errors are one JSON line each, `{"line":N,"msg":...,"fix":...}`. To fix one, change only the lines it names; do not rewrite the whole file.
# Brevi reference (for models)
Details kept out of the core guide, which every session reads. `brevi guide SECTION` prints one section; `brevi guide WORD` prints every line about a name.
## Strings
Strings interpolate: `"{name} is {age}"`, `"{-x} {xs.len()} {p.0}"` (`{` followed by a letter, `(`, `-`, `!` or `"` starts an expression, and strings may appear inside: `"{m["k"]}"`). Any other `{` is a plain brace; `\{` and `{{` always are, so `"{{"` is ONE brace (write `'{{'` for two); template tags such as `"{{#each "`, `"{{/if}}"` and `"{{else}}"` keep both braces.
Python format specs work inside `{}`: `"{x:.2f} {n:03d} {name:<10} {total:>8,}"`.
Escapes: `\n \t \r \0 \" \\ \{ \}`.
`'single quotes'` need no `\"` and keep `{` plain unless a name or `(` follows (`{{` is two braces), so JSON and template text is easy: `'{"id":{i},"error":"not found"}'`, `t.starts_with('{{#each ')`.
`"a".code()` is 97 and `97.char()` is "a". `"7".digit()` is 7 (-1 if not a digit). `len` counts characters; `chars` gives a list of one-character strings; `words` splits on any run of whitespace; `ints()` gives all whitespace-separated ints.
`s.slice(a)` runs to the end and `s.slice(i, i + 1)` is one character; negative bounds count from the end. `s.int().or(-1)` tests a number. `(a, _, b) = s.partition("=")`.
## Lists
`[0].repeat(n)` is n zeros; `unique` sorts and drops repeats; `(0..10).step(3)` is [0, 3, 6, 9]; `(0..n).rev()` counts down. `join(sep)` takes any items.
`xs.sort_by(f, desc)` sorts largest first when `desc` is true, keeping ties in order (as `sorted(xs, key=f, reverse=desc)`).
`first`, `last` and `pop` return the item itself and stop the program on an empty list; `min` and `max` fail on an empty list. `pop` and `remove(i)` return the removed item. In map/filter/any/all/count/sort_by, `(a, b) => b - a` unpacks a tuple item. `and`/`or` short-circuit.
`2.5.fmt(2)` is "2.50"; f64 `round(n)` rounds to n places.
## Type details
`let (a, b) = t` or `(a, b) = t` destructures a tuple. `Point(x, y)` works as a constructor when variables x and y exist. A `mut` parameter is called as `f(x)`. A return type is optional (`fn f(x: int) = x * 2`); write `-> T` if an error asks.
A missing map key reads as the type's zero (0, 0.0, "", [], dec 0). `print(a, b)` puts a space between.
## Python
Python spellings models write by habit are accepted: `True False`, `a if c else b`, `range(a, b, step)`, `for c in s`, `enumerate(xs) int(s) ord chr reversed`, `lambda x: x * 2`, `min(xs, key=f)`, `sorted(xs, key=f, reverse=True)`, `print(x, end="", sep=", ")`, `append strip split() startswith endswith isdigit items index get(k, d)`, `", ".join(xs)`, `&& || !` for `and or not`. `f"..."` is a plain string (Brevi strings already fill in `{x}`). Also `set(xs)` (a sorted list without repeats) with `s.add(x)`, `xs.extend(ys)`, `xs[::-1]` and `xs[::2]`, `divmod(a, b)`, `round(x)` and `round(x, 2)`, `enumerate(xs, 1)`, `m.setdefault(k, []).append(x)`, `defaultdict(int)`, `"%5.2f %s" % (x, s)`, `"{:>5} {}".format(a, b)`, `math.sqrt floor ceil`, `for line in sys.stdin`; `import sys`, `math`, `re` and `from collections import ...` lines are ignored.
Functions are values: `ops = [add, mul]`, `for (name, f) in [("+", add)]: f(1, 2)`, `ops[0](3, 4)`. A map of functions works when at least one value is a named function: `ops = {"+": add, "-": lambda a, b: a - b}`, then `ops[op](a, b)`.
`None` works for int, float, str and tuple values: `best = None`, `if best is None or v > best: best = v`, `return None` from `-> int`. It is a reserved value, not an Option, so it cannot hold a user type.
## Tools
`brevi build f.bv -o app --release` builds an optimized binary. Runtime errors print `{"e":"runtime","line":N,"msg":...}`.
`brevi emit f.bv` shows the Rust. `--explain` on any command adds the guide lines under each error. `brevi check f.bv --fast` skips rustc. `del fn name` in a patch file removes a function.
A fixed typo prints `{"line":N,"fixed":"totl -> total"}`.
`allow read, write` at the top declares what a program touches (`read`, `write`, `native` for rust fn, py fn and crates). `brevi run --sandbox` and the MCP tools refuse anything undeclared; a program with an `allow` line is held to it on every command.
# Brevi libraries (for models)
## Libraries
`use "geom.bv"` brings in another Brevi file's `fn`, `type`, `enum` and `test` items (path relative to this file; its errors add `"file"`).
For what Brevi lacks, write a function in Rust (any crate) or Python. Parameters and return type are Brevi types; call it like any function.
```
use sha2 "0.10" # a crate: use NAME "VERSION" "FEATURES" (built once, then cached)
rust fn sha256(s: str) -> str: # Rust body: str is String, [T] Vec, {K: V} a map like HashMap, mut x is &mut
use sha2::{Digest, Sha256};
format!("{:x}", Sha256::digest(s.as_bytes()))
rust fn hex(s: str) -> int !: # with !, return Ok(v); ? works on any error; Err("msg".into()) fails
Ok(i64::from_str_radix(&s, 16)?)
py fn mean(xs: [f64]) -> f64: # Python body; values cross as JSON (a type is a dict)
import statistics
return statistics.mean(xs)
py fn sq(x: int) -> int = x * x # one-line form, also for rust fn
```
A `py fn` can fail: a Python exception becomes its error. Top-level `rust:` and `py:` blocks hold shared Rust items or Python code. Give crate versions you know: without one cargo takes the newest.
# Brevi
Brevi is a programming language for models to write. It reads like Python, compiles to Rust, and reports every error as one short JSON line with a suggested fix.
## Setup
Check that `brevi --version` works. If it does not, run the repository's `install.sh`: it fetches the Rust toolchain if `rustc` is missing (Brevi compiles through it), then a prebuilt `brevi` from the latest release, or builds it with `cargo install --git https://github.com/ets-public/brevi`.
## Before writing code
Run `brevi guide` once and read the whole guide it prints. It is short and matches the installed compiler. Rare details are not in it: `brevi guide WORD` prints the lines about one name (`brevi guide partition`) and `brevi guide Strings` the full brace and format rules.
## Loop
1. Write the whole program to `NAME.bv`. Programs read stdin and write stdout.
2. Run `brevi run NAME.bv < input.txt`, or `brevi check NAME.bv` to compile without running, or `brevi test NAME.bv` for `test` blocks.
3. Each error is `{"line":N,"msg":...,"fix":...}`. Change only the lines it names, then run again. Do not rewrite the whole file.
4. `brevi fix NAME.bv` writes every suggested rename into the file. Adding `--explain` to any command adds the guide lines for each error.
5. To replace a whole function, write just the new `fn` (or `type`, `enum`, `test`) to a patch file and run `brevi patch NAME.bv patch.bv`.
## Notes
- `brevi check NAME.bv --fast` runs only Brevi's own checks, in milliseconds, without rustc.
- `brevi build NAME.bv -o app --release` makes a native binary.
- When Brevi has no built-in for something (hashing, HTTP, a data science library), run `brevi guide Libraries`: write that one function as a `rust fn` (with `use CRATE "VERSION"`) or a `py fn`, and keep the rest in Brevi. Split big programs into files with `use "other.bv"`.
- Python habits are accepted (`True`, `.append()`, `range()`, `", ".join(xs)`), so write what comes naturally and let the errors guide you.