Skip to main content

driver_lang/
lib.rs

1//! # driver_lang
2//!
3//! The driver that ties a compiler's phases into one run.
4//!
5//! A compiler is a sequence of phases — lex the source, parse the tokens, resolve
6//! names, check types, lower to an intermediate representation, emit code. Each
7//! phase takes the previous phase's artifact and produces the next. The machinery
8//! that holds those phases together — carrying the shared configuration and
9//! diagnostics from the first phase to the last, threading each artifact into the
10//! next phase, and stopping cleanly when a phase fails — is the same regardless of
11//! *what* language is being compiled. driver-lang is that machinery, and nothing
12//! more.
13//!
14//! It contributes three pieces:
15//!
16//! - A [`Session`] — the ambient state of one run. It holds the compilation
17//!   configuration (`C`, whatever a language needs) and the [`Diagnostic`]s every
18//!   phase emits, and it tracks how many were errors so a driver can decide when to
19//!   stop with [`Session::abort_if_errors`].
20//! - A [`Stage`] — one phase, as a transform from an input artifact to an output
21//!   artifact run against the session. Distinct from a *pass* (which rewrites one
22//!   type in place), a stage *changes the type* as the program moves down the
23//!   pipeline.
24//! - A [`Pipeline`] — stages composed end to end, with each stage's output checked
25//!   at compile time against the next stage's input. Running it threads an input
26//!   through every phase and returns the final artifact, stopping at the first
27//!   failure.
28//!
29//! The crate owns no compiler phases and wires no first-party dependency. It is
30//! generic over the artifacts that flow through it and the configuration the
31//! session carries, so a language plugs its own lexer, parser, and backend in as
32//! stages. The design mirrors how a real compiler's driver is a thin orchestrator
33//! over phases it does not itself implement.
34//!
35//! ## Example
36//!
37//! A three-phase calculator driver: tokenize a string into integers, sum them,
38//! then negate — collecting a diagnostic along the way.
39//!
40//! ```
41//! use driver_lang::{DriverError, Pipeline, Session, Stage};
42//!
43//! // Phase 1: text -> integers. Warns on tokens it skips; fails on an empty result.
44//! struct Lex;
45//! impl Stage<()> for Lex {
46//!     type Input = &'static str;
47//!     type Output = Vec<i64>;
48//!     fn name(&self) -> &'static str { "lex" }
49//!     fn run(&mut self, input: &'static str, session: &mut Session<()>)
50//!         -> Result<Vec<i64>, DriverError>
51//!     {
52//!         let mut out = Vec::new();
53//!         for word in input.split_whitespace() {
54//!             match word.parse::<i64>() {
55//!                 Ok(n) => out.push(n),
56//!                 Err(_) => { session.warn("skipping non-integer token"); }
57//!             }
58//!         }
59//!         if out.is_empty() {
60//!             return Err(DriverError::new("nothing to compute"));
61//!         }
62//!         Ok(out)
63//!     }
64//! }
65//!
66//! // Phase 2: integers -> their sum.
67//! struct Sum;
68//! impl Stage<()> for Sum {
69//!     type Input = Vec<i64>;
70//!     type Output = i64;
71//!     fn name(&self) -> &'static str { "sum" }
72//!     fn run(&mut self, input: Vec<i64>, _session: &mut Session<()>)
73//!         -> Result<i64, DriverError>
74//!     { Ok(input.iter().sum()) }
75//! }
76//!
77//! // Phase 3: negate.
78//! struct Negate;
79//! impl Stage<()> for Negate {
80//!     type Input = i64;
81//!     type Output = i64;
82//!     fn name(&self) -> &'static str { "negate" }
83//!     fn run(&mut self, input: i64, _session: &mut Session<()>)
84//!         -> Result<i64, DriverError>
85//!     { Ok(-input) }
86//! }
87//!
88//! let mut driver = Pipeline::new(Lex).then(Sum).then(Negate);
89//! let mut session = Session::new(());
90//!
91//! let result = driver.run("1 two 3", &mut session).unwrap();
92//! assert_eq!(result, -4);                    // -(1 + 3)
93//! assert_eq!(session.diagnostics().len(), 1); // the "two" warning
94//! ```
95//!
96//! ## Features
97//!
98//! - `std` (default) — link the standard library. Without it the crate is
99//!   `#![no_std]` and needs only `alloc`; every type here works in both modes.
100//! - `serde` — derive `serde::Serialize` for [`Severity`] and [`Diagnostic`], so
101//!   a run's diagnostics can be serialized for tooling.
102
103#![cfg_attr(not(feature = "std"), no_std)]
104#![cfg_attr(docsrs, feature(doc_cfg))]
105#![deny(missing_docs)]
106#![forbid(unsafe_code)]
107
108extern crate alloc;
109
110mod diagnostic;
111mod error;
112mod pipeline;
113mod session;
114mod severity;
115mod stage;
116
117pub use diagnostic::Diagnostic;
118pub use error::DriverError;
119pub use pipeline::{Pipeline, Then};
120pub use session::Session;
121pub use severity::Severity;
122pub use stage::Stage;