Skip to content

Module Contracts

This page summarizes the contracts module authors must preserve when adding or changing Wist/UniversalToolchain language features.

For the full source document, see docs/contracts/module-contracts.md. This reference page exists because these contracts are part of the public module-authoring route, not hidden project notes.

Why this page exists

UniversalToolchain is intentionally modular. A new feature can affect lexing, parsing, AST translation, bytecode, AIR, runtime manifests, optimizers and backends. Hidden assumptions in any of those layers can silently change language behavior.

A module is not complete when one example runs. It is complete when its syntax ownership, dialect selection, runtime behavior and backend support are explicit and tested.

Token-name contract

Token names are part of the internal module API.

Rules:

  • Do not invent a token name in one file and consume it by raw string in another unrelated file without a shared constant or clear documentation.
  • Do not reuse an existing token name for a different semantic meaning.
  • Do not rename token names without updating parser, visitor and dialect tests.
  • Prefer feature-scoped constants for shared token names.

Risk: a misspelled or reused token name can compile successfully while changing parser behavior.

Parser-priority contract

Parser creator priority is a grammar contract, not just an ordering hint.

Rules:

  • Priority must reflect intended precedence and syntax ownership.
  • Do not copy nearby priority values until a single example passes.
  • Priority changes require parser regression tests.
  • Overlapping syntax requires tests that show the wrong node creator does not take ownership.

Risk: a small priority change can silently change how existing programs parse.

Visitor-ownership contract

AST translation visitors must be self-filtering.

Rules:

  • A visitor must return without output when it does not own the node.
  • A visitor must not emit only because a token name or child count loosely matches.
  • A visitor must not assume it is the only visitor that can see a node.
  • Intentional cooperation between visitors must be documented and tested.

Risk: two visitors can emit for the same node, or no visitor can emit for a valid node.

Bytecode-tag contract

Bytecode tags are semantic metadata, not decoration.

Rules:

  • A tag must have a documented producer and consumer.
  • Backend-specific tags must not be used as generic semantic truth.
  • Optimizers must not depend on undocumented tag strings.
  • Tags affecting safety, purity, side effects, stack shape or backend lowering need documentation and tests.

Risk: tags can become a second untyped language hidden inside bytecode.

Shared-state contract

Mutable state shared between module components must be explicit, narrow and scoped.

Rules:

  • Avoid static mutable state.
  • Avoid state that survives across independent compilations unless it is immutable configuration.
  • Pass shared data explicitly.
  • Add tests that compile independent inputs and prove state does not leak.

Risk: a module can become order-dependent or compilation-history-dependent.

Dialect-selection contract

A feature is not modular unless a dialect can include or exclude it predictably.

Rules:

  • New features should be selectable through dialect/runtime composition when practical.
  • Restricted dialects must reject syntax or runtime behavior that is not selected.
  • Generic framework layers must not activate features through hardcoded module names.
  • Capability reports explain selected behavior; they must not activate behavior.

Risk: a feature can appear enabled even when the selected dialect did not request it.

Backend-capability contract

Optimized lowering must be backend-capability gated.

Rules:

  • Do not emit backend-specific intrinsics unless the selected backend supports them.
  • If an optimization changes AIR or bytecode shape, prove semantic parity with a reference backend.
  • Backend-specific fast paths need fallback behavior or clear diagnostics.
  • Unsupported backend operations must fail explicitly instead of silently falling back.

Risk: a program can work in CIL and fail in interpreter, or an optimizer can leak backend-specific intrinsics into unsupported backends.

Documentation contract

Every module with non-obvious behavior should document:

  • alias;
  • syntax ownership;
  • parser priority assumptions;
  • visitors;
  • bytecode/AIR responsibilities;
  • backend or intrinsic requirements;
  • dialect inclusion and exclusion behavior.

Built for developers who want to use, extend, or understand UniversalToolchain.