torb run
torb run runs a file, the only program below a directory or the program named in the bytecode VM, or builds and runs it natively with --native, passing the rest of the command line, the three streams and the exit code through.
Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite
run runs a program at once, in the bytecode VM inside torb, which needs no C compiler. --native is
build plus starting what came out. The two are one language: the conformance suite holds every
program to the same output, exit code and panics in both, so a program cannot behave one way under run and another
way when it is shipped.
Synopsis#
torb run [--vm | --native] [--profile dev|release] [path | name] [arguments] Run a file or a program
--native In front of the path: build it natively and run the binary
--profile dev|release In front of the path: build it natively, this hard does the C compiler optimize;
with --vm, run it in the VM in that profile
--release The same as --profile release
--vm The default, accepted: run it in the bytecode VM, whatever profile is named
--timings In front of the path: what every step up to the first instruction took, on standard errorWhat it does#
What it runs#
An argument that contains / or \, ends in .trb, or is . or .. is a path; anything else is the name of a
program (project.trb). So:
A file is run as it is: a script, or the entry of a program. A file that nothing imports may hold top-level code and needs no
fn main, which is what makes a single script runnable at all.A directory, and the working directory where nothing is named, runs the only program below it: a package's
src/main.trb, or theentryof one of itsprogramlines.torb run ./my-projectandtorb run my-project/src/main.trbreach the same file. Where there are several,runnames them instead of guessing:error: acme/shop has 2 programs. Name one: `torb run shop` = shop, migrateA name runs the program of that name below the working directory -
torb run migrate. A name nothing declares answerserror: There is no program `migrate` herewith the names that exist and the reminder that a directory is written as a path,./migrate.
A package without a program has nothing to run: error: acme/lib is a library: there is nothing to run, or
error: acme/tour has no program: there is nothing to run for a package of scripts with neither src/main.trb nor
src/lib.trb - its files are run by naming them. All of this is the same in the VM and with --native.
The cache of --native#
The binary goes into build/run/<profile>/<key>/ under the workspace root, and <key> is a hash of every file the
front end read, with its path and its text, plus the path and the program name that were given and the toolchain
that builds it: the hash of the compiler's own C that torb build wrote beside the running torb (program.hash,
or program.c where only that is there) and the text of every C file and header of the runtime it links. The profile
is dev unless the command line says --profile release or --release in front of the path. An unchanged program is
therefore not rebuilt: the first run costs a build, every run after it costs a process start.
Keying on every file that was read rather than on the ones the program imports is the same trade
check makes when it reads the whole workspace: an edit to a file the program does not import costs
one rebuild, and in exchange "is this binary current" is a question about bytes that were read anyway instead of about
a dependency graph that is only known after the check. A torb that was built again, or a runtime that was edited,
is another key, so a binary an older compiler built is never the one that starts. The cache is a directory and nothing
else - rm -r build/run is how it is cleared. A torb copied somewhere without the program.c it was built from is
known by its path alone, which is the one case that asks for it.
Arguments#
Everything after the path or the name is what the program's own Process.arguments() sees - run itself reads none
of it - so torb run tool.trb --verbose input.txt hands ["--verbose", "input.txt"] to tool.trb, and a word that
looks like a flag of run is passed on rather than read.
What run puts on the output#
Nothing of its own. A build that succeeds says nothing, the program is started with the three streams torb itself
holds rather than with a pipe, and the exit code is the program's. So a program's output arrives while it is produced,
its standard output and standard error stay apart and in order, a program that reads standard input reads the one the
user is typing into, and a gate that compares what a program wrote compares the program and not the driver.
A build that fails says so, on standard error, in the form check uses, and run leaves with
1 without starting anything.
The VM, the default#
torb run <path> builds nothing: the program is checked and lowered to the same typed IR build compiles, encoded
as bytecode, and run by the VM inside torb itself, on the same runtime a native binary links. Its output, its exit
code and its panics are the native binary's, which the conformance suite of the toolchain checks for every program. A
program that runs long wants --native: the VM interprets, and a loop of arithmetic is
a few dozen times slower than the binary. Its tasks run on the workers of torb's own pool - as many as TORB_WORKERS says, the
core count by default - under the rules of a native binary's. A program that uses what the VM does not run yet is
refused before anything runs, with a message that names what is missing. Two things answer differently because the
program runs inside torb: CurrentProcess.executable() is the path of the entry file, which is what executes, and a
recursion reaches the stack overflow panic at a depth of its own.
It runs in the dev profile, as --native builds, and the profile decides one thing in the VM: the return trace
of ?, which the report of a top-level ? prints under the error, one at line per ? the failure went through
(Errors at the top level). torb run --vm --release runs it without the
trace, as a release binary runs - which is how the conformance suite compares a program with its release build.
It starts at once, because it does only what the program needs: the files of the workspace are listed and only the
ones the program reaches are read and parsed - the packages its entry imports, depends on and names as its prelude -
and the bodies of the entry are checked, while the bodies of a module of std are checked when the lowering first
reaches that module. A program that prints one line is running about a quarter of a second after the command was
typed; --timings prints where that time went, step by step, on standard error. A program the lowering refuses is
checked in full before the refusal is printed, so the message is the one torb build gives.
The memory limit#
A program run runs - in the VM, or built with the dev profile - stops at a memory limit: the smaller of 8 GiB
and half the physical memory. A native binary hands it to the operating system before the program starts (a job object
on Windows, RLIMIT_DATA on Linux, its own count of what it allocates where the system has nothing that fits); in the
VM the kernel counts what the program allocates. A program that
allocates without end ends with panic: out of memory: the limit of 8 GiB was reached (...) and exit code 102
instead of paging the machine to a standstill. A child process the program starts is not held to its limit.
TORB_MEMORY_LIMIT sets another one - a number of bytes, or one with K, M, G or T (512M, 16G) - and 0 or
none sets none; a value that is neither makes the program refuse to start, with exit code 2. With --release there
is no limit unless the variable asks for one. In the VM the program runs inside torb, and the limit - the same
default, or the variable - is the program's alone: the VM's kernel counts what the program allocates and ends it with
the same message and exit code, and torb checks and lowers it unlimited by the variable. TORB_MEMORY_LIMIT=64M torb run big.trb gives the program 64 MiB.
Exit codes#
The program's own, unchanged. A panic leaves with 101, the number CONCEPT.md specifies, and running out of memory
with 102; an uncaught Fail at the top level leaves with 1; a program that could not be built leaves with 1 and
one that found no C compiler with 3.
Examples#
A script, with the program's own arguments behind it:
$ torb run tools/report.trb --since 2026-09-01
14 commits, 3 authorsIn a package with a second program, run wants a name, and everything behind the name is the program's:
$ torb run
error: acme/shop has 2 programs. Name one: `torb run shop`
= shop, migrate
$ torb run migrate --dry-run
3 tables to migrateBuilt natively, the second run of an unchanged program starts immediately, because the key of its sources did not change:
$ torb run --native examples/tour/src/01-bindings-and-values.trb
1
Hello, World! 1 + 1 is 2
list: [1, 2, 3], first: 1, a: 1Related#
- torb build - the build
runis made of, and where the binary goes when you ask for one. - torb check - the same front end without the back end, for when only the diagnostics are wanted.
- project.trb - the
programlines a name is looked up in. - The torb command - every subcommand in one table.
- Top-level code - what makes a file runnable on its own.