Skip to main content

Pipeline

Struct Pipeline 

Source
pub struct Pipeline<C, S> { /* private fields */ }
Expand description

An end-to-end driver: a chain of Stages whose types line up, run against a Session.

A Pipeline is how stages become a compiler. You start from one stage with Pipeline::new and extend it with then, which appends a stage whose Input matches the current Output. That match is checked at compile time: a pipeline that tries to feed tokens into a stage expecting an AST does not build. The finished pipeline is driven with run, which threads an input through every stage in order and returns the final artifact.

On the first stage that returns a DriverError, the pipeline stops — later stages do not run — and the error names the stage that failed. Diagnostics a stage emitted before failing remain in the session.

The whole pipeline is a single monomorphized type, so composing stages adds no runtime indirection over calling them directly.

§Examples

A two-stage calculator driver — tokenize, then sum:

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::<i64>().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);

The configuration type C is a phantom parameter: a pipeline threads a &mut Session<C> through its stages but stores no C of its own. It is inferred from the stages, so you write Pipeline::new(stage) and never name C explicitly.

Implementations§

Source§

impl<C, S> Pipeline<C, S>

Source

pub fn new(stage: S) -> Self

Start a pipeline from a single stage.

§Examples
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 driver = Pipeline::new(Identity);
assert_eq!(driver.name(), "identity");
Source§

impl<C, S: Stage<C>> Pipeline<C, S>

Source

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

Append a stage whose Input is this pipeline’s current Output.

The type bound N: Stage<C, Input = S::Output> is the compile-time check that the phases connect: the appended stage must accept exactly what the pipeline currently produces. The result is a longer pipeline that produces N’s output.

§Examples
use driver_lang::{DriverError, Pipeline, Session, Stage};

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

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

let mut driver = Pipeline::new(Parse).then(Halve);
let mut session = Session::new(());
assert_eq!(driver.run("abcdef", &mut session).unwrap(), 3);
Source

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; that error names the failing stage.

§Errors

Returns the DriverError of the first stage that fails, with the stage’s name stamped in.

§Examples
use driver_lang::{DriverError, Pipeline, Session, Stage};

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

let mut driver = Pipeline::new(Fail);
let mut session = Session::new(());
let err = driver.run((), &mut session).unwrap_err();
assert_eq!(err.stage(), "fail");
assert_eq!(err.message(), "nope");
Source

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

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

Auto Trait Implementations§

§

impl<C, S> Freeze for Pipeline<C, S>
where S: Freeze,

§

impl<C, S> RefUnwindSafe for Pipeline<C, S>
where S: RefUnwindSafe,

§

impl<C, S> Send for Pipeline<C, S>
where S: Send,

§

impl<C, S> Sync for Pipeline<C, S>
where S: Sync,

§

impl<C, S> Unpin for Pipeline<C, S>
where S: Unpin,

§

impl<C, S> UnsafeUnpin for Pipeline<C, S>
where S: UnsafeUnpin,

§

impl<C, S> UnwindSafe for Pipeline<C, S>
where S: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.