driver_lang/stage.rs
1//! The compilation-phase trait — the seam a driver is built from.
2
3use crate::error::DriverError;
4use crate::session::Session;
5
6/// One phase of compilation: a transform from an input artifact to an output
7/// artifact, run against a [`Session`].
8///
9/// A stage is the unit a driver is assembled from. Each phase of a compiler — lex,
10/// parse, resolve names, type-check, lower, emit — is a `Stage` that takes the
11/// previous phase's artifact as [`Input`](Self::Input) and produces the next one
12/// as [`Output`](Self::Output): a lexer is `Stage<Input = Source, Output =
13/// Tokens>`, a parser is `Stage<Input = Tokens, Output = Ast>`, and so on. Unlike
14/// a `pass` (which rewrites one type in place), a stage *changes the type* as the
15/// program moves down the pipeline — that type-threading is exactly what a driver
16/// exists to manage.
17///
18/// A stage is generic over the session's configuration type `C`, so every stage in
19/// a pipeline reads and writes the same shared configuration and diagnostics. Wire
20/// stages together with [`Pipeline`](crate::Pipeline), which enforces at compile
21/// time that each stage's `Output` is the next stage's `Input`.
22///
23/// # Contract
24///
25/// - [`name`](Self::name) returns a stable, static identifier, used to attribute a
26/// [`DriverError`] to the stage that produced it. It must not change between
27/// runs.
28/// - [`run`](Self::run) consumes `input`, may read and write the [`Session`]
29/// (emitting diagnostics, reading configuration), and returns the output. A
30/// phase that cannot produce its output returns a [`DriverError`] — never a
31/// panic. Emitting an error *diagnostic* records a problem but does not by itself
32/// stop the pipeline; return `Err`, or call
33/// [`Session::abort_if_errors`](crate::Session::abort_if_errors), to stop.
34///
35/// # Examples
36///
37/// A stage that parses whitespace-separated integers, warning on each token it
38/// cannot read and failing only if nothing parsed:
39///
40/// ```
41/// use driver_lang::{DriverError, Session, Stage};
42///
43/// struct Lex;
44///
45/// impl Stage<()> for Lex {
46/// type Input = &'static str;
47/// type Output = Vec<i64>;
48///
49/// fn name(&self) -> &'static str {
50/// "lex"
51/// }
52///
53/// fn run(&mut self, input: &'static str, session: &mut Session<()>)
54/// -> Result<Vec<i64>, DriverError>
55/// {
56/// let mut out = Vec::new();
57/// for word in input.split_whitespace() {
58/// match word.parse::<i64>() {
59/// Ok(n) => out.push(n),
60/// Err(_) => { session.warn("skipping non-integer token"); }
61/// }
62/// }
63/// if out.is_empty() {
64/// return Err(DriverError::new("no integers in input"));
65/// }
66/// Ok(out)
67/// }
68/// }
69///
70/// let mut session = Session::new(());
71/// let tokens = Lex.run("1 two 3", &mut session).unwrap();
72/// assert_eq!(tokens, vec![1, 3]);
73/// assert_eq!(session.diagnostics().len(), 1); // one "skipping" warning
74/// ```
75pub trait Stage<C> {
76 /// The artifact this stage consumes.
77 type Input;
78
79 /// The artifact this stage produces.
80 type Output;
81
82 /// A stable, static name for this stage, used to attribute errors.
83 fn name(&self) -> &'static str;
84
85 /// Run the stage: consume `input`, use the [`Session`] as needed, and produce
86 /// the output.
87 ///
88 /// Return a [`DriverError`] — never a panic — if the phase cannot produce its
89 /// output; the [`Pipeline`](crate::Pipeline) stops there and stamps this
90 /// stage's name into the error.
91 ///
92 /// # Errors
93 ///
94 /// Returns whatever [`DriverError`] the phase produces when it cannot continue.
95 fn run(
96 &mut self,
97 input: Self::Input,
98 session: &mut Session<C>,
99 ) -> Result<Self::Output, DriverError>;
100}
101
102#[cfg(test)]
103#[allow(clippy::unwrap_used, clippy::expect_used)]
104mod tests {
105 use super::*;
106 use alloc::vec;
107 use alloc::vec::Vec;
108
109 /// Doubles every element; changes the length-independent shape but not the type.
110 struct Double;
111 impl Stage<()> for Double {
112 type Input = Vec<i64>;
113 type Output = Vec<i64>;
114 fn name(&self) -> &'static str {
115 "double"
116 }
117 fn run(&mut self, input: Vec<i64>, _s: &mut Session<()>) -> Result<Vec<i64>, DriverError> {
118 Ok(input.into_iter().map(|x| x * 2).collect())
119 }
120 }
121
122 /// A type-changing stage: sums a list into a scalar.
123 struct Sum;
124 impl Stage<()> for Sum {
125 type Input = Vec<i64>;
126 type Output = i64;
127 fn name(&self) -> &'static str {
128 "sum"
129 }
130 fn run(&mut self, input: Vec<i64>, _s: &mut Session<()>) -> Result<i64, DriverError> {
131 Ok(input.iter().sum())
132 }
133 }
134
135 #[test]
136 fn test_stage_transforms_input() {
137 let mut s = Session::new(());
138 let out = Double.run(vec![1, 2, 3], &mut s).unwrap();
139 assert_eq!(out, vec![2, 4, 6]);
140 }
141
142 #[test]
143 fn test_stage_can_change_output_type() {
144 let mut s = Session::new(());
145 let out = Sum.run(vec![1, 2, 3], &mut s).unwrap();
146 assert_eq!(out, 6);
147 }
148
149 #[test]
150 fn test_stage_name_is_stable() {
151 assert_eq!(Double.name(), "double");
152 assert_eq!(Sum.name(), "sum");
153 }
154}