tamer: pipeline::lower_xmli: Use `lower_pipeline!`

All lowering pipelines are now using `lower_pipeline!`.  Finally.

The macro does require some refactoring and documentation, but it's working,
and we now have three pipelines whose definitions are smaller than a single
one was previously.  I've been hoping to do this for many months, so it's
nice to finally see this come to fruition.

I had been putting it off, but doing so has made it difficult to compose
other parts of the system, not knowing what abstractions I'll have at my
disposal.

DEV-13162
main
Mike Gerwitz 2023-06-02 13:40:25 -04:00
parent 109ba5f797
commit 6a99ee3cb3
2 changed files with 53 additions and 44 deletions

View File

@ -198,7 +198,7 @@ fn derive_xmli(
// TODO: Remove bad file?
// Let make do it?
let mut st = WriterState::default();
pipeline::lower_xmli(src, &asg, |result| {
let (_asg,) = pipeline::lower_xmli(src, &asg, |result| {
// Write failures should immediately bail out;
// we can't skip writing portions of the file and
// just keep going!
@ -207,7 +207,9 @@ fn derive_xmli(
.map(|newst| st = newst)
.map_err(Into::<UnrecoverableError>::into)
})
})
})?;
Ok(())
}
/// Entrypoint for the compiler

View File

@ -43,13 +43,13 @@
use std::convert::Infallible;
use crate::{
asg::{air::AirAggregate, visit::TreeWalkRel, Asg, AsgTreeToXirf},
asg::{air::AirAggregate, visit::TreeWalkRel, AsgTreeToXirf},
diagnose::Diagnostic,
nir::{InterpolateNir, NirToAir, TplShortDesugar, XirfToNir},
obj::xmlo::{XmloReader, XmloToAir, XmloToken},
parse::{
terminal, FinalizeError, FromParseError, Lower, LowerSource,
ParseError, ParseState, Parsed, ParsedObject, UnknownToken,
terminal, FinalizeError, Lower, LowerSource, ParseError, ParseState,
Parsed, ParsedObject, UnknownToken,
},
xir::{
autoclose::XirfAutoClose,
@ -75,25 +75,27 @@ use crate::{
macro_rules! lower_pipeline {
($(
$(#[$meta:meta])*
$vis:vis $fn:ident($srcobj:ty, $srcerr:ty)
$vis:vis $fn:ident$(<$l:lifetime>)?($srcobj:ty, $srcerr:ty)
$(|> $lower:ty $([$ctx:ident])? $(, until ($until:pat))?)*;
)*) => {$(
$(#[$meta])*
$vis fn $fn<ER: Diagnostic, EU: Diagnostic>(
$vis fn $fn<$($l,)? ER: Diagnostic, EU: Diagnostic>(
src: impl LowerSource<UnknownToken, $srcobj, $srcerr>,
$(
// Each parser may optionally receive context from an
// earlier run.
$($ctx: impl Into<<$lower as ParseState>::PubContext>,)?
)*
sink: impl FnMut(Result<(), ER>) -> Result<(), EU>,
sink: impl FnMut(
Result<lower_pipeline!(@last_obj_ty $($lower),*), ER>
) -> Result<(), EU>,
) -> Result<
(
$(
// Any context that is passed in is also returned so
// that individual pipelines can continue to build
// upon state from previous pipelines.
$( lower_pipeline!(@ret_ty $lower, $ctx), )?
$( lower_pipeline!(@ret_ctx_ty $lower, $ctx), )?
)*
),
EU
@ -134,10 +136,18 @@ macro_rules! lower_pipeline {
}
)*};
(@ret_ty $lower:ty, $_ctx:ident) => {
(@ret_ctx_ty $lower:ty, $_ctx:ident) => {
<$lower as ParseState>::PubContext
};
// The last object type enters the sink.
(@last_obj_ty $lower:ty, $($rest:ty),+) => {
lower_pipeline!(@last_obj_ty $($rest),+)
};
(@last_obj_ty $last:ty) => {
<$last as ParseState>::Object
};
// Because of how the lowering pipeline composes,
// contexts are nested with a terminal unit at the left,
// as in `(((), B), A)` with `A` being a parser that appears earlier
@ -169,6 +179,27 @@ macro_rules! lower_pipeline {
})
};
// TODO: Roll this into the above
(
@body_head($src:ident, $srcobj:ty, $srcerr:ty, $sink:ident)
(|> $head:ty [$ctx:ident]) $($rest:tt)*
) => {
Lower::<
ParsedObject<UnknownToken, $srcobj, $srcerr>,
$head,
ER,
>::lower_with_context::<_, EU>(
&mut $src.map(|result| result.map_err(ER::from)),
$ctx,
|next| {
lower_pipeline!(
@body_inner(next, $head, $sink)
$($rest)*
)
}
)
};
// Lower without context
// (with the default context for the parser).
//
@ -189,6 +220,8 @@ macro_rules! lower_pipeline {
// Lower with a context provided by the caller,
// optionally with an `until` clause that stops at (and includes) a
// matching object from the previous parser.
//
// TODO: Roll this into the above
(
@body_inner($next:ident, $lower_prev:ty, $sink:ident)
(|> $lower:ty [$ctx:ident] $(, until ($until:pat))?) $($rest:tt)*
@ -251,39 +284,13 @@ lower_pipeline! {
|> InterpolateNir
|> NirToAir
|> AirAggregate[air_ctx];
}
/// Lower an [`Asg`]-derived token stream into an `xmli` file.
///
/// TODO: More documentation once this has been further cleaned up.
pub fn lower_xmli<ER: Diagnostic, EU: Diagnostic>(
src: impl LowerSource<UnknownToken, TreeWalkRel, Infallible>,
asg: &Asg,
sink: impl FnMut(Result<XirToken, ER>) -> Result<(), EU>,
) -> Result<(), EU>
where
ER: From<ParseError<UnknownToken, Infallible>>
+ From<ParseError<TreeWalkRel, Infallible>>
+ FromParseError<XirfAutoClose>
+ FromParseError<XirfToXir<Text>>,
EU: From<FinalizeError>,
{
#[rustfmt::skip] // better visualize the structure despite the line length
Lower::<
ParsedObject<UnknownToken, TreeWalkRel, Infallible>,
AsgTreeToXirf,
ER,
>::lower_with_context::<_, EU>(
&mut src.map(|result| result.map_err(ER::from)),
asg,
|xirf_unclosed| {
Lower::<AsgTreeToXirf, XirfAutoClose, _>::lower(xirf_unclosed, |xirf| {
Lower::<XirfAutoClose, XirfToXir<Text>, _>::lower(xirf, |xir| {
terminal::<XirfToXir<Text>, _>(xir).try_for_each(sink)
})
})
}
)?;
Ok(())
/// Lower an [`Asg`](crate::asg::Asg)-derived token stream into an
/// `xmli` file.
///
/// TODO: More documentation once this has been further cleaned up.
pub lower_xmli<'a>(TreeWalkRel, Infallible)
|> AsgTreeToXirf<'a>[asg]
|> XirfAutoClose
|> XirfToXir<Text>;
}