mc documentation

A compiler small enough to read, written in itself. mc compiles a schoolbook-C language — seven types, one opaque pointer, if and loop — into Mach-O, ELF and PE, for macOS, Linux and Windows on arm64 and x86-64, writes and signs (or directly writes) the executable itself, and reaches a fixed point where one generation is byte-identical to the next. Everything the core leaves out — while, for, classes, a second backend, a whole object format — you teach it from ordinary mc source.

New here? Start at guide/00-getting-started.md. It goes from nothing to a running signed binary in about two minutes.

Curious how it compares? comparison.md measures mc against C, Go, Zig, Rust and C# on the same workload: a 4.1 ms compile, a 33 KB executable and a 1.2 MB toolchain. The default road folds constants and nothing else, at clang -O0 level; -O (M49) closes most of the gap — 1.30x clang -O2 on the same workload, measured and committed in a reproducible bench cell (M50), not estimated. The sources are in ../bench/.


The guide — task-oriented, read in order #

pagewhat it covers
Getting startedinstall, first program, --exe, the five dumps
One file, one programthe working tour of the language: types, memory, control flow, extern, function pointers
-O: what it does and what it does notthe optimizer, off by default: what qualifies for a register, what disqualifies a function, what it costs and what it never changes
A projectmc build and every mc.toml section by example
Using a package[deps], mc.lock, #include <pack/file.mc>, vendoring, and publishing a package of your own
Publishing a packageregistering on minicompiler.dev, what a GitHub Release publishes, the validator's box, the CI action, yank
Teaching the compiler#token/#infix/#prefix, #rule and the prelude, then the syntax hooks with a worked toy language
Emitting bytes#section, #opcode, emit()/reloc(), pass(), backend(), and the arm64-surface proof
Cross-compilingLinux arm64 targets, sysroots, external linkers
Two worked examplesexamples/api and examples/lang, walked through
How mc compiles itselfthe bootstrap chain, the fixed point, and the determinism rules for contributors
Footprintwhat the smallest program costs per target, floor by floor, and the ceilings that guard it
mc on a Linux hostthe host layer, the Linux bootstrap chain, cross-building the compiler, what make check skips on Linux and why
A new primitiveteaching mc a value type it has never heard of: type_new, syntax_lit, a derived machine, intrinsic
mc on a Windows hostthe same for Windows: no C runtime at all, the kernel32 runtime object, the .exe suffix, CRLF, and the Windows make check subset
Recreating the compiler"I want a compiler for X and nothing else": the five parts of <mc/core>, what each omitted one costs in bytes and in capability, removing a type or an intrinsic, and declaring the width of uptr
Run something you do not trustmc sandbox run|exec|check: the box in one command, the four caps, the five refusals and their exit codes, a project inside it, and the two hosts that have none
A new architectureadding an instruction set and an output format from OUTSIDE the compiler: the three registrations, how to prove the encoder, which families it reaches (including 8-bit parts, since M40) and which one it excludes permanently

The reference — exhaustive, read by lookup #

pagewhat it lists
language.mdgrammar, types, precedence, semantics, limits
directives.mdall ten # directives, with errors and examples
cli.mdevery command, flag, dump and exit code
toml.mdevery mc.toml key: type, default, meaning
packages.md[deps] and mc.lock: the resolution order, the tree hash, the closure rule, the refusals
tools.mdmc tool: install a program package, run it boxed, the permission-to-sandbox mapping
hooks.mdevery public function of the parser and hook API
sysroot.mdwhere a cross link finds its files: the resolution chain, the cache, the messages
objects.mdthe object model (sec_*, sym_*, reloc_add) and the codegen accessors (gen_*)
machine.mdthe machine task contract: the 31 tasks, and the three instruction sets side by side
diagnostics.mdevery message the compiler emits, with cause and fix
bundle.mdevery #include <name> the binary carries
sandbox.mdmc sandbox: the box, the shim, check, and what is not isolated

The design documents #

Most of these predate this tree and record why things are the way they are — the guide and the reference describe the compiler as it is; these describe the decisions that produced it. comparison.md is the exception: it reports a measurement taken against the tree as it stands, not a decision behind it, and lives here because it is exhaustive rather than task-oriented.

documentsubject
plan.mdthe language, the teaching surface, the architecture, the budget, the milestones
core-language.mdthe core language as specified milestone by milestone
surface.mdthe teaching surface, tier by tier, with the acceptance criteria
build.mdmc build, the bundle, #embed, Linux and Windows targets, limits
bootstrap.mdcutting clang, then ld, then the checkout
determinism.mdthe rules that make the output reproducible
macho-notes.mdevery Mach-O field, with its verified value
comparison.mdmc measured against C, Go, Zig, Rust and C#: compile time, binary size, run time, and the feature matrix
ci.mdthe GitHub Actions workflows and the release process
specs/one spec per milestone, M1.md … M53.md

There is deliberate overlap between the design documents and this tree — surface.md and guide/30+guide/40, build.md and guide/20+guide/50, core-language.md and reference/language.md, bootstrap.md and guide/70. The guide and reference are written against the compiler as it stands today and are checked mechanically; the design documents keep the reasoning and the milestone history.


1.0.0 #

The road to 1.0.0 — plan.md § "What 1.0.0 promises, and what it does not" — is what turns the API's current stability into a written promise: a recorded public surface (421 names and formats across seven kinds, reference/hooks.md § 8) that does not lose an entry without a MAJOR, ten committed fixed points on five hosts, and a release canary — a real out-of-tree consumer's own build gating every publish before the registry hears about it (ci.md § The canary). It does not promise a src/ internal, a message's wording, or an emitted byte, which is determinism.md's promise and moves by design. The surface freeze itself landed in 0.17.0 (specs/M53.md); 1.0.0 is cut when the real-world consumer's own gap list reaches zero.

How this documentation is checked #

scripts/check-docs.sh, inside make check, does three things:

  1. Coverage. Every p_*, syntax*, type_alias, pass, backend*, machine*, sec_*, sym_*, reloc_add and gen_* definition in src/, every CLI flag, every TOML key the driver looks up, and every directive in the lexer's table must appear in reference/. The lists are extracted from the source, never written down in the script, so a new public function fails the check until it is documented.
  2. Samples. Every fenced ```mc block in this tree is compiled by the real compiler. A block that declares // expect-exit: or // expect-stdout: is built with --exe and run, and its output compared; one that declares // expect-error: must fail to compile with that text on stderr. A block may name the compiler that should build it —

  3. Links. Every relative markdown link in this tree resolves to a file that exists.

So every command, every message and every program you see here was actually run.

Edit this page