Everything in Brine goes through one command, brine: creating projects, checking, building, running, testing, formatting and debugging. Every command that reports diagnostics takes --json, and exit codes are stable: 0 success, 1 your code has a problem, 2 the command line or setup has a problem, and 101 a program panicked.
brine --helpbrine — a verified, natively parallel systems language
Usage: brine <command> [options]
Project: new <name> Create a new project in ./<name> init Create a project in the current directory
Build and run: run [file] [-- args] Build and run (default: the current project) --release optimize more --threads <n> worker threads for `par` (default: all cores) --ranks <n> run <n> cooperating processes (the `world` module) --target wasm build WebAssembly and run it with Node.js build [file] Build a native executable --release | --debug optimization profile (default: dev) -o <path> output path --emit-c also write the generated C next to the output --target <t> host (default), wasm (a .wasm module) or web (a page) check [file] Type-check and verify without building (fastest) --report list every runtime check the prover could not remove test [file] [filter] Run `test "...":` blocks in parallel (also --target wasm) debug [file] [-- args] Debug build, then launch gdb or lldb
Tools: fmt [files] [--check] Format source files (default: the whole project) explain <code> Explain an error code, e.g. `brine explain E0601` guide Print the language quick reference doctor Check that your toolchain works version Print the version
Global options: --json Machine-readable output (one JSON object per line) --color <when> auto | always | never -q, --quiet Only print errorsCommands at a glance
Section titled “Commands at a glance”| Command | What it does | Flags |
|---|---|---|
brine new <name> |
create a project in ./<name> |
|
brine init |
create a project in the current directory | |
brine run [file] [-- args] |
build and run | --release, --debug, --threads N, --ranks N, --target wasm, --typed |
brine build [file] |
build an executable, a .wasm module or a web folder |
--release, --debug, -o PATH, --emit-c, --target T, --typed |
brine check [file] |
type-check and prove, no code generation | --report, --typed |
brine test [file] [filter] |
run test "...": blocks in parallel |
--release, --target wasm, --typed |
brine debug [file] [-- args] |
debug build, then gdb or lldb | |
brine fmt [files] |
format source files | --check |
brine explain [code] |
explain an error code, or list them all | |
brine guide |
print the language quick reference | |
brine doctor |
check the toolchain | |
brine version |
print the version |
Every command also accepts the global options. A flag a command doesn’t know is an error, with a suggestion:
error: unknown option `--reprot` (did you mean `--report`?)Which file a command works on
Section titled “Which file a command works on”run, build, check, test and debug take an optional file:
| You pass | Brine uses |
|---|---|
| nothing | the project around you: the nearest brine.toml in this directory or a parent, and its main file (src/main.br unless [build] main says otherwise) |
file.br |
that file, as a single-file program (imports load next to it) |
a directory with brine.toml |
that project |
a directory with main.br and no brine.toml |
that folder’s main.br |
Outside a project and without a file, Brine says what to do:
error: no brine.toml found in this directory or any parent; pass a file (`brine run hello.br`) or create a project with `brine new <name>`See Projects and brine.toml for where each kind of build puts its output.
brine new
Section titled “brine new”brine new hello Created project `hello`
cd hello brine runCreates hello/ with brine.toml, src/main.br, AGENTS.md, README.md and .gitignore, and runs git init unless the folder is already inside a Git repository. The name must start with an ASCII letter or _ and contain only letters, digits, _ and -. The folder must not exist yet. With --json it prints {"created":"hello"}. Projects and brine.toml shows the files.
brine init
Section titled “brine init”mkdir myapp && cd myappbrine init Created project `myapp` (brine.toml, src/main.br, .gitignore, AGENTS.md, README.md)Like brine new, in the current directory. The project is named after the folder (characters that aren’t allowed become _). Files that already exist are kept, and the message lists only the ones it wrote. It fails if the directory already has a brine.toml.
brine run
Section titled “brine run”Builds the program, then runs it. Status lines go to stderr; the program’s own output is untouched.
brine run Built hello v0.1.0 [dev] in 2.05s Running target/dev/hello.exeHello, brine![0, 1, 4, 9, 16, 25, 36, 49, 64, 81]A second run with nothing changed skips the compile and the link:
Fresh hello v0.1.0 [dev] in 0.02s Running target/dev/hello.exeHello, brine
| Flag | Effect |
|---|---|
--release |
build with the release profile (optimized C) |
--debug |
build with the debug profile (no optimizations, debug info) |
--threads <n> |
worker threads for par (sets BRINE_THREADS for the program); a positive number |
--ranks <n> |
start n cooperating processes for the world module, 1 to 1024 |
--target wasm |
build a WebAssembly module and run it with Node.js, or with BRINE_WASM_RUNNER; needs wasi-sdk. Can’t be combined with --ranks. --target web is refused: open a web build in a browser. See WebAssembly |
--typed |
require a written type on every variable, as [check] typed does |
Passing arguments
Section titled “Passing arguments”Everything after -- goes to the program:
fn main(): print(args())brine run argv.br -- one --two 3 Built argv [dev] in 0.10s Running C:/Users/you/AppData/Local/Temp/brine-cache/465ef71a47ea2199/dev/argv.exe["one", "--two", "3"]With a file, plain words after it are passed too (brine run argv.br a b), but anything that starts with - would be read as a Brine flag, so use --. Inside a project, always use --: brine run foo means “run the file foo”.
Several processes
Section titled “Several processes”brine run world.br --ranks 4 Built world [dev] in 0.10s Running C:/Users/you/AppData/Local/Temp/brine-cache/88f5d4ab53aec8ad/dev/world.exe (4 ranks)4 ranks, total = 499999500000, squares = [0, 1, 4, 9]after one lap: token->0->1->2->3rank 3 received [[0, 3], [1, 3], [2, 3], [3, 3]]Each rank is a process on this machine, connected over loopback TCP. Only rank 0’s output is shown. If one rank fails, Brine stops the others half a second later and returns the first non-zero exit code. See Multiple processes.
Exit code
Section titled “Exit code”brine run returns the program’s exit code: 0 when main returns normally, the value main returns if it returns an int (clamped to 0-255), 1 when main returns Err, and 101 after a panic:
fn main(): let xs = [1, 2, 3] let i = len(args()) print(xs[i + 5]) Built panic [dev] in 0.09s Running C:/Users/you/AppData/Local/Temp/brine-cache/fce079b2338829bc/dev/panic.exe
panic: index 5 is out of bounds for an array of length 3 --> panic.br:4:11That run exits with 101. If the program doesn’t compile, nothing runs and the exit code is 1.
brine build
Section titled “brine build”Builds an executable without running it.
brine build --release Built hello v0.1.0 [release] in 2.74s Output target/release/hello.exe| Flag | Effect |
|---|---|
--release |
optimized build through C at -O3, one translation unit |
--debug |
debug build: native code with DWARF on x86-64 Windows/Linux and AArch64 Linux, C with -O0 -g elsewhere (Debugging) |
-o <path> |
where to write the executable; on Windows .exe is added when the name has no extension (native builds only) |
--emit-c |
also write the program as one C file next to the executable (hello.exe → hello.c) |
--target <t> |
host (the default, also native), wasm (a WASI module, also wasm32 or wasi) or web (a folder with index.html, brine.js and program.wasm). See WebAssembly |
--typed |
require a written type on every variable |
Where the executable goes:
| Build | Output |
|---|---|
project, no -o |
target/<profile>/<name> (target/dev/hello.exe) |
single file, no -o |
the current directory, named after the file (brine build tool.br → ./tool.exe) |
-o path |
path |
--target wasm writes <name>.wasm instead (target/dev-wasm/hello.wasm in a project), and --target web a folder named <name>-web (target/dev-web/hello-web/).
--emit-c writes the whole program as the C backend would generate it, even when the executable itself was built by the native backend:
Wrote target/dev/hello.c Fresh hello v0.1.0 [dev] in 0.02s Output target/dev/hello.exeWith --json, a successful build prints {"built":"target/dev/hello.exe"}.
brine check
Section titled “brine check”Parses, type-checks and runs the prover, without generating code. It is the fastest feedback loop: run it after every edit.
fn total(xs: [int]) -> int: var s = 0 for i in 0..len(xs): s += xs[i] return s
fn pick(xs: [int], k: int) -> int: return xs[k]
fn main(): let xs = [1, 2, 3, 4] print(total(xs), pick(xs, 2))brine check sum.br Checked sum in 0.00s — 1 runtime check proven unnecessary, 1 remain (see --report)| Flag | Effect |
|---|---|
--report |
list every runtime check the prover couldn’t remove, one per line |
--typed |
require a written type on every variable (E0337) |
brine check sum.br --reportsum.br:8:12: index bounds check `xs[k]` Checked sum in 0.00s — 1 runtime check proven unnecessary, 1 remainxs[i] in total was proven in bounds; xs[k] in pick wasn’t, because nothing says k is in range. A requires 0 <= k < len(xs) would remove it. See Runtime checks.
brine check doesn’t need a main function, so it also works on library modules. It exits with 0 when there are no errors (warnings don’t count) and 1 when there are.
With --json:
{"runtime_check":"index bounds check","location":"sum.br:8:12","code":"xs[k]"}{"ok":true,"checks_proven":1,"checks_remaining":1}brine test
Section titled “brine test”Builds the test "...": blocks of a file or project and runs each one in its own process, in parallel.
fn double(x: int) -> int: return x * 2
test "double doubles": assert double(4) == 8
test "double of zero": assert double(0) == 1
fn main(): print(double(21))brine test t.br Built tests [dev] in 0.10stest double doubles ... oktest double of zero ... FAILED
---- double of zero ----
panic: assertion failed: double(0) == 1 --> t.br:8:5
test result: FAILED. 1 passed; 1 failed; finished in 0.03s| Argument or flag | Effect |
|---|---|
file |
a .br file or a directory; without one, the current project |
filter |
run only the tests whose name contains this text (brine test t.br doubles, or brine test doubles in a project) |
--release |
build the tests with the release profile |
--target wasm |
build the tests as a WebAssembly module and run each one with Node.js (WebAssembly) |
--typed |
require a written type on every variable |
Details:
- A test passes when it finishes without a panic. The exit code is
0when every test passed and1when any failed or the tests didn’t compile. - Each test process gets
BRINE_THREADS=2, so many tests can run at once without each starting a thread per core, andBRINEset to the path of thebrinerunning the tests, so a test can run the compiler. - Results are printed in source order, whatever order the tests finish in.
--quietprints only the failures and the summary. - A filter that matches nothing is not an error: it prints
(no tests match the filter; add one withtest “name”:)and exits with0.
With --json, one object per test:
{"test":"double doubles","ok":true,"seconds":0.021,"output":""}{"test":"double of zero","ok":false,"seconds":0.026,"output":"\npanic: assertion failed: double(0) == 1\n --> t.br:8:5\n"}brine debug
Section titled “brine debug”Makes a debug build, then starts gdb (or lldb, if there is no gdb) on it. Arguments after -- go to the program. brine debug takes no other flags.
brine debug shapes.brgdb gets Brine’s pretty-printers, so values print the way Brine prints them, and stepping skips the C runtime. Without gdb or lldb:
error: no debugger found (install gdb or lldb); the debug build is at <path>Debugging has a full session.
brine fmt
Section titled “brine fmt”Rewrites source files in the canonical format: 4-space indentation, one space around operators, at most one blank line, and lines up to 100 characters.
brine fmt # every .br file in the projectbrine fmt src/main.br # just these files (or directories)brine fmt --check # change nothing; exit 1 if a file would changewould reformat messy.br1 file checked, 1 need formattingWithout arguments it formats every .br file under the project root (or the current directory outside a project), skipping target/ and folders whose names start with .. A file with a syntax error is reported and left alone. See Formatting for what it changes.
| Flag | Effect |
|---|---|
--check |
report files that aren’t formatted (would reformat <file>, or {"unformatted":"<file>"} with --json) and exit with 1; write nothing |
brine explain
Section titled “brine explain”brine explain E0401E0401: assigning to an immutable binding
Variables declared with `let` (and loop variables) can't change. Declare thevariable with `var` if it needs to change:
var total = 0 total += 1Without a code, it lists every code with its title. With --json, it prints {"code":...,"title":...,"explanation":...} (or one {"code":...,"title":...} line per code for the list). An unknown code exits with 2. The same text is on the error codes page.
brine guide
Section titled “brine guide”Prints the language quick reference: the whole language on one page, built into the compiler, so it works offline and always matches the installed version. It’s the same text as the quick reference page.
brine guide | lessbrine doctor
Section titled “brine doctor”Checks that Brine can build and run programs on this machine.
brine doctor ✓ brine 0.4.0 (windows x86_64) ✓ C compiler gcc — gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r1) 16.2.0 ✓ parallelism 32 hardware threads ✓ local sockets needed for `brine run --ranks` ✓ build + run compiled and ran a parallel program ✓ debugger gdb ✓ WebAssembly C:/Users/you/tools/wasi-sdk-34.0-x86_64-windows\bin\clang.exe| Check | What it tests |
|---|---|
brine |
the version and the platform |
| C compiler | BRINE_CC, CC, or the first of clang, gcc, cc that runs |
| parallelism | how many hardware threads par will use by default |
| local sockets | that a local TCP port can be reserved (for --ranks) |
| build + run | builds and runs a small par sum program in a temp folder and checks its output |
| debugger | gdb or lldb, for brine debug (optional: a missing debugger doesn’t fail) |
| WebAssembly | wasi-sdk’s clang, through WASI_SDK_PATH or /opt/wasi-sdk, for --target wasm and web (optional) |
It exits with 1 if a required check fails. With --json it prints one {"check":...,"ok":...,"detail":...} line per required check.
brine version
Section titled “brine version”brine versionbrine 0.4.0brine --version and brine -V print the same; brine version --json prints {"version":"0.4.0"}. brine help, brine --help and brine -h print the help, as does brine alone.
Global options
Section titled “Global options”These work with every command, before or after it (brine --json check and brine check --json are the same). Flags that take a value also accept =: --color=never.
| Option | Effect |
|---|---|
--json |
machine-readable output on stdout, one JSON object per line; status lines are not printed |
--color <when> |
auto (the default: color when stderr is a terminal and NO_COLOR isn’t set), always or never |
-q, --quiet |
print only errors: no Built/Running lines, no warnings |
JSON output
Section titled “JSON output”In text mode, diagnostics and status lines go to stderr. With --json, each diagnostic is one line on stdout:
{"severity":"error","code":"E0401","message":"cannot assign to `x` because it was declared with `let`","file":"bad.br","line":3,"column":5,"end_line":3,"end_column":6,"label":"immutable","related":[{"file":"bad.br","line":2,"column":9,"end_line":2,"end_column":10,"label":"declared here"}],"notes":[],"help":["declare it with `var x = ...` to make it mutable"],"fixes":[]}| Field | Meaning |
|---|---|
severity |
"error" or "warning" |
code |
the stable code ("E0401"), or null for command-line errors |
message |
the one-line message |
file, line, column, end_line, end_column |
the primary span (1-based; the end is exclusive) |
label |
the text under the primary span |
related |
other spans, each with its own location and label |
notes, help |
the = note: and = help: lines |
fixes |
machine-applicable edits: {line, column, end_line, end_column, replacement} |
Errors in the command line itself also come out as JSON when --json is given:
{"severity":"error","code":null,"message":"unknown command `chek`; did you mean `brine check`? (see `brine help`)"}Other JSON records each command prints:
| Command | Records |
|---|---|
check |
{"ok":true,"checks_proven":N,"checks_remaining":N}; with --report, a {"runtime_check","location","code"} line per check |
build |
{"built":"<path>"} |
test |
{"test","ok","seconds","output"} per test |
fmt --check |
{"unformatted":"<path>"} per file |
new |
{"created":"<name>"} |
explain |
{"code","title","explanation"} |
doctor |
{"check","ok","detail"} per check |
version |
{"version":"0.4.0"} |
For run, the program’s own output is not wrapped: it passes through as is.
Exit codes
Section titled “Exit codes”| Code | When |
|---|---|
0 |
success |
1 |
the program has errors (check, build, run, test, debug); a test failed; fmt --check found a file to reformat or fmt hit a syntax error; doctor found a problem; a program’s main returned Err |
2 |
a problem with the command itself: unknown command or option, a missing or bad value, a file that doesn’t exist, no project found, an invalid brine.toml, an unknown error code for explain |
101 |
a program panicked (overflow, an out-of-bounds index, a failed assert, …) |
| other | brine run returns the program’s own exit code (fn main() -> int) |
Warnings never change the exit code.
Internal commands
Section titled “Internal commands”Three more commands exist for working on the compiler itself. They aren’t in --help and may change in any release:
| Command | Prints |
|---|---|
brine lex [--comments] <file> |
the tokens |
brine parse <file> |
the syntax tree |
brine emit-c [--tests] [--release|--debug] <file> [-o out.c] |
the generated C (this is how the bootstrap seed is made) |