Tooling
The turf CLI: flags, --explain, --smart, building from source, and the test suite.
Turf’s tooling today is centered on a single CLI compiler binary, turf, which also runs a fixed set of compile-time static analyses and prints human-friendly, color-coded diagnostics on every build.
Basic invocation
turf <input.tr> -o <output_name>
./<output_name>
The input file’s name must end in .tr — anything else is a hard error. If -o is omitted, the output name defaults to the input filename’s basename with .tr stripped.
Flags
| Flag | Implies | What it does |
|---|---|---|
--emit-llvm | — | Writes human-readable LLVM IR to <output>.ll instead of compiling and linking a native binary. |
--smart | — | Enables the SLM (AI-assisted) fix-suggestion pipeline. See SLM Integration. |
--dump-ast | — | Prints the parsed AST (functions/structs, then top-level statements). Runs before the Resolver, so nodes show no resolution annotations. |
--resolve | — | Prints a [Resolver] banner confirming the Resolver pass succeeded, plus the final scope depth. |
--dump-resolver-symbols | --resolve | Same banner, plus a full dump of the symbol table. |
--dump-resolved | --resolve | Same banner, plus an AST dump annotated with a resolved/not-resolved badge per node. |
--typecheck | --resolve | Prints a [TypeChecker] banner confirming pass/fail status. |
--dump-typechecked | --resolve + --typecheck | Same banner, plus an AST dump annotated with each node’s inferred type and module qualifiers. Only printed if type-checking succeeded. |
--dump-modules | --resolve | Dumps module-system introspection: imports, type ownership, flat function tables. |
-o <name> | — | Sets the output binary/IR base name. |
--link <lib> | — | Adds -l<lib> to the final linker invocation, on top of any libraries a program’s own extern from "libname" fn declarations already pull in. Repeatable. |
These flags don’t turn the passes on. The Resolver and TypeChecker run unconditionally on every compile, regardless of any flag.
--resolve,--typecheck, and the--dump-*family only control whether extra banners or AST dumps are printed. If either pass fails, compilation aborts before code generation ever sees the AST — flags or no flags.
--explain E0NN
turf --explain E001
A separate mode that takes no .tr input at all. Prints a header (E0NN ClassName (CATEGORY)) followed by the explanation text, then exits.
$ turf --explain E001
E001 ArrayBoundsError (ARRAY_ERROR)
You tried to reach past the end of an array
...
As of v1.0.0 there are 68 registered error codes, E001–E068, each assigned once and never reassigned (assignment order is alphabetical by source file and class name at the time each was added — it’s an opaque identifier, not a severity ranking).
A small curated subset is flagged Important — non-obvious concepts where the one-line message alone isn’t enough, and worth reading the full --explain entry before patching: E010 (RecursionWithoutBaseError), E017 (DoubleFreeError), E018 (UseAfterFreeError), E035 (InvalidTransitionError), E036 (InvalidTransitionTargetError).
Reading a diagnostic
── error at line 4, col 12 [E001], in file geometry.tr
You tried to reach past the end of an array
Note: declared here (line 2, col 5)
Hint: array size is fixed at declaration — did you mean a smaller index?
4 │ mut last: int = primes[3];
│ ^ Here!
Run `turf --explain E001` for more details.
Top to bottom: a header (bright red for errors, bright yellow for warnings), the message body, zero or more blue Note: lines for related source locations (e.g. a variable’s declaration site), an optional green Hint: line, a source snippet with the offending token underlined, and — for diagnostics with a resolved code — a footer pointing at --explain.
If a diagnostic has already fired on a given source line, every subsequent diagnostic on that same line is silently dropped, so one root-cause typo doesn’t produce a wall of downstream noise. Output is always sorted by (line, column), reflecting the source file rather than the order compiler passes happened to run in.
”Did you mean?”
Unknown variables, types, keywords, struct fields, struct methods, module symbols, and enum variants get typo-correction suggestions via restricted Damerau-Levenshtein distance (adjacent-character transpositions count as one edit, not two):
$ turf typo.tr
── error [E0NN]
Undeclared variable 'contuner'.
Hint: did you mean 'container'?
Compile-time static analysis
Turf builds a control-flow graph for every function body and runs a fixed sequence of analyses over it, unconditionally, on every compile — separate from ordinary type checking:
- Cyclomatic-complexity warning — fires above complexity 15, advisory only.
- Unreachable code / dead branches — printed as raw uncolored lines to stderr rather than the normal diagnostic format.
- Missing return (
E009, fatal) — a non-voidfunction with a path that reaches its exit without an explicitreturn. - Statement after terminator — two distinct diagnostics depending on what follows a
return/break/continue:E012for most statements,E013specifically for a variable declaration. - Use-before-initialization (
E008, fatal) — definite-assignment dataflow analysis; also checked inside loop/branch/comparecondition expressions. - Dead-store / unused-variable warnings — backward liveness analysis; a write whose value is never read afterward, or a variable never read at all.
- Infinite-loop detection —
while true { ... }/do { ... } while(true);with nobreakon every path, found via Tarjan’s SCC algorithm over the CFG. - Infinite-recursion detection (
E010,Important, fatal) — every path through the function passes through a self-call, with no base case. - Use-after-free / double-free (
E017/E018, bothImportant, fatal) — a forward dataflow pass trackingmalloc/realloc/freestate per variable. - Constructor field-assignment — a struct’s
.newmust assign every declared field on every path. - Compile-time array-bounds checking (
E001) — a constant-propagation pass catches an out-of-bounds constant index at compile time, before the usual runtime check would ever run.
Building from source
Requirements
- LLVM 22 (via
LLVMConfig.cmake) - CMake 3.20+
- C++20 compiler (Clang recommended)
llama.cpp(vendored as a submodule underexternal/llama.cpp; built automatically as a static library on first configure if not already built)
Build
git clone --recursive <this-repo>
cd turf
chmod +x scripts/cmake_build.sh
./scripts/cmake_build.sh --release
cmake_build.sh detects available CPU cores and builds in parallel; the resulting binary is placed at build/turf. On first run, if llama.cpp’s static libraries aren’t already built, CMake builds them automatically before building Turf itself — this can take a while the first time.
Compile and run a file directly
./scripts/compile_and_run.sh path/to/file.tr
Compiles, runs, and cleans up the produced binary.
Test suite
From the repo root, after building the turf binary:
./scripts/test_runner.sh
What’s not here yet
There is no LSP server, no turf fmt, no turf test, and no package manager as of v1.0.0 — see Roadmap for what’s planned.