Basalt is a small, self-hosting programming language and C compiler. The project contains two implementations of the same language pipeline: an OCaml host compiler used as the reference implementation and a Bootstrap compiler written in Basalt itself. Both implementations parse Basalt source, perform static type checking, and emit portable C11.
The repository is organized for reproducible compiler work rather than for generated build output. Source code lives under src/, tests under tests/, documentation under docs/, and repeatable development commands under scripts/.
Basalt supports integers, booleans, characters, strings, floating-point values, pointers, fixed and dynamic arrays, structs, enums, namespaces, generic types, function pointers, controlled C FFI through extern, include, and includec, ownership checks, scope-aware type checking, and structured standard-library components such as array, map, option, and result.
Safety is a first-class concern:
- Compile-time bounds checks for fixed arrays. Indexing
T[n]with a constant outside the array is rejected before any C is emitted — including the0 - 1form of-1. - Move/borrow checking for dynamic arrays. Values own their buffers; passing them moves them, releasing consumes them, and borrows (
&) block mutation, moves, and release while live. - Null safety through
option. Theoption::Option<T>module (tag + payload) makes absence explicit and total — the only way to read the payload isunwrap_or, so there is no panic path. - Runtime fail-closed policy. Tracked allocations are registry-checked; invalid bounds and double releases terminate deterministically with exit code
2.
The Bootstrap compiler's built-in function table is data-driven (bi_register(name, tag, flags)), so adding a built-in is a two-line change across the two compilers instead of edits to several hardcoded hash lists.
Arithmetic operators include +, -, *, /, and the modulo operator %. Modulo has multiplicative precedence and is accepted by both the Host and Bootstrap compilers. For example:
func main(): int {
let residue: int = (17 + 8) % 5;
return residue;
}
Implementation note: Basalt emits C11 and validates generated programs with strict GCC diagnostics. Floating-point arithmetic follows the current compiler rules;
%is intended for integer operands in portable generated C.
| Path | Contents |
|---|---|
src/compiler/ |
OCaml Host compiler, Dune metadata, lexer, parser, type checker, AST, and C emitter |
src/bootstrap/ |
Canonical self-hosting Basalt compiler source, generated C bootstrap artefact, and fixed-point checksum |
src/stdlib/ |
Generic containers and standard library modules |
tests/regression/ |
Focused language and compiler regression programs |
tests/stress/ |
The 164-case corpus plus the dedicated modulo stress and negative tests |
tests/conformance/ |
Host/Bootstrap conformance programs and runner material |
tests/adversarial/ |
Sanitizer-oriented and adversarial compiler tests |
tests/benchmark/ |
Cross-language benchmark source material |
docs/ |
Language specification, design notes, naming policy, and release notes |
scripts/ |
Build, test, and fixed-point commands |
The repository includes a deterministic, repository-side package manager at scripts/basalt_pkg.py. It reads Basalt.toml, resolves SemVer requirements against a read-only registry, writes Basalt.lock, verifies SHA-256 archives, and materializes verified source under .basalt/vendor/. The tool is independent of the frozen OCaml Host compiler and does not add package-import syntax to the Bootstrap compiler.
| Concern | Initial implementation |
|---|---|
| Manifest | Basalt.toml with package metadata and dependency requirements |
| Reproducibility | Basalt.lock with exact versions, sources, edges, and checksums |
| Artifact storage | $BASALT_HOME/cache, content-addressed by SHA-256 |
| Source materialization | .basalt/vendor/<name>/<version>/, promoted atomically after validation |
| Offline operation | fetch --offline and build --offline, using only lockfile and cache |
For a local registry or CI fixture, use the global options before the subcommand:
python3 scripts/basalt_pkg.py --root . --registry .tmp/registry fetch
python3 scripts/basalt_pkg.py --root . --registry .tmp/registry update
python3 scripts/basalt_pkg.py --root . fetch --offline
python3 scripts/basalt_pkg.py --root . verifyThe full contract, registry record format, lockfile invariants, archive safety policy, and current build boundary are documented in docs/PACKAGE_MANAGER.md. Package archives are source input only: the initial tool never executes package-provided scripts, and native compiler package imports remain a later compatibility milestone. For a real build, --compiler selects the Bootstrap compiler and --cc selects the C compiler, for example python3 scripts/basalt_pkg.py --root . build --compiler .tmp/bootstrap.bin --cc clang --compiler-arg=-std=c11 --compiler-arg=-Wall --compiler-arg=-Werror.
From the repository root, run:
./scripts/build.shThe script invokes Dune in src/compiler/ and produces the Host executable at src/compiler/_build/default/bin/basaltc.exe. The compiler accepts an Basalt source path. It writes the generated C file beside the source file, using the source filename with .c appended.
The canonical Bootstrap source is src/bootstrap/basaltc.basalt. The standard sequence is:
./scripts/fixed_point.shThe script first uses the Host compiler to generate C for the Bootstrap compiler, compiles that C with strict GCC flags, and then runs two Bootstrap generations. A successful run proves that n2.c and n3.c are byte-identical.
The strict compiler profile is:
gcc -std=c11 -Wall -Wextra -Wpedantic -Wconversion -Wshadow -WerrorThe master verification command is:
./scripts/run_ownership_stress.shIt builds both compiler paths, runs the move/borrow valid fixture under ASan/UBSan with leak detection, requires both compilers to reject every ownership-negative fixture, and then runs the regression, stress, adversarial, conformance, and fixed-point suites, plus a guard that no executable may be left under tests/.
The individual suites can be run directly:
./scripts/run_regression.sh
./scripts/run_conformance.sh
./scripts/run_adversarial.sh
./scripts/run_stress.sh
./scripts/fixed_point.shThe regression suite compiles and executes every registered fixture through both compilers: valid programs must compile with strict GCC and run; expect_reject fixtures must be rejected by both. The modulo-specific checks cover ordinary residues, zero and one, self-modulo, loop accumulation, precedence, nested expressions, a negative-value simulation, a generic Result context, and rejection of string operands. The complete stress corpus currently reports 164/164 passing cases before the dedicated modulo cases are added to the release repository.
docs/USER_GUIDE.md— how to build Basalt, write programs, and use the standard library, with verified examplesdocs/DEVELOPER_GUIDE.md— how the two compilers cooperate, the verification machinery, and worked examples of adding built-ins, stdlib modules, compiler features, and package-manager fixturesdocs/PACKAGE_MANAGER.md— manifest, SemVer, registry, lockfile, cache, vendor, security, and build-boundary contract
Start with docs/LANGUAGE_SPEC.md for syntax and semantics. docs/DESIGN.md describes the Host/Bootstrap architecture, docs/NAMING.md defines repository naming conventions, and docs/RELEASE_NOTES.md records the current release changes.
Generated code is checked with strict C11 warnings and is exercised with AddressSanitizer and UndefinedBehaviorSanitizer in the adversarial and stress workflows. Dynamic-array allocation, resizing, indexing, mutation, and release use checked runtime helpers with registry validation, overflow guards, lifetime checks, and deterministic failure on invalid bounds. The Host and Bootstrap compilers share this fail-closed policy; the OOB negative fixture must terminate with exit code 2 in both paths.
Basalt does not yet provide complete Rust-style static borrow checking. Raw pointer dereference, pointer arithmetic, extern, and includec remain explicitly low-level interoperability boundaries. Programs using those features should follow the ownership rules documented in docs/MEMORY_SAFETY_AUDIT.md and should be tested with sanitizers. The repository intentionally excludes compiler build directories, generated test outputs, caches, and local binaries from version control.
Basalt is distributed under the MIT License. See LICENSE.