From 33d2b4f0b81dc9561d060ae50f2f91a86e10cb6f Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 21 Feb 2023 16:40:36 -0500 Subject: [PATCH] tamer: tamec: POC lowering pipeline with XirfAutoClose and XirfToXir This replaces the stub `derive_xmli` with the same result (well, minus a space before the '/' in the output) using what will become the lowering pipeline. Once again, this is quite verbose, and the lowering pipeline in general needs to be further abstracted away. Unlike the rest of the pipeline, an error during the derivation process will immediately terminate with an unrecoverable error, because we do not want to write partial files. This does not remove the garbage file, because the build system ought to do that itself (e.g. `make`)...but that is certainly open for debate. DEV-13708 --- tamer/src/bin/tamec.rs | 65 ++++++++++++++++++++++++++++++++++++-- tamer/src/parse.rs | 2 +- tamer/src/parse/lower.rs | 24 ++++++++++++++ tamer/src/xir/autoclose.rs | 4 +-- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/tamer/src/bin/tamec.rs b/tamer/src/bin/tamec.rs index 63e55339..18fd06e8 100644 --- a/tamer/src/bin/tamec.rs +++ b/tamer/src/bin/tamec.rs @@ -30,6 +30,7 @@ extern crate tamer; use getopts::{Fail, Options}; use std::{ + convert::Infallible, env, error::Error, fmt::{self, Display, Write}, @@ -50,7 +51,8 @@ use tamer::{ XirfToNirError, }, parse::{ - lowerable, FinalizeError, Lower, ParseError, ParsedObject, UnknownToken, + lowerable, FinalizeError, Lower, ParseError, ParsedObject, Token, + UnknownToken, }, xir::{ self, @@ -186,7 +188,7 @@ fn compile( false => { #[cfg(feature = "wip-asg-derived-xmli")] { - derive_xmli(asg, fout) + derive_xmli(asg, fout, &escaper) } #[cfg(not(feature = "wip-asg-derived-xmli"))] { @@ -215,8 +217,51 @@ fn compile( fn derive_xmli( _asg: tamer::asg::Asg, mut fout: impl std::io::Write, + escaper: &DefaultEscaper, ) -> Result<(), UnrecoverableError> { - fout.write_all(b"")?; + use tamer::{ + convert::ExpectInto, + iter::TrippableIterator, + parse::terminal, + span::UNKNOWN_SPAN, + xir::{ + autoclose::XirfAutoClose, + flat::{Depth, Text, XirfToXir}, + writer::XmlWriter, + OpenSpan, + }, + }; + + // Note how this does not contain a closing token; + // it is closed by `XirfAutoClose`. + let xir_toks = vec![XirfToken::Open( + "it-has-begun".unwrap_into(), + OpenSpan::without_name_span(UNKNOWN_SPAN), + Depth(0), + )]; + + let mut head = lowerable(xir_toks.into_iter().map(Ok)); + + // THIS IS A PROOF-OF-CONCEPT LOWERING PIPELINE. + Lower::< + ParsedObject, XirfToken, Infallible>, + XirfAutoClose, + _, + >::lower::<_, UnrecoverableError>(&mut head, |xirf| { + Lower::, _>::lower(xirf, |xir| { + terminal::, UnrecoverableError>(xir).while_ok( + |toks| { + // Write failures should immediately bail out; + // we can't skip writing portions of the file and + // just keep going! + toks.write(&mut fout, Default::default(), escaper)?; + Ok::<_, UnrecoverableError>(()) + }, + // TODO: Remove bad file? + // Let make do it? + ) + }) + })?; Ok(()) } @@ -391,6 +436,20 @@ impl From for UnrecoverableError { } } +impl From for UnrecoverableError { + fn from(_: Infallible) -> Self { + unreachable!(">::from") + } +} + +impl From> for UnrecoverableError { + fn from(_: ParseError) -> Self { + unreachable!( + ">>::from" + ) + } +} + impl From> for RecoverableError { fn from(e: ParseError) -> Self { Self::XirParseError(e) diff --git a/tamer/src/parse.rs b/tamer/src/parse.rs index d2f0235a..a8a15e56 100644 --- a/tamer/src/parse.rs +++ b/tamer/src/parse.rs @@ -30,7 +30,7 @@ mod trace; pub mod util; pub use error::{FinalizeError, ParseError}; -pub use lower::{lowerable, Lower, LowerIter, ParsedObject}; +pub use lower::{lowerable, terminal, Lower, LowerIter, ParsedObject}; pub use parser::{FinalizedParser, Parsed, ParsedResult, Parser}; pub use state::{ context::{Context, Empty as EmptyContext, NoContext}, diff --git a/tamer/src/parse/lower.rs b/tamer/src/parse/lower.rs index 320a0fac..b0c27e0f 100644 --- a/tamer/src/parse/lower.rs +++ b/tamer/src/parse/lower.rs @@ -257,6 +257,8 @@ pub type WidenedParsedResult = /// itself a [`ParseState`]. /// /// See [`ParsedObject`] for more information. +/// +/// This is the dual of [`terminal`]. pub fn lowerable( iter: impl Iterator>, ) -> impl Iterator>> { @@ -265,6 +267,28 @@ pub fn lowerable( }) } +/// Indicate a terminal parser in a lowering pipeline, +/// and unwrap the parse API. +/// +/// This is the dual of [`lowerable`], +/// responsible for breaking out of the pipeline for processing of the +/// final object stream. +/// +/// [`Parsed::Incomplete`] is filtered. +/// Errors are lifted into `E` just as would be expected by [`Lower`]. +pub fn terminal< + S: ParseState, + E: Diagnostic + From>, +>( + iter: impl Iterator>, +) -> impl Iterator> { + iter.filter_map(|result| match result { + Ok(Parsed::Incomplete) => None, + Ok(Parsed::Object(obj)) => Some(Ok(obj)), + Err(e) => Some(Err(e.into())), + }) +} + /// Representation of a [`ParseState`] producing some type of [`Object`]. /// /// This is intended to be used not as a value, diff --git a/tamer/src/xir/autoclose.rs b/tamer/src/xir/autoclose.rs index c05bcf93..6870543b 100644 --- a/tamer/src/xir/autoclose.rs +++ b/tamer/src/xir/autoclose.rs @@ -115,7 +115,7 @@ use XirfAutoClose::*; /// /// See the [module-level documentation](super) for more information. #[derive(Debug, PartialEq, Eq, Default)] -enum XirfAutoClose { +pub enum XirfAutoClose { /// Element contains no children and can be self-closing. #[default] EmptyEle, @@ -250,7 +250,7 @@ impl From for Depth { /// Stack of open elements and associated metadata. #[derive(Debug, PartialEq)] -struct AutoCloseStack(Vec); +pub struct AutoCloseStack(Vec); type StackItem = (QName, Span, VDepth, PDepth);