Skip to main content

module_lang/
lib.rs

1//! # module_lang
2//!
3//! Module and import resolution across multiple source files. Given the modules a
4//! front-end has already parsed — each exporting a set of named items — this crate
5//! answers the question every `use`/`import` asks: *which item does this name refer
6//! to?*
7//!
8//! A [`ModuleGraph`] holds one module per source file, each carrying the
9//! [`SourceId`] it was read from and a flat namespace of names. A name is either
10//! [`define`](ModuleGraph::define)d in the module, with a [`Visibility`] and a
11//! language-supplied payload, or [`import`](ModuleGraph::import)ed into it from
12//! another module. [`resolve`](ModuleGraph::resolve) walks definitions and import
13//! edges to the payload a name refers to, or returns a defined [`ResolveError`] for
14//! a name that is missing, private, or part of an import cycle.
15//!
16//! It sits in the SEMA tier of the `-lang` family, above
17//! [`symbol_lang`](https://docs.rs/symbol-lang) — which interns the names this
18//! crate keys on — and [`source_lang`](https://docs.rs/source-lang) — which owns
19//! the files the modules came from. It owns module and import resolution only: no
20//! parsing, no type checking, no code generation.
21//!
22//! ## Model
23//!
24//! Modules are added in order and identified by a stable [`ModuleId`]. Each
25//! module's names live in a [`BTreeMap`](alloc::collections::BTreeMap) keyed on the
26//! interned [`Symbol`], so a lookup compares integers in `O(log items)` and
27//! iteration is deterministic. A name is declared at most once per module — a
28//! second definition or a colliding import is a [`ResolveError::DuplicateName`].
29//!
30//! Resolution treats a module's own names as fully visible: a name defined in a
31//! module resolves from within it whether it is public or private. Crossing a
32//! module boundary through an import is where [`Visibility`] applies — an import
33//! reaches a name in another module only if it is [`Visibility::Public`]. Imports
34//! chain (a module may re-export what it imported), and the walk tracks the
35//! `(module, name)` pairs it has seen, so a chain that loops back is reported as a
36//! [`ResolveError::ImportCycle`] in bounded time rather than recursing without end.
37//!
38//! ## Quickstart
39//!
40//! ```
41//! use intern_lang::Interner;
42//! use module_lang::{ModuleGraph, ResolveError, Visibility};
43//! use source_lang::SourceMap;
44//!
45//! // Two files, each a module: `app` uses an item exported by `util`.
46//! let mut sources = SourceMap::new();
47//! let app_src = sources.add("app.lang", "use util.helper;")?;
48//! let util_src = sources.add("util.lang", "pub fn helper() {}")?;
49//!
50//! let mut names = Interner::new();
51//! let helper = names.intern("helper");
52//!
53//! let mut graph: ModuleGraph<&str> = ModuleGraph::new();
54//! let app = graph.add_module(names.intern("app"), app_src);
55//! let util = graph.add_module(names.intern("util"), util_src);
56//!
57//! graph.define(util, helper, Visibility::Public, "fn helper")?;
58//! graph.import(app, util, helper)?;
59//!
60//! // The import resolves through to the definition in `util`.
61//! assert_eq!(graph.resolve(app, helper), Ok(&"fn helper"));
62//!
63//! // A name nobody declared is a defined error, never a panic.
64//! let missing = names.intern("nope");
65//! assert!(matches!(graph.resolve(app, missing), Err(ResolveError::Unresolved { .. })));
66//! # Ok::<(), Box<dyn std::error::Error>>(())
67//! ```
68//!
69//! ## Stability
70//!
71//! The public API is being designed across the 0.x series and freezes at `1.0`.
72//! Until then a minor release may make a breaking change, each documented in the
73//! [`CHANGELOG`](https://github.com/jamesgober/module-lang/blob/main/CHANGELOG.md).
74//! See [`docs/API.md`](https://github.com/jamesgober/module-lang/blob/main/docs/API.md).
75
76#![cfg_attr(not(feature = "std"), no_std)]
77#![cfg_attr(docsrs, feature(doc_cfg))]
78#![deny(missing_docs)]
79#![forbid(unsafe_code)]
80
81extern crate alloc;
82
83mod error;
84mod graph;
85mod id;
86
87pub use error::ResolveError;
88pub use graph::{ModuleGraph, Visibility};
89pub use id::ModuleId;
90
91// The foreign handle types this crate's API speaks in, re-exported so a consumer
92// need not also name symbol-lang / source-lang just to call it.
93pub use source_lang::SourceId;
94pub use symbol_lang::Symbol;