The torb command
Every subcommand of the toolchain, what it does today, and which of them are still planned.
Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite
torb is a single binary: the runtime, the compiler, the package manager, the test runner, the linter, the formatter and
the language server are all in it. That keeps a project's setup to one installation and one manifest.
Synopsis#
torb run [path|name] [...] Run a file or a program in the VM; --native builds and runs it
torb repl Read entries from standard input, run each in the VM, keep their bindings
torb check [path]... Check projects, workspaces or single files
torb build [path|name] Compile every program below the path, or the one named, through C
torb ir <path>... Print the typed IR the back end lowers
torb parse <path>... Check the syntax of files or directories
torb tokens <file> Print the tokens of a file
torb ast <file> Print the syntax tree of a file
torb highlight <file> Print the semantic tokens of a file as JSON, for an editor
torb natives --header Write torb_natives.h and machine_natives.c from the manifest of natives
torb manifest [path]... Evaluate every project.trb the paths reach in the sandboxed VM
torb docs <command> Check, index, and derive the documentation
torb doc [path]... The reference of a package: its public API and doc comments as a static site
torb format [path]... Write sources in the layout of the language; --check only reports
torb lint [path]... The rules of style the checker leaves alone; --fix writes their fixes
torb rename <Type>.<field>=<name>... [path]... Rename fields everywhere the checker resolves them
torb lsp The language server, over standard input and output, for an editor
torb debug The debugger, over standard input and output (the Debug Adapter Protocol)
torb canon [path]... Deprecated: runs torb format
torb test [path]... Run the *.test.trb files below the paths in the VM; --native builds them
torb new <path> A new package or app, from a template or the built-in scaffold
torb init The same, into the current, empty-enough directory
torb add <package>... Add a dependency to project.trb, resolve, lock and install it
torb remove <package>... Remove a dependency from project.trb and project.lock.trb
torb update [package]... Resolve every package, or the named ones, to the highest version allowed
torb install Fetch and verify every package project.lock.trb pins into the cache
torb publish Build and check the archive of a package, and publish it (--dry-run: build only)
torb lock [--check] Write the settings of every member into project.lock.trb; --check only compares
torb vendor Copy every registry package the lock pins into vendor/, as `path:` sources
torb audit Report the locked versions the registry yanked and the packages it deprecated;
--advisories <directory>: also the advisories that cover a pin
torb login Read a token of a registry from standard input, check it, store it in credentials.trb
torb logout Remove the stored token of a registry
torb owner list|add|remove <owner> [account] The members of an owner on a registry, and changes to them
torb yank <package> <version> --reason <text> Withdraw a version from new resolutions (--undo: take it back)
torb deprecate <package> [--use <package>] [--reason <text>] Flag a whole package as deprecated (--undo: take it back)
torb --version Print `torb <version>`: the one number of the toolchain, the language and std
Every command: --color auto|always|never colour where the output is a terminal, always, or nevertorb is the binary the installer puts on the PATH, and a command below runs in the directory
of a project - the one with project.trb - unless it names a path:
torb check .
torb test
torb format --check .An argument that starts with -- and is none of the flags of its subcommand is refused before anything runs, with
error: unknown flag <argument>, a pointer to torb --help and exit code 2, rather than looked for as a file or a
program of that name; a path that starts with -- is written ./--name. Behind the path of run every argument is
the program's own. torb --help prints the usage, and --help or -h among the flags of a subcommand prints that
command's usage on standard output, leaves with 0 and runs nothing - torb new --help makes no package called
--help, and torb run --help asks torb, while torb run tool.trb --help asks the program.
What it does#
--version#
Prints one line to standard output, torb 0.1.0, and leaves with 0. The number is the one of
RELEASE.md section 3: the toolchain, the language and the standard library share it, so it is
also what the language server names in its serverInfo and what the language line of a release in the index of a
registry is compared with.
--color#
Every command takes --color auto, --color always or --color never (also written --color=never), in front of the
command or anywhere among its own arguments - but not behind the path of torb run, where every argument is the
program's. torb colours what its output means, in the 16 colours of the terminal's own theme:
| What | Colour |
|---|---|
the word error | bold red, and its carets ^^^ red |
the word warning of torb lint | bold yellow, and its carets yellow |
| the message after the word | bold |
-->, the bar of the gutter and the line numbers | faint |
the = in front of a note | bold cyan |
a summary that found nothing, 3 files, no problems | green |
a summary that counts problems, 1 problem in 1 of 1 file | bold |
ok and FAILED of torb test, and its summary | green, bold red, and green or bold |
the name in torb --version, a type the REPL shows | bold, faint |
Colour never carries anything alone: every diagnostic starts with its word, the carets mark its span and every summary
counts in words, so a run without colour loses nothing - and it writes exactly the bytes it wrote before torb had
colours, which is what every golden file compares. The first of these rules that applies decides:
--color alwaysor--color never.FORCE_COLORorCLICOLOR_FORCEset to anything but empty or0: colour.NO_COLORset to anything but empty: no colour (no-color.org).auto, the default: colour where the output is a terminal whoseTERMis notdumb, decided for standard output and standard error each:torb check . 2> problems.txtwrites the file without colour.
On Windows a console shows the colours once torb has turned on its processing of escape sequences, which every
console of Windows 10 and later has; one that refuses gets plain text. Where COLORTERM is truecolor or 24bit and
the ground of the terminal is known - TORB_BACKGROUND=light or dark, or the last field of COLORFGBG - torb
writes the text colours of the brand in 24 bits instead (BRAND.md section 10): a 24-bit colour
chosen without knowing the background can vanish on it. The red of an error is never the brand's red.
torb test --native hands --color always or --color never to the test binary, which writes its own report; with
auto the binary decides the same way, on the same streams.
$ torb check --color always src/main.trb 2>&1 | cat -v
^[[1;31merror^[[0m^[[1m: Cannot find `prnt` here^[[0m
^[[2m-->^[[0m src/main.trb:1:1
^[[2m|^[[0m
^[[2m1 |^[[0m prnt "hello"
^[[2m|^[[0m ^[[31m^^^^^[[0m
^[[1m1 problem in 1 of 1 file^[[0mrun#
Runs a file, the only program below a directory (the working directory where nothing is named), or the program a name
names, in the bytecode VM inside torb, passing the rest of the command line to the program as Process.arguments()
and the program's three streams and its exit code through. An argument with a / or a \, one that ends in .trb,
and . and .. are paths; anything else is a program name. A script may hold top-level code because nothing imports
it.
torb run --native builds it into build/run/<key>/ instead and starts the binary build would have
written; the key is a hash of every source file that was read, so an unchanged program is not rebuilt. Both run the same
typed IR on the same runtime (torb run).
repl#
Reads entries from standard input - a line, or as many as it takes to close what the line opened, > and .
prompting each on standard error - checks each against the declarations and bindings of the entries before it, runs
it in the VM and keeps what it declares and binds. The value of an entry that ends in an expression is shown value
first, 3: Int64; messages name the entry (<entry 3>) and its own lines. A piped file is the same session as a
typed one (torb repl).
check#
Resolves every module, every import, every name in a type position and the type of every expression, and reports what is
wrong. It answers <n> files, no problems when everything holds, and otherwise one block per diagnostic with the line and
a caret under the span.
| Flag | What it adds |
|---|---|
--statistics | How many expressions of every module have a type, and how many are deferred to a later milestone |
--timings | The wall time of every pass, in the order they ran |
--every-target | Every program and test lowered once per target of the toolchain, without C: a native reached on a system it does not exist on |
--partial | Every operation of the checked files that can panic and that the compiler did not prove: a call of std/ that can panic (an index, a slice, expect, a key of a map), integer arithmetic that still carries its check, an index step that still compares. An audit for code that must not stop, one line per site; it changes no answer of check |
--deny deprecated | Every use of a deprecated declaration is a problem instead of a warning: an error block, counted in the last line, and the exit code 1 |
check is the gate: a false positive of it is a bug in the checker, not a reason to change the program.
build#
Compiles the typed IR ahead of time: every program below the path (default .) - each package's src/main.trb and
its program lines - or the one a name names, each into build/<profile>/<program> of its package; a library is
checked and builds nothing. The first back end prints C and hands it to a C compiler, so torb build needs one on the
machine; --emit-c writes the C and stops, which needs nothing. --output <file> says where the binary of one program
goes, and the C is written next to it. Nothing observable differs between a binary and the same program under
torb run (torb build).
parse, tokens, ast and ir#
The four windows into the front end. parse checks syntax only, recursively over directories. tokens and ast print the
lexer and parser output of one file in a deterministic format - the one compiler/src/syntax/dump.trb and the
generated Show of the syntax tree define. ir prints the typed intermediate representation the back end lowers,
with --statistics for the counts alone, and --bytecode prints what the VM runs instead: the chunks of the final IR,
disassembled.
highlight#
Prints the semantic tokens of one file as a single JSON document, which is what an editor colors a name with when the
TextMate grammar cannot tell a field from a local or a case from a type. torb highlight --stdin reads the source from
standard input instead of a file, for a buffer that was never saved.
{"tokens": [[line, startCharacter, length, "kind", ["modifier", ...]], ...]}line and startCharacter are 0-based, and startCharacter and length count UTF-16 code units, which is what VS
Code's semantic token protocol takes. The tokens are sorted, never overlap and never cross a line break.
It answers from the syntax tree alone and opens no other file, so it costs a parse and not a type check - tens of
milliseconds for a thousand lines, which is what lets an editor ask on every pause in typing. That is also the list of
what it cannot know: the type of an arbitrary receiver, and what a single-segment use names in the module it comes
from. A name it cannot place gets no token, never a guess, so the grammar's own color stands.
The command never fails: a file with syntax errors is colored as far as it parsed, a file that cannot be read prints an
empty list, and the only exit code is 0. The language server's semantic tokens are these, sharpened by the checker;
the VS Code extension asks highlight where the language server does not run.
lsp#
The language server: the Language Server Protocol over standard input and output, which is what an editor starts. It
holds the workspace between two requests and answers from the checker's tables - the diagnostics of check with their
ranges, for the open files and in the background for the whole workspace, hover from the model of doc, go to
definition, the references, a checked rename, completion, signature help, symbols, the formatting of format, semantic
tokens, and the fixes of lint as quick fixes - and a keystroke inside of a function checks that function, not the
file (torb lsp, Set up your editor).
debug#
The debugger: the Debug Adapter Protocol over standard input and output, which is what an editor starts for its Debug
buttons. A launch names a program or tests, which run in the VM in a process of their own, and the program stops at
breakpoints, after a step, on a pause and where a panic begins; the editor reads the call stack, the locals of every
frame and the answers of evaluate (torb debug, the design record).
format#
Writes the one layout of the language over the syntax tree, never with a regular expression: first every rule of the
formatter canon - a call becomes a command wherever the grammar allows it and gets parentheses everywhere else, a
multi-line """ string is indented, and the three rules that rewrite a small form of the tree - then indentation, the
spaces between tokens and blank lines. Line breaks between tokens, comments and each file's line endings are kept. Every
edit and every layout is parsed again and only stays if the tree is the one from before with every span and call style
erased, so a run cannot change what a program means (torb format). A file that changes is type
checked before and after: a call of a field that holds a function keeps its parentheses, and a file that type checked
still does.
| Flag | What it does |
|---|---|
--check | Report the files that are not in the layout, write nothing, and leave with 1 if there is one |
lint#
Reports the rules of style the type checker leaves alone, each finding in the format of check with warning in
front and the id of its rule under it: the own name of a type where Self means the same (self-name), a Bool field
named as a question (question-field, which runs the checker for the rename it fixes with), a binding of an
irrefutable pattern that nothing reads (unread-binding),
true, false or None for a Bool or an optional without its label (labeled-literal, which runs the checker),
a written Some or Ok the value would become on its own (redundant-wrap, which runs the checker), a closure that
only passes its parameters on to a declared function (redundant-closure, which runs the checker), and a
private(var) field, which is spelled protected var (protected-field) (torb lint). Every rule
runs on every program: no setting, comment or flag switches one off.
| Flag | What it does |
|---|---|
--fix | Write every fix a rule is certain of, then lint again and report what is left |
--rule <id> | Report or fix only this rule in this run; it narrows the output and does not change what is valid |
rename#
Renames fields at their declaration and at every use the checker resolves to them - a member, a bare name inside the
type, the label of a constructor, a copy, a case or a pattern, a with ... by delegate, a doc link - and nothing
else: a parameter or a local of the same name keeps it. A rename that cannot be made is printed and nothing is written
(torb rename).
canon#
Deprecated: torb canon prints a warning and runs torb format with the same paths and --check; --rule is accepted
and ignored, because format runs every rule of the canon (torb canon).
test#
Runs every *.test.trb file below the paths it is given - any test package, and several of them at once, which is
still one binary and one report - and without a path every one of the package the working directory is in. The output
is the name of a file, then one line per test of it, then the next file - in the order of the files, which is the
order their paths sort in - and at the end a blank line and N passed, M failed (K files). The command leaves with 0
where nothing failed and 1 otherwise.
One binary for all of them, and not one per file, because every test file imports its harness and through it
whatever it tests: one binary per file would be one C compile of a translation unit that size per file, and the C
compiler is where the time of a build goes. The whole suite together is about the size of one such translation unit.
That is also why --jobs means nothing here - one binary is one process - and it is accepted and ignored rather than
rejected. What runs a suite on several cores is --shard k/n: the k-th of every n files, so n processes of the one
binary run it between them, and --shards n builds the binary once and runs all n of them at the same time.
--filter <name> runs the test of one full name, or every test of a group, and --report json writes JSON Lines in
place of the lines a person reads, an event per file and per test, which is what an editor's list of tests reads
(torb test).
test and group themselves are not a command's: they are ordinary functions of std/test, and the report comes
from one place - one line per test, ok or FAILED with the group names in front of it, and the counts
of the summary. So a test file that is built on its own (torb build one.test.trb) runs its tests the same way,
which is what the language's own conformance suite compares.
manifest#
Evaluates every project.trb the paths reach - a manifest named directly, or every package of a workspace below a
directory - as the receiver script it is: in the VM, inside the sandbox a project file gets, against the Project of
std/project (see project.trb). It prints the settings each one configured, as literal command
calls. With --check it prints nothing but a summary, and fails where the static reader the other commands use reads
anything else out of those settings than out of the file itself.
add, remove, update, install, publish, lock and audit#
The package manager. add, remove and update edit project.trb where they must, resolve every member of the
workspace together by PubGrub - the highest version every requirement allows, and an explanation where there is none -
and write project.lock.trb; install fetches what the lock pins into the cache and checks every
tree hash; publish builds the archive of a package exactly as a registry receives it and writes it into a file:
registry; lock writes the settings block of every member from its evaluated manifest and leaves the graph alone,
and lock --check writes nothing and fails where the file is not what lock would write; vendor copies what the lock
pins into vendor/ after checking every tree hash, names each copy with a source ... path: line and locks it as a
path: package, so the project builds without the cache or a network; audit reads the lock, asks the registry of each
package it pins whether the version is yanked or the package deprecated, with --advisories <directory> also checks
the pins against a directory of advisory files, and exits 1 where it found something and 2 where it could not
look. Every one of them takes --project <directory> and --offline. See torb add,
torb remove, torb update, torb install,
torb publish, torb lock, torb vendor and
torb audit.
login, logout, owner, yank and deprecate#
The user's side of a registry on the network. login reads a token from standard input, never from an argument,
asks the registry whether it holds and stores it in the user's credentials.trb, where publish, owner, yank and
deprecate find it when TORB_TOKEN is not set; logout removes it again. owner lists the members of an owner and
adds, changes or removes one; yank withdraws a version from new resolutions with a reason, and yank --undo takes it
back; deprecate flags a whole package, with the package to use instead and a reason that torb add prints, and
deprecate --undo takes the flag back - both on a file: registry by appending the line to the index itself. All of
them take --registry <url>; owner, yank and deprecate otherwise speak to the registry the owner is bound to,
and login and logout to the public registry. See torb login, torb logout,
torb owner, torb yank and torb deprecate.
docs#
Five commands over the documentation: check is the gate, index writes the generated part of every index.md, skill
derives the Agent Skill, bundle writes llms.txt and llms-full.txt, and site writes the website. See
the docs commands and torb docs site.
doc#
Turns the public API of a package and its doc comments into a reference: a static site with an index per package and
a page per module (--output, default build/doc), or the same model as one JSON document for an editor or a registry
(--json). --check writes nothing and fails on a link of a doc comment that resolves to nothing and on an example
under # Examples that does not compile or run (torb doc).
What is still planned#
These are in the design and not in the binary. A page about one of them carries status: planned.
| Command | What it will do |
|---|---|
torb audit | The public advisory database: torb audit fetching it by default, instead of the --advisories directory it reads now |
Examples#
Check the whole repository and see that every expression has a type:
$ torb check .
255 files, no problems
$ torb check --statistics .
149982 of 149982 expressions typed (100%), 0 deferredBuild a native binary, and look at the C first - see torb build for what the back end does not lower yet:
$ torb build examples/tour/src/scratch.trb --emit-c --output build/dev/scratch
wrote ../build/dev/program.c
$ torb build examples/tour/src/scratch.trb --output build/dev/scratch
wrote ../build/dev/scratch.exeRelated#
- Verify your work - the commands to run before you are done.
- Install and run - the first use of
runandcheck. - torb check, torb run, torb repl, torb build, torb test, torb format, torb lint, torb lsp, torb debug, torb doc, torb new, torb init - one page per command, in depth.
- Command calls - the rule of the canon that
formatwrites first. - The docs commands - the
docssubcommands.