Skip to main content
v0.13.0

Interpreter Architecture

Rationale for the tree-walk approach: ADR-0004 (ADR-0051, amended again — this whole file and decisions/ are excluded from the metel-website build; still public and readable directly on GitHub in metel-docs, where relative links like the one above resolve fine on their own)

Pipeline​

01pipeline stageModule loading & parsing

root file → ModuleGraph (topological order); invokes parser per file

passes on module_loader::ModuleGraph
02pipeline stageName & reference resolution

per-module import scopes, pub_surface, re-exports; assigns SymbolIds; internally calls reference_resolver::collect_references to build a ReferenceTable, carried in ResolvedNames for later stages to consume

passes on name_resolver::ResolvedNames
03pipeline stagePath normalization

rewrites qualified Expr::Path nodes to Expr::ResolvedPath

passes on path_normalizer::NormalizedModuleGraph
04pipeline stageCoherence

aspect-impl orphan rule (T0014) and overlap detection (T0015); validation only — resolves type/aspect names to their declaring module, nothing more

passes on path_normalizer::NormalizedModuleGraph (unchanged; validation gate only)
05pipeline stageType inference & construction

per-module HM inference + construction (errors reported here) also populates TypedImplBlock::aspect_id via names.symbols

passes on typed_ast::TypedModuleGraph
06pipeline stageMove check

optional (--move-check flag): rejects use-after-move (RFC-0071, #579); validation only — off by default in v0.12.0, see the changelog

passes on typed_ast::TypedModuleGraph (unchanged; validation gate only)
07pipeline stageElaboration

resolves MethodDispatch per call site; wraps graph in ElaboratedModuleGraph

passes on elaborator::ElaboratedModuleGraph
08pipeline stageEvaluation

tree-walks ElaboratedModuleGraph → program output

Each stage is a separate Rust module. Module loading through elaboration are owned by metel-frontend; pipeline.rs in metel-interpreter orchestrates those stages and then invokes the evaluator. No stage is skipped, though Move Check only runs when --move-check is passed — see pipeline.rs::run_file.

Workspace Structure​

metel-core/ Cargo workspace
├── metel-frontend/ reusable language frontend
│ └── src/
│ ├── grammar.pest, parser/, ast/, types/, typed_ast/
│ ├── module_loader.rs, name_resolver.rs, reference_resolver.rs,
│ │ module_paths.rs, path_normalizer.rs, symbols.rs
│ ├── coherence.rs, move_check/, place.rs
│ ├── typeinference/, typechecker/, elaborator/
│ ├── stdlib.rs, native_keys.rs, error/
│ └── lib.rs
└── metel-interpreter/ tree-walking runtime and executable
└── src/
├── main.rs, pipeline.rs, lib.rs
├── evaluator/ runtime values, environments, dispatch, patterns,
│ builtins, calls, and lvalue evaluation
└── bin/ measurement and benchmark tools

metel-frontend owns every representation and validation step from parsing through ElaboratedModuleGraph: module loading, name/reference resolution, path normalization, coherence, type checking, move checking, and elaboration. Its public API is deliberately the language-facing boundary a future compiler and the LSP can consume without depending on evaluator runtime values or runtime generic reconstruction.

metel-interpreter owns pipeline orchestration, the CLI, test harnesses, and evaluator::evaluate_graph. It re-exports the frontend modules for compatibility with existing consumers, but new frontend-facing code should depend on metel-frontend directly. The boundary follows the data flow: the frontend produces an ElaboratedModuleGraph; evaluation begins only after that point. This preserves ADR-0004's compiler path and ADR-0010's requirement that a compiler pre-monomorphize rather than reuse the evaluator's runtime reconstruction.

Component Boundaries​

DataTypeOwning crateProduced byConsumed by
Module graphmodule_loader::ModuleGraphmetel-frontendmodule loadername resolver / path normalizer
Resolved namesname_resolver::ResolvedNamesmetel-frontendname resolverpath normalizer / typechecker / elaborator
Normalized graphpath_normalizer::NormalizedModuleGraphmetel-frontendpath normalizercoherence / typechecker
Reference tablereference_resolver::ReferenceTablemetel-frontendreference resolver (called from name resolver)typechecker
Typed module graphtyped_ast::TypedModuleGraphmetel-frontendtypechecker (check_graph)move check (optional) / elaborator (elaborate)
Elaborated module graphelaborator::ElaboratedModuleGraphmetel-frontendelaborator (elaborate)evaluator (evaluate_graph)
Untyped program (single-file)ast::Programmetel-frontendload_program (single-file shim)typechecker (check)
Typed program (single-file)typed_ast::TypedProgrammetel-frontendtypechecker (check)evaluator (evaluate)
ErrorsMetelErrormetel-frontendany stagecaller / CLI

Error Design​

All errors use a unified MetelError type:

enum MetelError {
ParseError { code: ErrorCode, message: String, start: usize, end: usize, filename: String },
TypeError { code: ErrorCode, message: String, start: usize, end: usize, filename: String },
RuntimePanic { message: String, start: usize, end: usize, filename: String },
Internal { message: String },
}

Type error codes: E0001–E0008. Runtime panics (.yolo() on nope, out-of-bounds, division by zero) terminate with a non-zero exit code.

Component Notes​

ComponentNotes
Module Loadersrc/module_loader.rs — load_root builds the topological ModuleGraph; load_program parses a single file (shim for single-file test harnesses)
Name Resolversrc/name_resolver.rs — resolve produces per-module ModuleScope, pub_surface, and re-exports; also assigns a SymbolId to every top-level declaration and stores the intern table in ResolvedNames::symbols
Path Normalizersrc/path_normalizer.rs — normalize rewrites qualified Expr::Path nodes to Expr::ResolvedPath; produces NormalizedModuleGraph
Symbolssrc/symbols.rs — SymbolId newtype; reserved ID constants for builtin types and aspects; SymbolTable intern helper
Reference Resolversrc/reference_resolver.rs — collect_references builds the ReferenceTable consumed later by the typechecker; invoked from within name_resolver::resolve, not a standalone pipeline call
Coherencesrc/coherence.rs — check(&NormalizedModuleGraph, &ResolvedNames); aspect-impl orphan rule (T0014) and overlap detection (T0015), RFC-0060/#542; validation only, runs after path normalization and before type-checking
Move Checksrc/move_check/ — check_graph(&TypedModuleGraph), opt-in via --move-check; rejects use-after-move (RFC-0071, #579); shares src/place.rs's addressable lvalue-path representation with the typechecker
Elaboratorsrc/elaborator/mod.rs — elaborate(TypedModuleGraph, &ResolvedNames) -> ElaboratedModuleGraph; resolves every MethodDispatch::Dynamic site to Inherent or Aspect { aspect_id }; see ADR-0037
Parsersrc/parser/, src/grammar.pest
Type Checkertypechecker.md (metel-core, not this repo)
Evaluatorevaluator.md (metel-core, not this repo)
Testingtesting.md (metel-core, not this repo)
Design decisionsdecisions/ (51 ADRs; start from ADR-0004 above if new here)