LEVIATHAN v962456e · 962456eee1

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-native and --native-obj need the output path as the next argument.
  • --run and --ir execute the program in the compiler's own process. The other modes produce an executable or text and do not run the program.
  • --runtime only affects linking. --target also changes what the target:: 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 --version prints 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 backend for --emit-llvm, --native-obj and --build-native, and the other modes still work.
  • --no-rules is a debugging aid. Rules do not fire under it, and a program that calls a macro does not compile.

See also