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