Compiler design / 03. Starting a build
A build chooses its command, builder and folded project values before any page source enters the compiler.
moth build does not open @page.moth first. It asks a blunter question: which command, builder, profile and inputs should interpret this project at all?
Until those answers settle, the compiler has no honest graph to walk. Choose climate and soil first. Then grow the plant.
Bootstrap starts with the command, not with config.moth.
The command selects:
checkThe selected builder exposes a capability surface before config compilation:
Frontend-owned directives and compiler-owned builtins join that surface. A builder cannot replace them.
The command chooses what kind of project this build is allowed to be. That choice decides which source kinds, packages and targets the project may use.
Diagram placeholder: Command selection feeds builder, profile, overlays and target intent
Caption: accepted bootstrap order. Question: what becomes known before config opens?
config.moth is build-system-owned compile-time Moth source. It is not a module. It produces no HIR, no start, no runtime artefact and no package interface.
Config operates on exactly one authored file identity. It rejects source imports before path resolution. It never builds a config package graph.
The ordinary compiler owners still touch that one file:
tokenization
-> declaration-shell parsing
-> local declaration ordering
-> AST semantic checking and folding
-> folded config values
Then config stops. No borrow checker. No backend.
Allowed shape includes one required open project const record, private helper constants, builder and tooling section records, scalars, optional values, anonymous const records, collections of foldable values and templates represented by their folded strings.
Rejected shape includes imports, runtime declarations, functions, traits, page fragments, export: and nested config files.
A short shape for the walkthrough project might look like:
project #= |
name = "compiler_walkthrough",
entry_root = "src",
|
html #= |
dev_output = "dev",
release_output = "release",
|
config.moth does not select the builder. The command already did.
Diagram placeholder: One config source stops at folded values and does not enter HIR
Caption: exact design boundary. Question: what does config produce that Stage 0 can consume?
@projectDirect primitive fields of project may declare a build-input contract with #Import. Accepted types stay narrow: String, Int, Float, Bool, Char and optional forms of those.
Resolution order:
The folded project record becomes a specialised immutable ProjectGlobalsInterface under the reserved @project import root.
That interface carries stable field identities, folded backend-neutral values, source locations, field-level fingerprints and project-context provenance. It carries no AST, no HIR and no runtime body.
Normal modules may import @project explicitly. Nothing injects it silently. Child modules, dependency aliases and packages cannot steal the @project root. Direct re-export of @project is forbidden.
For the greeting walkthrough, record the values Stage 0 needs for entry discovery: project name, entry root and builder sections. Those folded facts bridge files on disk to a project the graph can name.
Diagram placeholder: Resolved project fields enter ordinary module compilation through an immutable interface
Caption: simplified interface view. Question: how do build values reach source without becoming a second language?
Draw the line early and keep it sharp.
Owner | Responsibility |
|---|---|
Build system | discovers source, builds graphs, schedules modules, plans entries, writes outputs |
Compiler | prepares syntax, binds interfaces, checks semantics, lowers HIR, validates access and target contracts over explicit inputs |
Builder | assembles successful artefacts into project-shaped outputs after compilation succeeds |
The compiler does not wander the filesystem inventing a project. The builder does not re-parse @page.moth to rediscover greet. Each stage trusts the previous owner's facts.
When the walkthrough mentions config, label current versus accepted if the progress matrix still shows transitional forms.
Alternative: an unrestricted host-language build script that can do anything the host process can do.
Choice: command-selected capabilities and typed compile-time config.
Benefit: the project contract stays visible and bounded before discovery. Tools can validate schema without executing arbitrary host code.
Cost: users lose general host-code build hooks and must fit builder schemas.
config.moth and a specialised ProjectGlobalsInterface.import_values_anonymous_records_plan.md and entry-config-blocks-runtime-title-plan.md still move pieces of the surface.You leave with a validated command, a capability surface, folded project values and @project. Next: which files become semantic modules, and how a folder becomes a graph.