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