driver-lang 1.0.0

Compiler driver and session orchestration.
Documentation
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
<h1 align="center">
    <img width="99" alt="Rust logo" src="https://raw.githubusercontent.com/jamesgober/rust-collection/72baabd71f00e14aa9184efcb16fa3deddda3a0a/assets/rust-logo.svg">
    <br><b>driver-lang</b><br>
    <sub><sup>API REFERENCE</sup></sub>
</h1>
<div align="center">
    <sup>
        <a href="../README.md" title="Project Home"><b>HOME</b></a>
        <span>&nbsp;&nbsp;</span>
        <span>API</span>
        <span>&nbsp;&nbsp;</span>
        <a href="../CHANGELOG.md" title="Changelog"><b>CHANGELOG</b></a>
    </sup>
</div>
<br>

The driver that ties a compiler's phases into one run. The public surface is five
types: a [`Session`](#session) carrying configuration and diagnostics, a
[`Stage`](#stage) for one phase, a [`Pipeline`](#pipeline) composing stages, and
the [`Diagnostic`](#diagnostic) / [`Severity`](#severity) / [`DriverError`](#drivererror)
values they produce.

- **Version:** 1.0.0
- **MSRV:** Rust 1.85 (2024 edition)
- **`no_std`:** yes (needs `alloc`; the default `std` feature only toggles the `no_std` attribute)
- **Unsafe:** none — `#![forbid(unsafe_code)]`
- **Stability:** stable — the surface below is frozen (see [Stability]#stability).

## Table of Contents

- **[Stability]#stability**
- **[Installation]#installation**
- **[Quick Start]#quick-start**
- **[Mental model]#mental-model**
- **[Public API]#public-api**
  - [`Session<C>`]#session
    - [`Session::new`]#session-new
    - [`Session::config`]#config · [`config_mut`]#config_mut · [`into_config`]#into_config
    - [`Session::emit`]#emit
    - [`Session::error`]#session-error · [`warn`]#warn · [`note`]#note
    - [`Session::diagnostics`]#diagnostics · [`take_diagnostics`]#take_diagnostics
    - [`Session::error_count`]#error_count · [`has_errors`]#has_errors
    - [`Session::abort_if_errors`]#abort_if_errors
  - [`Stage<C>`]#stage
    - [`Stage::name`]#stage-name
    - [`Stage::run`]#stage-run
  - [`Pipeline<C, S>`]#pipeline
    - [`Pipeline::new`]#pipeline-new
    - [`Pipeline::then`]#then
    - [`Pipeline::run`]#pipeline-run
    - [`Pipeline::name`]#pipeline-name
    - [`Then<A, B>`]#then-type
  - [`Diagnostic`]#diagnostic
  - [`Severity`]#severity
  - [`DriverError`]#drivererror
- **[Feature flags]#feature-flags**
- **[Design notes]#design-notes**

<br>

## Stability

As of **1.0.0** the public API documented here is **stable and frozen**. The crate
follows [Semantic Versioning](https://semver.org):

- Nothing in the frozen surface — [`Session`]#session, [`Stage`]#stage,
  [`Pipeline`]#pipeline and its [`Then`]#then-type node,
  [`Diagnostic`]#diagnostic, [`Severity`]#severity, [`DriverError`]#drivererror,
  every method listed above, and the `std` / `serde` feature flags — will be removed
  or changed in a breaking way within the `1.x` series. A breaking change means a new
  major version.
- `1.x` releases may **add** to the surface (new methods, new trait impls, new
  feature-gated helpers) without breaking existing code.
- Two behaviors are part of the contract, not just the current implementation: a
  pipeline runs its stages left to right and stops at the first
  [`DriverError`]#drivererror; and only [`Severity::Error`]#severity counts toward
  [`error_count`]#error_count and [`abort_if_errors`]#abort_if_errors. A `1.x`
  release will not change either except to fix a documented bug.
- The private fields behind `Session`, `Pipeline`, `Then`, `Diagnostic`, and
  `DriverError` are not part of the surface; they may change freely.
- MSRV (Rust 1.85) is a compatibility surface: a raise is a minor, documented change,
  never a patch.

<br>

## Installation

```toml
[dependencies]
driver-lang = "1"

# no_std:
driver-lang = { version = "1", default-features = false }

# with serde derives on Severity and Diagnostic:
driver-lang = { version = "1", features = ["serde"] }
```

<br>

## Quick Start

```rust
use driver_lang::{DriverError, Pipeline, Session, Stage};

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, _s: &mut Session<()>)
        -> Result<Vec<i64>, DriverError>
    {
        input.split_whitespace()
            .map(|w| w.parse().map_err(|_| DriverError::new("not an integer")))
            .collect()
    }
}

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>, _s: &mut Session<()>)
        -> Result<i64, DriverError>
    { Ok(input.iter().sum()) }
}

let mut driver = Pipeline::new(Lex).then(Sum);
let mut session = Session::new(());
assert_eq!(driver.run("1 2 3 4", &mut session).unwrap(), 10);
```

<br>

## Mental model

A compiler is a chain of phases. Each phase takes the previous one's artifact and
produces the next: source → tokens → AST → typed AST → IR → object code. driver-lang
gives you three pieces to express that chain and run it:

- A **[`Stage<C>`]#stage** is one phase — an `Input` type, an `Output` type, and a
  `run` that turns the first into the second while reading and writing a shared
  session. Unlike a *pass* (which rewrites one type in place), a stage **changes the
  type** as the program moves down the pipeline.
- A **[`Session<C>`]#session** is the ambient state every stage shares: the
  compilation configuration `C`, and the [`Diagnostic`]#diagnostics the phases
  emit. It tracks how many diagnostics were errors, so the driver can stop at the
  right point.
- A **[`Pipeline<C, S>`]#pipeline** composes stages end to end. [`then`]#then
  only accepts a stage whose input matches the current output — the phases are
  checked to connect at compile time — and [`run`]#pipeline-run threads an input
  through the whole chain.

There is no dynamic dispatch: a pipeline is one monomorphized type, so composing
stages costs nothing over calling them by hand.

<br>

## Public API

<a id="session"></a>
### `Session<C>`

```rust
pub struct Session<C> { /* private */ }
```

The ambient state threaded through one compilation run. It owns the compilation
*configuration* (`C` — target, options, input paths, whatever a language needs) and
the *diagnostics* every stage emits, and it keeps a running count of how many were
errors. Every [`Stage`](#stage) receives `&mut Session<C>`, so the session is how an
earlier phase's findings reach a later one.

`C` is whatever a language defines. The session never inspects it — it hands out
`&C` / `&mut C` and otherwise leaves it alone — so the crate holds no opinion about
what a configuration looks like. Derives `Debug` and `Clone` when `C` does.

---

<a id="session-new"></a>
#### `Session::new`

```rust
pub fn new(config: C) -> Session<C>
```

Create a session over a configuration, with no diagnostics recorded yet.

**Parameters**

- `config: C` — the compilation configuration this run carries. Borrow it later with
  [`config`]#config, mutate it with [`config_mut`]#config_mut, or reclaim it with
  [`into_config`]#into_config.

**Returns** a fresh `Session<C>` with an empty diagnostic list and a zero error count.

**Examples**

```rust
use driver_lang::Session;

// A unit config when a run needs no options.
let session = Session::new(());
assert!(!session.has_errors());

// A real config: here, a target triple.
let session = Session::new("x86_64-unknown-linux-gnu");
assert_eq!(*session.config(), "x86_64-unknown-linux-gnu");
```

---

<a id="config"></a>
#### `Session::config` / <a id="config_mut"></a>`config_mut` / <a id="into_config"></a>`into_config`

```rust
pub fn config(&self) -> &C
pub fn config_mut(&mut self) -> &mut C
pub fn into_config(self) -> C
```

Access the configuration. [`config`](#config) borrows it (any stage can read the
options a run was started with), [`config_mut`](#config_mut) borrows it mutably (a
stage can update an option later stages read), and [`into_config`](#into_config)
consumes the session and returns the config, discarding the diagnostics — call
[`take_diagnostics`](#take_diagnostics) first if you need to keep them.

**Returns** `&C`, `&mut C`, and `C` respectively.

**Examples**

```rust
use driver_lang::Session;

struct Options { optimize: bool }

let mut session = Session::new(Options { optimize: false });
assert!(!session.config().optimize);

// A stage flips an option the next stage will read.
session.config_mut().optimize = true;
assert!(session.config().optimize);

// Reclaim the config when the run is done.
let options = session.into_config();
assert!(options.optimize);
```

---

<a id="emit"></a>
#### `Session::emit`

```rust
pub fn emit(&mut self, diagnostic: Diagnostic) -> &mut Self
```

Record a [`Diagnostic`](#diagnostic), updating the error count if it is an error.
This is the one path by which diagnostics enter the session; the
[`error`](#session-error) / [`warn`](#warn) / [`note`](#note) shorthands all route
through it. Returns `&mut Self` so emissions can be chained.

**Parameters**

- `diagnostic: Diagnostic` — the message to record. If its severity is
  [`Error`]#severity, [`error_count`]#error_count increments.

**Returns** `&mut Self` for chaining.

**Examples**

```rust
use driver_lang::{Diagnostic, Session};

let mut session = Session::new(());
session
    .emit(Diagnostic::error("cannot find type `Foo`"))
    .emit(Diagnostic::note("defined in a later module"));

assert_eq!(session.diagnostics().len(), 2);
assert_eq!(session.error_count(), 1); // the note does not count
```

Use `emit` directly when a diagnostic is built elsewhere (for example, one carried
across a boundary):

```rust
use driver_lang::{Diagnostic, Severity, Session};

fn from_level(level: u8, msg: &'static str) -> Diagnostic {
    let severity = if level == 0 { Severity::Error } else { Severity::Warning };
    Diagnostic::new(severity, msg)
}

let mut session = Session::new(());
session.emit(from_level(0, "hard failure"));
assert!(session.has_errors());
```

---

<a id="session-error"></a>
#### `Session::error` / <a id="warn"></a>`warn` / <a id="note"></a>`note`

```rust
pub fn error(&mut self, message: impl Into<Cow<'static, str>>) -> &mut Self
pub fn warn(&mut self, message: impl Into<Cow<'static, str>>) -> &mut Self
pub fn note(&mut self, message: impl Into<Cow<'static, str>>) -> &mut Self
```

Emit a diagnostic of the named severity — shorthands for
`emit(Diagnostic::error(message))` and friends. These are the common way a stage
reports what it found.

**Parameters**

- `message: impl Into<Cow<'static, str>>` — the message. A string literal is stored
  without allocation; an owned `String` (e.g. from `format!`) is taken by value.

**Returns** `&mut Self` for chaining.

**Examples**

```rust
use driver_lang::Session;

let mut session = Session::new(());
session
    .error("expected `;`")
    .warn("unused variable `x`")
    .note("`x` is introduced here");

assert_eq!(session.error_count(), 1);
assert_eq!(session.diagnostics().len(), 3);
```

A computed message with `format!`:

```rust
use driver_lang::Session;

let mut session = Session::new(());
let name = "Frobnicate";
session.error(format!("unknown function `{name}`"));
assert_eq!(session.diagnostics()[0].message(), "unknown function `Frobnicate`");
```

---

<a id="diagnostics"></a>
#### `Session::diagnostics`

```rust
pub fn diagnostics(&self) -> &[Diagnostic]
```

All diagnostics recorded so far, in emission order. Read them to render output, or
inspect them in a test.

**Returns** a slice `&[Diagnostic]` over every emission.

**Examples**

```rust
use driver_lang::Session;

let mut session = Session::new(());
session.note("a").warn("b").error("c");

let rendered: Vec<String> = session.diagnostics().iter().map(|d| d.to_string()).collect();
assert_eq!(rendered, ["note: a", "warning: b", "error: c"]);
```

---

<a id="take_diagnostics"></a>
#### `Session::take_diagnostics`

```rust
pub fn take_diagnostics(&mut self) -> Vec<Diagnostic>
```

Remove and return all recorded diagnostics, resetting the error count to zero. The
configuration is left untouched. Use it to drain diagnostics for rendering between
runs, or to hand them off before [`into_config`](#into_config) discards the session.

**Returns** an owned `Vec<Diagnostic>` of every diagnostic emitted since the last
drain.

**Examples**

```rust
use driver_lang::Session;

let mut session = Session::new(());
session.error("boom").warn("hmm");

let drained = session.take_diagnostics();
assert_eq!(drained.len(), 2);

// The session is clean again.
assert!(!session.has_errors());
assert!(session.diagnostics().is_empty());
```

---

<a id="error_count"></a>
#### `Session::error_count` / <a id="has_errors"></a>`has_errors`

```rust
pub fn error_count(&self) -> usize
pub fn has_errors(&self) -> bool
```

How many [`Error`](#severity)-severity diagnostics have been emitted, and whether
that count is non-zero. Both are O(1) — the count is maintained incrementally as
diagnostics are emitted — so a driver can check them between every phase without
re-scanning the list.

**Returns** the error count as `usize`, and `error_count() != 0` as `bool`.

**Examples**

```rust
use driver_lang::Session;

let mut session = Session::new(());
assert_eq!(session.error_count(), 0);
assert!(!session.has_errors());

session.warn("just a warning");
assert!(!session.has_errors()); // warnings do not count

session.error("a real error");
assert_eq!(session.error_count(), 1);
assert!(session.has_errors());
```

---

<a id="abort_if_errors"></a>
#### `Session::abort_if_errors`

```rust
pub fn abort_if_errors(&self) -> Result<(), DriverError>
```

Stop the run if any error has been emitted. This is the driver's checkpoint: emit
diagnostics through a phase, then call `abort_if_errors` to turn accumulated errors
into the [`DriverError`](#drivererror) that ends the pipeline — the "aborting due to
N previous errors" step at the end of a compiler phase. Because a stage can emit
several errors before this is checked, one checkpoint reports many problems at once
instead of stopping at the first.

**Returns**

- `Ok(())` when the session holds no errors — so `session.abort_if_errors()?` inside
  [`run`]#stage-run is a no-op on a healthy run.
- `Err(DriverError)` reporting the error count otherwise. The error has no stage name
  yet; the [`Pipeline`]#pipeline stamps in the stage that made the call.

**Examples**

Clean session — no-op:

```rust
use driver_lang::Session;

let mut session = Session::new(());
session.warn("only a warning");
assert!(session.abort_if_errors().is_ok());
```

As a phase checkpoint that reports every error at once:

```rust
use driver_lang::{DriverError, Session, Stage, Pipeline};

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 token in input.split_whitespace() {
            match token.parse::<i64>() {
                Ok(n) => out.push(n),
                Err(_) => { session.error("not an integer"); }
            }
        }
        session.abort_if_errors()?; // reports both bad tokens together
        Ok(out)
    }
}

let mut driver = Pipeline::new(Lex);
let mut session = Session::new(());
let err = driver.run("1 x 2 y", &mut session).unwrap_err();
assert_eq!(err.message(), "aborting due to 2 previous errors");
```

<br>

<a id="stage"></a>
### `Stage<C>`

```rust
pub trait Stage<C> {
    type Input;
    type Output;
    fn name(&self) -> &'static str;
    fn run(&mut self, input: Self::Input, session: &mut Session<C>)
        -> Result<Self::Output, DriverError>;
}
```

One phase of compilation: a transform from an input artifact to an output artifact,
run against a [`Session`](#session). Implement it for each phase of your compiler —
a lexer is `Stage<Input = Source, Output = Tokens>`, a parser is `Stage<Input =
Tokens, Output = Ast>`, and so on. A stage is generic over the session's
configuration type `C`, so every stage in a pipeline shares one configuration and
one diagnostic buffer.

Compose stages with [`Pipeline`](#pipeline), which enforces that each stage's
`Output` is the next stage's `Input`.

**Contract**

- [`name`]#stage-name is a stable, static identifier used to attribute errors. It
  must not change between runs.
- [`run`]#stage-run consumes `input`, may read and write the session, and returns
  the output — or a [`DriverError`]#drivererror if it cannot proceed. It must not
  panic on a recoverable condition.

---

<a id="stage-name"></a>
#### `Stage::name`

```rust
fn name(&self) -> &'static str
```

A stable, static name for this stage. It appears in a [`DriverError`](#drivererror)
when the stage fails, so a caller can tell which phase halted the run.

**Returns** the stage's identifier.

**Examples**

```rust
use driver_lang::{DriverError, Session, Stage};

struct TypeCheck;
impl Stage<()> for TypeCheck {
    type Input = ();
    type Output = ();
    fn name(&self) -> &'static str { "type-check" }
    fn run(&mut self, _input: (), _s: &mut Session<()>) -> Result<(), DriverError> {
        Ok(())
    }
}

assert_eq!(TypeCheck.name(), "type-check");
```

---

<a id="stage-run"></a>
#### `Stage::run`

```rust
fn run(&mut self, input: Self::Input, session: &mut Session<C>)
    -> Result<Self::Output, DriverError>
```

Run the stage: consume `input`, use the [`Session`](#session) as needed, and produce
the output.

**Parameters**

- `input: Self::Input` — the previous phase's artifact, taken by value.
- `session: &mut Session<C>` — the shared session. Read configuration with
  [`config`]#config, record findings with [`error`]#session-error /
  [`warn`]#warn / [`note`]#note.

**Returns**

- `Ok(Self::Output)` — the artifact for the next phase.
- `Err(DriverError)` — the phase cannot proceed; the pipeline stops here and stamps
  this stage's name into the error. Return `Err` (or call
  [`abort_if_errors`]#abort_if_errors) rather than panicking.

**Examples**

A stage that emits a diagnostic and still succeeds — emitting an error *diagnostic*
records a problem but does not by itself stop the pipeline:

```rust
use driver_lang::{DriverError, Session, Stage};

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"); }
            }
        }
        Ok(out)
    }
}

let mut session = Session::new(());
let tokens = Lex.run("1 two 3", &mut session).unwrap();
assert_eq!(tokens, vec![1, 3]);
assert_eq!(session.diagnostics().len(), 1);
```

A stage that fails outright:

```rust
use driver_lang::{DriverError, Session, Stage};

struct RequireNonEmpty;
impl Stage<()> for RequireNonEmpty {
    type Input = Vec<i64>;
    type Output = Vec<i64>;
    fn name(&self) -> &'static str { "require-non-empty" }
    fn run(&mut self, input: Vec<i64>, _s: &mut Session<()>)
        -> Result<Vec<i64>, DriverError>
    {
        if input.is_empty() {
            return Err(DriverError::new("no input"));
        }
        Ok(input)
    }
}

let mut session = Session::new(());
let err = RequireNonEmpty.run(vec![], &mut session).unwrap_err();
assert_eq!(err.message(), "no input");
```

<br>

<a id="pipeline"></a>
### `Pipeline<C, S>`

```rust
pub struct Pipeline<C, S> { /* private */ }
```

An end-to-end driver: a chain of [`Stage`](#stage)s whose types line up, run against
a [`Session`](#session). Start from one stage with [`new`](#pipeline-new), extend it
with [`then`](#then), and drive it with [`run`](#pipeline-run).

`C` is a phantom parameter — a pipeline threads a `&mut Session<C>` through its
stages but stores no `C` of its own — so it is inferred from the stages and you never
name it. On the first stage that returns a [`DriverError`](#drivererror), the
pipeline stops and the error names the failing stage.

---

<a id="pipeline-new"></a>
#### `Pipeline::new`

```rust
pub fn new(stage: S) -> Pipeline<C, S>
```

Start a pipeline from a single stage.

**Parameters**

- `stage: S` — the first (and, until you call [`then`]#then, only) stage.

**Returns** a `Pipeline<C, S>` wrapping the stage.

**Examples**

```rust
use driver_lang::{DriverError, Pipeline, Session, Stage};

struct Identity;
impl Stage<()> for Identity {
    type Input = i64;
    type Output = i64;
    fn name(&self) -> &'static str { "identity" }
    fn run(&mut self, input: i64, _s: &mut Session<()>) -> Result<i64, DriverError> {
        Ok(input)
    }
}

let mut driver = Pipeline::new(Identity);
let mut session = Session::new(());
assert_eq!(driver.run(42, &mut session).unwrap(), 42);
```

---

<a id="then"></a>
#### `Pipeline::then`

```rust
pub fn then<N>(self, next: N) -> Pipeline<C, Then<S, N>>
where
    N: Stage<C, Input = S::Output>,
```

Append a stage whose [`Input`](#stage) is this pipeline's current
[`Output`](#stage). The bound `N: Stage<C, Input = S::Output>` is the compile-time
check that the phases connect — a stage that does not accept what the pipeline
currently produces is a type error, not a runtime surprise.

**Parameters**

- `next: N` — the stage to run after the current chain. Its `Input` must equal the
  current `Output`, and it must share the configuration type `C`.

**Returns** a longer `Pipeline<C, Then<S, N>>` that produces `N`'s output.

**Examples**

```rust
use driver_lang::{DriverError, Pipeline, Session, Stage};

struct Lex;
impl Stage<()> for Lex {
    type Input = &'static str;
    type Output = Vec<i64>;
    fn name(&self) -> &'static str { "lex" }
    fn run(&mut self, i: &'static str, _s: &mut Session<()>) -> Result<Vec<i64>, DriverError> {
        i.split_whitespace().map(|w| w.parse().map_err(|_| DriverError::new("bad"))).collect()
    }
}
struct Sum;
impl Stage<()> for Sum {
    type Input = Vec<i64>;
    type Output = i64;
    fn name(&self) -> &'static str { "sum" }
    fn run(&mut self, i: Vec<i64>, _s: &mut Session<()>) -> Result<i64, DriverError> {
        Ok(i.iter().sum())
    }
}
struct Negate;
impl Stage<()> for Negate {
    type Input = i64;
    type Output = i64;
    fn name(&self) -> &'static str { "negate" }
    fn run(&mut self, i: i64, _s: &mut Session<()>) -> Result<i64, DriverError> { Ok(-i) }
}

// Chain three phases; each `then` checks the types connect.
let mut driver = Pipeline::new(Lex).then(Sum).then(Negate);
let mut session = Session::new(());
assert_eq!(driver.run("1 2 3", &mut session).unwrap(), -6);
```

---

<a id="pipeline-run"></a>
#### `Pipeline::run`

```rust
pub fn run(&mut self, input: S::Input, session: &mut Session<C>)
    -> Result<S::Output, DriverError>
```

Run the pipeline: thread `input` through every stage in order and return the final
output. Stages run left to right, each receiving the previous stage's output and the
shared session. The run stops at the first stage that returns a
[`DriverError`](#drivererror).

**Parameters**

- `input: S::Input` — the input to the first stage.
- `session: &mut Session<C>` — the session shared by every stage.

**Returns**

- `Ok(S::Output)` — the final artifact.
- `Err(DriverError)` — the first stage that failed, with its name stamped in. Later
  stages do not run; diagnostics emitted before the failure remain in the session.

**Examples**

Success, and a failure attributed to the right stage:

```rust
use driver_lang::{DriverError, Pipeline, Session, Stage};

struct Lex;
impl Stage<()> for Lex {
    type Input = &'static str;
    type Output = Vec<i64>;
    fn name(&self) -> &'static str { "lex" }
    fn run(&mut self, i: &'static str, _s: &mut Session<()>) -> Result<Vec<i64>, DriverError> {
        i.split_whitespace()
            .map(|w| w.parse().map_err(|_| DriverError::new("not an integer")))
            .collect()
    }
}
struct Sum;
impl Stage<()> for Sum {
    type Input = Vec<i64>;
    type Output = i64;
    fn name(&self) -> &'static str { "sum" }
    fn run(&mut self, i: Vec<i64>, _s: &mut Session<()>) -> Result<i64, DriverError> {
        Ok(i.iter().sum())
    }
}

let mut driver = Pipeline::new(Lex).then(Sum);
let mut session = Session::new(());

assert_eq!(driver.run("4 5 6", &mut session).unwrap(), 15);

let err = driver.run("4 oops 6", &mut session).unwrap_err();
assert_eq!(err.stage(), "lex"); // the parse failed in `lex`, before `sum`
```

---

<a id="pipeline-name"></a>
#### `Pipeline::name`

```rust
pub fn name(&self) -> &'static str
```

The name of the final stage — the stage whose output this pipeline produces.

**Returns** the last stage's [`name`](#stage-name).

**Examples**

```rust
use driver_lang::{DriverError, Pipeline, Session, Stage};

struct A;
impl Stage<()> for A {
    type Input = ();
    type Output = ();
    fn name(&self) -> &'static str { "a" }
    fn run(&mut self, _i: (), _s: &mut Session<()>) -> Result<(), DriverError> { Ok(()) }
}
struct B;
impl Stage<()> for B {
    type Input = ();
    type Output = ();
    fn name(&self) -> &'static str { "b" }
    fn run(&mut self, _i: (), _s: &mut Session<()>) -> Result<(), DriverError> { Ok(()) }
}

let driver = Pipeline::new(A).then(B);
assert_eq!(driver.name(), "b");
```

---

<a id="then-type"></a>
#### `Then<A, B>`

```rust
pub struct Then<A, B> { /* private */ }
```

Two stages run back to back — the node [`Pipeline::then`](#then) builds. `Then<A, B>`
is itself a [`Stage`](#stage): it feeds an input through `A`, then feeds `A`'s output
through `B`, and its `Input` / `Output` are `A::Input` / `B::Output`. You rarely name
it directly — a three-stage pipeline has type `Pipeline<C, Then<Then<A, B>, C2>>`,
assembled for you by chaining `.then(...)`. It is public only because it appears in
those inferred types. Composition is monomorphized: each stage is a direct,
inlinable call with no boxing.

<br>

<a id="diagnostic"></a>
### `Diagnostic`

```rust
pub struct Diagnostic { /* private */ }

impl Diagnostic {
    pub fn new(severity: Severity, message: impl Into<Cow<'static, str>>) -> Diagnostic
    pub fn error(message: impl Into<Cow<'static, str>>) -> Diagnostic
    pub fn warning(message: impl Into<Cow<'static, str>>) -> Diagnostic
    pub fn note(message: impl Into<Cow<'static, str>>) -> Diagnostic
    pub fn severity(&self) -> Severity
    pub fn message(&self) -> &str
    pub fn is_error(&self) -> bool
}
```

One message a stage reports about the program under compilation: a
[`Severity`](#severity) paired with text. Stages create diagnostics and hand them to
the [`Session`](#session) with [`emit`](#emit) (or the [`error`](#session-error) /
[`warn`](#warn) / [`note`](#note) shorthands).

The message is a `Cow<'static, str>`: a constant message costs no allocation, a
computed one is owned only when built. The type is deliberately small — a severity
and a message. It does not model spans or error codes; a language plugs a richer
diagnostics representation in as its own concern.

- `new` builds a diagnostic with an explicit severity; `error` / `warning` / `note`
  are shorthands.
- `severity` and `message` read the parts; `is_error` is `severity().is_error()`.
- `Display` renders as `severity: message` (e.g. `error: unexpected token`).
- Derives `Debug`, `Clone`, `PartialEq`, `Eq`, and — behind the `serde` feature —
  `Serialize`.

**Parameters** (constructors)

- `severity: Severity` (`new` only) — the seriousness.
- `message: impl Into<Cow<'static, str>>` — a string literal (no allocation) or an
  owned `String`.

**Examples**

```rust
use driver_lang::{Diagnostic, Severity};

let d = Diagnostic::error("unexpected `}`");
assert_eq!(d.severity(), Severity::Error);
assert_eq!(d.message(), "unexpected `}`");
assert!(d.is_error());
assert_eq!(d.to_string(), "error: unexpected `}`");

// `new` with an explicit, possibly computed, severity.
let level = 1;
let severity = if level == 0 { Severity::Error } else { Severity::Warning };
let d = Diagnostic::new(severity, format!("issue at level {level}"));
assert!(!d.is_error());
```

<br>

<a id="severity"></a>
### `Severity`

```rust
pub enum Severity { Error, Warning, Note }

impl Severity {
    pub fn is_error(self) -> bool
    pub fn as_str(self) -> &'static str
}
```

The seriousness of a [`Diagnostic`](#diagnostic). Only `Error` counts toward
[`Session::error_count`](#error_count); a `Warning` or `Note` is recorded and
rendered but never trips [`abort_if_errors`](#abort_if_errors).

- `is_error` is `true` only for `Error`.
- `as_str` is the lowercase label (`"error"`, `"warning"`, `"note"`); `Display` uses
  it.
- Derives `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash`, and — behind the
  `serde` feature — `Serialize` (rendered lowercase). It does **not** derive `Ord`:
  compare with `is_error`, not a numeric rank.

**Examples**

```rust
use driver_lang::Severity;

assert!(Severity::Error.is_error());
assert!(!Severity::Warning.is_error());
assert_eq!(Severity::Note.as_str(), "note");
assert_eq!(Severity::Warning.to_string(), "warning");
```

<br>

<a id="drivererror"></a>
### `DriverError`

```rust
pub struct DriverError { /* private */ }

impl DriverError {
    pub fn new(message: impl Into<Cow<'static, str>>) -> DriverError
    pub fn stage(&self) -> &str
    pub fn message(&self) -> &str
}
```

The error that stops a compilation run. A [`Stage`](#stage) returns one from
[`run`](#stage-run) when it cannot produce its output; it is the *control-flow*
failure that ends the pipeline, distinct from a [`Diagnostic`](#diagnostic) (a record
a stage emits and may keep going after).

A stage does not name itself: it returns `DriverError::new(reason)` with an empty
stage, and the [`Pipeline`](#pipeline) stamps in the failing stage's name as it
propagates. Stamping fills only an empty name, so in a chain the innermost stage —
the one that actually failed — keeps the attribution.

- `new` builds an error with an empty stage; `stage` and `message` read the parts.
- `Display` renders as `stage \`name\` failed: message`, or `driver error: message`
  before a stage name is stamped in.
- Implements `std::error::Error`; derives `Debug`, `Clone`, `PartialEq`, `Eq`.

**Parameters**

- `message: impl Into<Cow<'static, str>>` — a string literal (no allocation) or an
  owned `String`.

**Examples**

```rust
use driver_lang::DriverError;

// A static reason.
let err = DriverError::new("unresolved import");
assert_eq!(err.message(), "unresolved import");
assert_eq!(err.stage(), ""); // empty until it flows through a Pipeline

// A computed reason.
let path = "src/main";
let err = DriverError::new(format!("cannot read `{path}.rs`"));
assert_eq!(err.message(), "cannot read `src/main.rs`");
assert_eq!(err.to_string(), "driver error: cannot read `src/main.rs`");
```

<br>

## Feature flags

| Feature | Default | Description                                                                 |
|---------|:-------:|-----------------------------------------------------------------------------|
| `std`   || Links the standard library. With it off the crate is `no_std` + `alloc`; every type works in both modes. |
| `serde` || Derives `serde::Serialize` for [`Severity`]#severity and [`Diagnostic`]#diagnostic. |

Feature flags are additive: enabling `serde` never changes existing behavior, and
disabling `std` removes no type or method — it only switches the crate to `no_std`.

<br>

## Design notes

**A driver, not a pass manager.** A pass rewrites one type in place; a stage changes
the type as the artifact moves down the pipeline. driver-lang manages that
type-threading, the shared session, and the clean stop — the concerns unique to
driving a whole compile. IR-level, same-type transforms belong in a pass manager
([`pass-lang`](https://crates.io/crates/pass-lang)) that a stage can run internally.

**No first-party dependencies.** The crate is generic over the artifacts that flow
through it and the configuration the session carries. It does not wire a lexer, a
parser, or a specific diagnostics crate — a language plugs those in as stages and as
the session's `C`. This keeps the driver a thin orchestrator with nothing to force on
a consumer, and keeps its dependency surface empty but for optional `serde`.

**Single-threaded by design.** A compile pipeline is an inherently ordered sequence
of phases, each consuming the last one's output, so the driver carries no atomic
overhead. Parallelism, where a language wants it, lives inside a stage (parsing many
files at once, say), not in the driver that sequences the phases.

**Diagnostics are policy-free.** The session records diagnostics and counts errors;
it never decides on its own to stop. A stage decides, by returning a
[`DriverError`](#drivererror) or calling [`abort_if_errors`](#abort_if_errors). This
keeps mechanism (recording) separate from policy (when a run is doomed), so a
language can collect as many errors as it wants before halting.

<br>

---

<div align="center">
    <sup>
        <a href="../README.md" title="Project Home"><b>HOME</b></a>
        <span>&nbsp;&nbsp;</span>
        <span>API</span>
        <span>&nbsp;&nbsp;</span>
        <a href="../CHANGELOG.md" title="Changelog"><b>CHANGELOG</b></a>
    </sup>
    <br><br>
    <sub>Copyright &copy; 2026 <strong>James Gober</strong>.</sub>
</div>