Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Implementation architecture

This is the map for someone working on the Rust code. For why the pieces are shaped this way, follow the ADR links. For what the language does, see the spec.

The pipeline

source: &str
   │
   ▼
┌──────────┐   Vec<Token>        ┌──────────┐   ast::Module      ┌──────────┐
│  lexer   │ ──────────────────▶ │  parser  │ ────────────────▶ │ resolver │
└──────────┘   + INDENT/DEDENT   └──────────┘   (tree)           └──────────┘
                                                                      │
                                                          ResolvedModule (tree + scope info)
                                                                      │
                                                                      ▼
                                                                ┌─────────────┐
                                                                │ interpreter │ ──▶ Output
                                                                └─────────────┘

Every stage takes the previous stage's output and either produces the next artifact or a Vec<Diagnostic>. Nothing prints; the caller (korrin::run or the CLI) decides what to do with output and errors.

Modules in crates/korrin/src

ModuleResponsibilityKey ADRs
spanSpan (byte offsets) and Spanned<T>.0008
diagnosticsDiagnostic, ErrorCode, SourceMap, rendering.0008
lexerBytes → tokens; indentation state machine; bracket-depth tracking for newline suppression.0001, 0003, 0006, 0007
astNode types for expressions and statements. Spans on every node.
parserRecursive-descent statements + Pratt-parsed expressions; error recovery.0003, 0006
resolverStatic checks (return/break/self placement, duplicate params, self-inheritance) and name resolution to scope depth.0005, 0011
interpreterTree walk. Value, Environment, control-flow Signal enum, arithmetic.0004, 0009, 0010, 0011
builtinsThe built-in function and method table.

Cross-cutting conventions

  • Errors are values. A stage returns Result<T, Vec<Diagnostic>> (or accumulates into a sink). No panic! on user input, ever — panics are for internal invariant violations only.
  • Control flow is a value too. return / break / continue are an interpreter::Signal enum threaded through Result, not Rust panic/catch and not exceptions. This keeps the interpreter honest and maps to a future VM. (ADR 0004)
  • Spans everywhere. If you add an AST node, it carries a Span. If you raise a diagnostic, it has a primary label.
  • The library never touches the terminal. No println!, no colour detection, no std::process::exit. That is korrin-cli's job. (ADR 0012)

How to…