Compiler design / 03. Starting a build

A build chooses its command, builder and folded project values before any page source enters the compiler.

A build starts before source compilation

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.

Command and capability surface

Bootstrap starts with the command, not with config.moth.

The command selects:

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

Self-contained project config

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?

Build inputs and @project

Direct 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:

  1. explicit CLI or programmatic build input
  2. builder-provided primitive global
  3. the folded declaration default
  4. a structured missing-input diagnostic

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?

The ownership boundary

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.

What can fail here

When the walkthrough mentions config, label current versus accepted if the progress matrix still shows transitional forms.

Why Moth does it this way

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.

Roadmap and current status

Exit state

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.

Compiler design / Previous / Next