Skip to content

Latest commit

 

History

73 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Basalt

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/.

Highlights

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 the 0 - 1 form 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. The option::Option<T> module (tag + payload) makes absence explicit and total — the only way to read the payload is unwrap_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.

Repository layout

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

Package manager

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 . verify

The 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.

Building the Host compiler

From the repository root, run:

./scripts/build.sh

The 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.

Building and using the Bootstrap compiler

The canonical Bootstrap source is src/bootstrap/basaltc.basalt. The standard sequence is:

./scripts/fixed_point.sh

The 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 -Werror

Testing

The master verification command is:

./scripts/run_ownership_stress.sh

It 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.sh

The 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.

Guides

  • docs/USER_GUIDE.md — how to build Basalt, write programs, and use the standard library, with verified examples
  • docs/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 fixtures
  • docs/PACKAGE_MANAGER.md — manifest, SemVer, registry, lockfile, cache, vendor, security, and build-boundary contract

Language documentation

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.

Portability and safety

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.

License

Basalt is distributed under the MIT License. See LICENSE.

About

A small, self-hosting programming language

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages