Skip to main content
mehen’s internals are organized around a clean separation between parsing, analyzing, and reporting.

Crate layout

Parser diversity, single contract

A core architectural decision: each language uses the parser best suited to it, but every analyzer returns the same LanguageAnalysis shape. mehen does not force a single AST model across languages. mehen-metrics owns the math (Halstead formulas, MI variants, cyclomatic accumulators); each per-language analyzer owns the interpretation — which syntax constructs count as decisions, which tokens classify as Halstead operators, which members count as public methods.

Pipeline

The key constraint: LanguageAnalysis is parser-neutral and Send + 'static. That lets the engine analyze files in an ignore-aware worker pool without arena-backed parsers (Oxc, Mago) leaking lifetimes across thread boundaries. Directory discovery uses the same .gitignore semantics as ripgrep, normalizes roots once, and excludes generated, vendored, and binary paths using each repository’s Git attributes. It processes each file directly in a traversal worker, so a producer cannot accumulate an unbounded queue while parsers are busy.

Markdown is special

The Markdown analyzer (mehen-markdown) parses the document into a block/inline AST and dispatches code fences back through LanguageDispatcher. That is how Markdown Halstead and MCC credit embedded code blocks without duplicating language logic.

ANTLR-backed analyzers

Kotlin, Java, and C# run through ANTLR v4 grammars generated to Rust ahead of time and checked in. A normal cargo build uses those modules without compiling codegen; cargo xtask antlr generate links the workspace-pinned antlr-rust-codegen crate directly. The shared mehen-antlr crate carries the concerns every ANTLR analyzer needs but tree-sitter and the language-specific parsers handle differently:
  • Span conversion. ANTLR reports positions in character offsets; mehen’s spans are byte offsets. mehen-antlr converts once, centrally.
  • Comments live on a hidden channel. They are absent from the parse tree, so CLOC is recovered from the token stream rather than by walking nodes.
  • No parent pointers. ANTLR rule contexts cannot look upward, so analyzers thread parent-dependent context (e.g. else-if detection) top-down as they descend the tree.
Despite those differences, the analyzers still return the same parser-neutral LanguageAnalysis as every other backend. See Add a new language → ANTLR for the full flow.

SQL is its own metric family

SQL does not fit the function/class-centric model that the source-code metrics assume — a declarative SELECT has no methods, classes, or imperative branches to count. mehen-sql therefore publishes a dedicated sql.* metric namespace (CTE graphs, join/subquery structure, object-touch risk, SQL Halstead, composite scores) instead of the shared code families. The sqruff CST is confined to the crate’s facts.rs behind a parser-neutral SqlFileFacts adapter, so the rest of the pipeline never sees a sqruff type. Dialect resolution (directive-or-inference, validated before sqruff ever runs) also lives inside the crate. See SQL metrics.

See also