Compiler internals

A brief overview for contributors. Not exhaustive — enough to orient someone reading the source.

Pipeline

text
.cleat source → Lexer → Parser → AST → Checker → Codegen → go build → native binary

Each stage is a Go package under internal/.

Key directories

DirectoryPurpose
cmd/cleat/CLI entry point — dispatches to build, test, fmt, lsp, new
internal/lexer/Tokenizer — produces token stream from source bytes
internal/parser/Recursive-descent parser — produces AST from tokens
internal/ast/AST node definitions (expressions, statements, declarations, types)
internal/checker/Type checker, effect validation, symbol resolution, struct analysis
internal/codegen/Go code generator — emits .go files from checked AST
internal/loader/Multi-package import resolver — builds the package DAG
internal/diag/Rustc-style diagnostic renderer with ANSI colors
internal/format/Canonical source formatter (printer + comment preservation)
internal/lsp/Language server — diagnostics, hover, go-to-definition, autocomplete
internal/config/Minimal TOML parser for cleat.toml
internal/stdlib/Embedded stdlib .cleat sources (via //go:embed std)
runtime/cleat/Go runtime library (Result, Effects, Stream, Tool, Agent, Server, ...)
editors/vscode/VS Code extension + TextMate grammar

Codegen specializations

The codegen has special handling for several call patterns:

PatternWhat the codegen does
json.decode with type annotationEmits cleat.JsonDecode[T] instead of the generic wrapper
llm.generate with type annotationEmits cleat.LlmGenerate[T] with JSON Schema as string constant
Agent.run with type annotationTwo-step: text call + cleat.ExtractStructured[T]
HTTP/FS/Env native functionsConversion wrappers between flat runtime types and package-local structs
Server handler wrappersConvert ServerRawRequest ↔ package-local Request/Response
Guard interceptionIIFE wrapping at guarded call sites

Flat type boundary

The runtime uses flat types (e.g., HttpRawResponse, ServerRawRequest) at the runtime ↔ codegen boundary to avoid import cycles between the runtime package and generated code. Codegen wrappers convert between flat types and package-local struct types.

Symbol table

The checker uses scope chains for symbol resolution:

Symbols carry kind (SymFunc, SymTool, SymAgent, SymGuard, SymServer, ...), type, position, and effects.

Effect enforcement

  1. Each function's needs clause populates a set of allowed effects
  2. At every call site, the checker compares the callee's required effects against the caller's allowed set
  3. Missing effects produce a diagnostic with the exact effect name and call site
  4. Effects are validated transitively — if A calls B which needs net, A must also declare net

Testing

826+ tests across 11 packages. Run with go test ./.... Tests include:

Edit this page on GitHub