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 AArch64 Mach-O and ELF, writes and signs 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.


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
A projectmc build and every mc.toml section by example
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
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 #

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.

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 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
ci.mdthe GitHub Actions workflows and the release process
specs/one spec per milestone, M1.mdM30.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.


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