From 31f6a102eb03f8d8224323687a1978f28d4c5359 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Thu, 8 Jun 2023 11:48:33 -0400 Subject: [PATCH] tamer: pipeline::macro: Partially applied pipeline This configures the pipeline and returns a closure that can then be provided with the source and sink. The next obvious step would be to curry the source and sink. But I wanted to commit this before I take a different (but equivalent) approach that makes the pipeline operations more explicit and helps to guide the user (developer) in developing and composing them. The FP approach is less boilerplate, but is also more general and provides less guidance. Given that composition at the topmost levels of the system, especially with all the types involved, is one of the most confusing aspects of the system---and one of the most important to get right and make clear, since it's intended to elucidate the entire system at a high level, and guide the reader. Well, it does a poor job at that now, but that's the ultimate goal. In essence---brutally general abstractions make sense at lower levels, but the complexity at higher levels benefits from rigid guardrails, even though it does not necessitate it. DEV-13162 --- tamer/src/bin/tamec.rs | 8 +++--- tamer/src/ld/poc.rs | 7 +++--- tamer/src/pipeline/macro.rs | 49 ++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/tamer/src/bin/tamec.rs b/tamer/src/bin/tamec.rs index 338fc156..cbbf7196 100644 --- a/tamer/src/bin/tamec.rs +++ b/tamer/src/bin/tamec.rs @@ -150,10 +150,8 @@ fn compile( // TODO: Determine a good default capacity once we have this populated // and can come up with some heuristics. - let (air_ctx,) = parse_package_xml( - src, - DefaultAsg::with_capacity(1024, 2048), - report_err, + let (air_ctx,) = parse_package_xml(DefaultAsg::with_capacity(1024, 2048))( + src, report_err, )?; match reporter.has_errors() { @@ -203,7 +201,7 @@ fn derive_xmli( // TODO: Remove bad file? // Let make do it? let mut st = WriterState::default(); - let (_asg,) = pipeline::lower_xmli(src, &asg, |result| { + let (_asg,) = pipeline::lower_xmli(&asg)(src, |result| { // Write failures should immediately bail out; // we can't skip writing portions of the file and // just keep going! diff --git a/tamer/src/ld/poc.rs b/tamer/src/ld/poc.rs index 99911780..4da7b896 100644 --- a/tamer/src/ld/poc.rs +++ b/tamer/src/ld/poc.rs @@ -106,9 +106,10 @@ fn load_xmlo, S: Escaper>( let src = &mut lowerable(XmlXirReader::new(file, escaper, ctx)); - let (mut state, mut air_ctx) = pipeline::load_xmlo::<_, TameldError, _>( - src, state, air_ctx, identity, - )?; + let (mut state, mut air_ctx) = + pipeline::load_xmlo::<_, TameldError, _, _, _>(state, air_ctx)( + src, identity, + )?; let mut dir = path; dir.pop(); diff --git a/tamer/src/pipeline/macro.rs b/tamer/src/pipeline/macro.rs index 89d0a002..d396f563 100644 --- a/tamer/src/pipeline/macro.rs +++ b/tamer/src/pipeline/macro.rs @@ -72,19 +72,20 @@ macro_rules! lower_pipeline { /// of the types of all parsers in the pipeline. /// It can be understood as: /// - /// 1. A function accepting three classes of arguments: - /// 1. The _source_ token stream, - /// which consists of tokens expected by the first parser - /// in the pipeline; - /// 2. _Context_ for certain parsers that request it, - /// allowing for state to persist between separate - /// pipelines; and - /// 3. A _sink_ that serves as the final destination for the - /// token stream. - /// 2. A [`Result`] consisting of the updated context that was + /// 1. A function accepting _context_ for whatever parsers request + /// it, + /// allowing both for configuration and for state to + /// persist between separate pipelines. + /// This returns a closure representing a configured pipeline. + /// 2. The _source_ token stream is accepted by the closure, + /// which consists of tokens expected by the first parser + /// in the pipeline; + /// 4. A _sink_ serves as the final destination for the token + /// stream. + /// 5. A [`Result`] consisting of the updated context that was /// originally passed into the function, /// so that it may be utilized in future pipelines. - /// 3. A _recoverable error_ type `ER` that may be utilized when + /// 6. A _recoverable error_ type `ER` that may be utilized when /// compilation should continue despite an error. /// All parsers are expected to perform their own error /// recovery in an attempt to continue parsing to discover @@ -94,25 +95,17 @@ macro_rules! lower_pipeline { /// errors of any parser in the pipeline, /// which is the reason for the large block of /// [`From`]s in this function's `where` clause. - /// 4. An _unrecoverable error_ type `EU` that may be yielded by + /// 7. An _unrecoverable error_ type `EU` that may be yielded by /// the sink to terminate compilation immediately. /// This is a component of the [`Result`] type that is /// ultimately yielded as the result of this function. - $vis fn $fn<$($l,)? ES: Diagnostic, ER: Diagnostic, EU: Diagnostic>( - src: impl LowerSource< - UnknownToken, - lower_pipeline!(@first_tok_ty $($lower),*), - ES - >, + $vis fn $fn<$($l,)? ES: Diagnostic, ER: Diagnostic, EU: Diagnostic, SA, SB>( $( // Each parser may optionally receive context from an // earlier run. $($ctx: impl Into<<$lower as ParseState>::PubContext>,)? )* - sink: impl FnMut( - Result - ) -> Result<(), EU>, - ) -> Result< + ) -> impl FnOnce(SA, SB) -> Result< ( $( // Any context that is passed in is also returned so @@ -147,7 +140,18 @@ macro_rules! lower_pipeline { // which is _not_ an error that parsers are expected to // recover from. EU: From, + + SA: LowerSource< + UnknownToken, + lower_pipeline!(@first_tok_ty $($lower),*), + ES + >, + + SB: FnMut( + Result + ) -> Result<(), EU> { + move |src, sink| { let lower_pipeline!(@ret_pat $($($ctx)?)*) = lower_pipeline!( @body_head(src, sink) $((|> $lower $([$ctx])? $(, until ($until))?))* @@ -156,6 +160,7 @@ macro_rules! lower_pipeline { Ok(($( $($ctx,)? )*)) + } } )*};