Compiler
The leviathan command line
Every option of the leviathan compiler, grouped by what it does, with the exit statuses and how arguments reach your program.
since 0.1.0-alpha.1linux
Description
leviathan [options] <source-file>
leviathan [options] --plan <build-plan>
leviathan --version
The compiler reads one source file, or the set of files named by a build plan (see
trident.overview), checks the whole program at once, and then does whatever the mode option
asks for. With no mode option it only checks the program: it prints a description of the
declared types and exits with status 0, or prints the diagnostics and exits with status 1.
Diagnostics carry source positions, and the compiler keeps going after the first error so that
one run reports as many problems as it can.
Put every option before the source file. The compiler treats any argument it does not recognise as the source file name and the last such argument wins, so a misspelled option written before the file is silently ignored and an option written after the file is read as a file name.
Running and building
| Option | Meaning |
|---|---|
--run |
Check, then execute with the tree-walking evaluator. |
--ir |
Check, lower to bytecode, and execute the bytecode. |
--build <out> |
Check, translate to C++, and build the executable <out> with the system C++ compiler (clang++, g++ or cc, whichever is found first). |
--build-native <out> |
Check, generate code with the LLVM backend, and link the executable <out>. |
--native-obj <out> |
Like --build-native, but stop after writing the object file <out> without linking. |
--emit-llvm |
Print the program as LLVM IR text on standard output. |
--emit-cpp |
Print the program as a self-contained C++ translation unit on standard output. |
--target <triple> |
The machine the program is built for. It selects the code --native-obj and --build-native generate and what the target:: constants report in every mode; see lang.cross-compilation. |
--opt-level <0|2> |
Optimisation level for object generation: 0 is a quick debug build, 2 (the default) optimises. |
--runtime <path> |
Link against this runtime library instead of the one installed beside the compiler. |
--no-columnar |
Store arrays of structs row by row (see lang.columnar-arrays). |
-- <args> |
Everything after a lone -- is handed to the program. |
The two build options print built <out> on standard error when they succeed. On a Windows
target --build-native appends .exe to the output name and on a WebAssembly target it
appends .wasm.
Looking at what the compiler sees
| Option | Prints |
|---|---|
--tokens |
The token stream, one token per line with its line and column. |
--ast |
The syntax tree as parsed, before rules and macros have run. |
--expand |
The program after rules, macros and comptime evaluation, as ordinary source you can compile again. |
--ast-after-rules |
The syntax tree of that expanded program. |
--rules |
Every rule that fired and where, including the ones the standard library defines. |
--resolve |
The declared types after name resolution, without type checking. |
--no-rules |
Turns the rule and macro stage off for the run. |
--comptime-budget <n> |
Raises or lowers the step limit for comptime evaluation (see lang.comptime). Exceeding it is a compile error. |
--reentrant-budget <n> |
Changes the round limit for rules that re-trigger each other (default 8). |
Projects and namespaces
These options answer questions about how a multi-file program fits together. They work on a single file too, which they treat as a project of one file.
| Option | Prints |
|---|---|
--plan <file> |
Compile the project a build plan describes instead of one source file. |
--imports |
For each file, the namespaces it declares, the ones it uses, and every namespace visible to it. |
--graph |
Which files depend on which through uses, any cycles, and the order the files build in. |
--namespaces |
Every namespace, the files that open it, and its members. |
--why <name> [in <file>] |
Where a bare name could come from and which candidate wins, for one file if you name it. |
--lint-namespaces |
An opt-in check that each file sits in a folder matching the namespace it opens. It exits with status 1 when a file does not. |
--assets |
The files that comptime import() calls read: path, size, hash when a plan supplies one, and owning module. |
Standard library and documentation
| Option | Meaning |
|---|---|
--prelude <dir> |
Read the standard library from this directory instead of the one that ships with the compiler. The directory must contain every standard library file. |
--check-prelude |
Type-check the standard library itself and print a summary of what the checker found. Exits with status 1 if it found errors. No source file is needed. |
--doc-json |
Print the documented symbols of the standard library as JSON. Given a source file or plan, it needs --package-name <name> and --package-root <dir> and dumps that package instead; --package-version <v> records its version. |
The standard library files are looked up in this order: --prelude, the LV_PRELUDE_DIR
environment variable, a prelude directory beside the compiler, and finally a copy built into the
compiler. A directory named by --prelude or LV_PRELUDE_DIR that is missing or incomplete is an
error, never silently skipped.
Checking memory behaviour
--ownership, --ir-verify and --mem-verify examine how a program allocates; they are
described in lang.ownership-analysis.
Program arguments, exit status and the program's own output
Arguments after -- reach the program through env::args(). The first element is the program
name, so the real arguments start at index 1. Natively built programs receive their arguments
from the operating system as usual, and the -- form is only needed with --run and --ir.
The compiler exits with 0 when the program ran to completion, with 1 when compilation failed or
an exception went uncaught (it prints Uncaught <Type>: <message> on standard output), and with
2 when it was invoked wrongly or could not read the file. A program can choose its own status
with env::exit(n), and --run, --ir and natively built programs all report it.
A program to inspect with the compiler
namespace Meta {
macro bump(e) => `(($e) + 1)`;
}
uses Meta;
int base = 41;
console.writeln(bump!(base));
42
Rules
- Options must come before the source file name.
- Exactly one mode option takes effect when several are given: the one written last.
--build,--build-nativeand--native-objneed the output path as the next argument.--runand--irexecute the program in the compiler's own process. The other modes produce an executable or text and do not run the program.--runtimeonly affects linking.--targetalso changes what thetarget::constants fold to, in every mode, including--run.- A source error stops the build and leaves no output file.
Examples
Saved as meta.lev, the program above can be looked at in several ways. --tokens shows what the
lexer made of the first lines:
$ leviathan --tokens meta.lev
1:1 KwNamespace namespace
1:11 Identifier Meta
1:16 LBrace {
2:5 Identifier macro
2:11 Identifier bump
2:15 LParen (
--ast shows the tree before the macro runs and --expand shows the source after it has:
$ leviathan --ast meta.lev
Program
Namespace Meta
Macro bump(e) => ($e + 1)
Uses Meta
Var base : int = 41
Expr console.writeln(bump!(base))
$ leviathan --expand meta.lev
namespace Meta {
}
uses Meta;
int base = 41;
console.writeln((base + 1));
Handing arguments to a program. This source reads its arguments:
A program that reads its arguments
Array<string> args = env::args();
console.writeln("program name and ${args.length() - 1} argument(s)");
for (string a in args.skip(1)) {
console.writeln("arg: ${a}");
}
Saved as args.lev, it is run with:
$ leviathan --run args.lev -- red "dark green"
program name and 2 argument(s)
arg: red
arg: dark green
Building and inspecting a project through its plan (see trident.overview for how the plan is
made):
leviathan --imports --plan build/plan.lvplan
leviathan --why Link in ./main.lev --plan build/plan.lvplan
leviathan --build-native app --plan build/plan.lvplan
Generating code for another machine:
leviathan --build-native app.exe --target x86_64-pc-windows-gnu --runtime runtime/x86_64-pc-windows-gnu/liblvrt.a main.lev
Notes
leviathan --versionprints the compiler's name and version. It must be the only argument.- The compiler is built with or without the LLVM backend. A compiler built without it reports
this build has no LLVM backendfor--emit-llvm,--native-objand--build-native, and the other modes still work. --no-rulesis a debugging aid. Rules do not fire under it, and a program that calls a macro does not compile.
See also
- Implementation status and execution modes — What the reference promises, what
unreleasedmeans on a page, and the four ways to run a Leviathan program. - The execution engines — The four ways the compiler runs a program, why they always agree, and the few places where one of them has to refuse a program.
- Native backends: LLVM and C++ — The two ways to turn a program into a native executable, what each one covers, and the runtime limits of natively built programs.
- Cross-compilation: --target and per-target runtimes — Building executables for another operating system or processor, what the build needs for each target, and what a target can reject.
- Ownership analysis and memory verification — How the compiler decides which allocations die with their function, and the options that report and verify that analysis.
- Two tools: trident and leviathan — How the package manager and the compiler divide the work, what the build plan between them is, and the commands that build and run a project.
- comptime — run the language at compile time —
comptimevariables, expressions andifstatements evaluate ordinary code during compilation and fold the result into the program. - import() — comptime file inclusion —
std::import(path)reads a project file at compile time and folds to its contents as acomptimestring. - Imports — uses and use — Bring a namespace's names into scope in bulk with
uses, or one name at a time withuse ... as .... - Namespaces — Group declarations under a name, reopen a namespace to add to it, nest namespaces, and reach members with
::. - Rules — match a shape, inject code — A rule matches declarations by shape and injects quasiquoted code at a named anchor, with
match,where, andinject … at. - Procedural macros — comptime code returns code —
macro name(string payload) comptime { … }runs at compile time, receives a string, and returns the code that replaces the call.