Expand description
The Zelkova compiler
§How to compile a package ?
Note: an interface is built for every module that canonicalizes and kept for whatever imports it within its package; a package that checks also offers its modules’ interfaces to the packages that depend on it. What is still missing is emitting one, so a dependency is compiled from source on every build.
- Read the package’s
zelkova.tomlmanifest, and resolve the build: every package reachable throughdependencies, plus the root package’stest-dependencies, each ordered after the packages it depends on. Steps 2 to 6 then run once per package — bar the ones onlytest-dependenciesreaches, which a build that did not ask for the tests resolves and leaves uncompiled — over the two source roots:src/and, for the root package when its tests were asked for,tests/, beside its own manifest. The root’stests/is compiled last, after the packages onlytest-dependenciesreach, since one of those may depend on the root’ssrc/. Before a package’s modules are checked, the whole map of module names it can import is built — its own, plus each direct dependency’s public modules under that package’s namespace or, unwrapped, under their own names — and a name two modules both answer to stops that package there. - Collect all
*.zelkovafiles with their path name relatives to the root. - Create a
SourceFilesmapping fromModuleNametoparser::Module.- module names are deduced from file name
- parsing is done through
parser::parse_recovering, which reports every declaration that fails rather than only the first. A file whose header parsed keeps its module, with a record of each declaration that failed; one whose header did not is still a module its importers find, holding nothing - Verify that
parser::Module.namematch the one from the file system
- Build a dependency graphs from the modules import
- build it
- Verify there is no cyclic relation between modules
- Following the deps graph,
- canonicalize each modules. A declaration that does not canonicalize costs the
module that declaration and nothing else, an import that does not resolve costs
it that import and nothing else, and the module publishes its
Interfaceto the modules checked after it, whatever the checks find in it. A not-found error that only restates one of those failures is not reported again (canonical::canonicalize_recovering) - check each module (type check, exhaustiveness, etc…), and build the
ir::Modulea backend reads out of the canonical module and what the typer solved. The IR is built whether or not a declaration was broken or rejected, and lists each such one as unchecked - Bonus point to parallelize the tree branches which are not dependent on each others
- canonicalize each modules. A declaration that does not canonicalize costs the
module that declaration and nothing else, an import that does not resolve costs
it that import and nothing else, and the module publishes its
- Report. Each phase returns every error it found,
check_module_recoveringtags those with the module they came from, andcheck_packageaccumulates them across modules and hands them back with the file database and every status line, printing nothing. See thePhaseErrortrait for what a phase error owes theDiagnosticit renders as.
Every step is check_package’s, which writes nothing anywhere. Emitting a build that
checked, writing it and printing what the check found are the driver module’s.
Modules§
- canonical
- The canonical representation of a zelkova programs is a translation of a local source into the broader world.
- default_
imports - The imports every module gets without writing them.
- dependencies
- Walk package’s module dependencies
- exhaustiveness
- This module contains the exhaustiveness checker for pattern matching.
- ir
- The intermediate representation code generation reads.
- manifest
zelkova.toml, the package manifest.- name
- Home to the
NameandQualNameprimitives - program
- A program’s entry point: the module a manifest’s
mainnames, held to Programs. - resolve
- The packages a build is made from, and the module names each of them sees.
- scalars
- The five type names the compiler knows as scalars, and the one it knows beside them.
- source
- typer
- This module contains the type checker pass of the language
Structs§
- Checked
Module - One module the checks built a tree for, in both the forms the compiler still needs
it. Whether it passed them is said beside it: by
check_module’sOk, or by the empty error list ofcheck_module_recovering’sOutcome::Module. - Checked
Source - One module of a build the check built a tree for, with the file it was read from and
the source root it was read under. Which list of
PackageCheckholds it says whether it checked. - Interface
- An interface is trim down version of a module.
- Invalid
Package Name PackageName::newrefused this string; it names the input so a caller can report it.- Module
Name - A module name represent
- Package
Check - What
check_packagefound: everything a build needs to emit, and everything that went wrong, with nothing printed and nothing written. - Package
Name - A package name: one flat identifier, ASCII lowercase letters, digits and hyphens,
starting with a letter, with every hyphen followed by a letter —
docs/spec/packages.md’s whole rule forname. That shape is what keeps a package name and a module name from ever being confused (one is lowercase-with-hyphens, the other uppercase-with-dots), and what makes the namespace a package’s name derives unambiguous. - Source
Span - A byte span paired with the file it was written in.
- Span
Label - One underlined region of the user’s source, and what to say about it.
- Status
- One status line of a check, in the order the check reached it:
parsed 8 modules,checked modules: [..], or what failed in either.
Enums§
- Compilation
Error - Every way checking a package can fail, tagged with the phase that failed.
Traits§
- Checked
- What
dependencies::ModuleWalker::check_in_orderneeds of whatever its checker hands back: a name to key the module by, and the interface the modules after it are checked against. - Phase
Error - What a compiler phase owes the diagnostic reporter.
Functions§
- check_
module - Take a parsed module file within the ecosystem and apply all checks to it
- check_
module_ recovering - Apply every check to one parsed module, and hand back the module the checks built beside everything they found wrong with it.
- check_
package - Check the package rooted at
package_dirand every package it depends on, aszelkova::compile_packagedoes, and hand back what was found without printing or writing anything. - check_
package_ with_ tests check_package, checking the root package’stests/as well as itssrc/, aszelkova::compile_package_with_testsdoes.- phase_
diagnostic - Render the errors one phase produced for one module.
- plain_
diagnostic - Render an error that is about no place in any source — a path on disk, a program that could not be started — as its message and its notes, with no label.