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
main
Mike Gerwitz 2023-02-21 16:40:36 -05:00
parent 29178f2360
commit 33d2b4f0b8
4 changed files with 89 additions and 6 deletions

View File

@ -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<R: Reporter>(
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<R: Reporter>(
fn derive_xmli(
_asg: tamer::asg::Asg,
mut fout: impl std::io::Write,
escaper: &DefaultEscaper,
) -> Result<(), UnrecoverableError> {
fout.write_all(b"<it-has-begun />")?;
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<Text>, XirfToken<Text>, Infallible>,
XirfAutoClose,
_,
>::lower::<_, UnrecoverableError>(&mut head, |xirf| {
Lower::<XirfAutoClose, XirfToXir<Text>, _>::lower(xirf, |xir| {
terminal::<XirfToXir<Text>, 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<FinalizeError> for UnrecoverableError {
}
}
impl From<Infallible> for UnrecoverableError {
fn from(_: Infallible) -> Self {
unreachable!("<UnrecoverableError as From<Infallible>>::from")
}
}
impl<T: Token> From<ParseError<T, Infallible>> for UnrecoverableError {
fn from(_: ParseError<T, Infallible>) -> Self {
unreachable!(
"<UnrecoverableError as From<ParseError<T, Infallible>>>::from"
)
}
}
impl From<ParseError<UnknownToken, xir::Error>> for RecoverableError {
fn from(e: ParseError<UnknownToken, xir::Error>) -> Self {
Self::XirParseError(e)

View File

@ -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},

View File

@ -257,6 +257,8 @@ pub type WidenedParsedResult<S, E> =
/// itself a [`ParseState`].
///
/// See [`ParsedObject`] for more information.
///
/// This is the dual of [`terminal`].
pub fn lowerable<T: Token, O: Object, E: Diagnostic + PartialEq>(
iter: impl Iterator<Item = Result<O, E>>,
) -> impl Iterator<Item = ParsedResult<ParsedObject<T, O, E>>> {
@ -265,6 +267,28 @@ pub fn lowerable<T: Token, O: Object, E: Diagnostic + PartialEq>(
})
}
/// 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<ParseError<S::Token, S::Error>>,
>(
iter: impl Iterator<Item = ParsedResult<S>>,
) -> impl Iterator<Item = Result<S::Object, E>> {
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,

View File

@ -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<PDepth> for Depth {
/// Stack of open elements and associated metadata.
#[derive(Debug, PartialEq)]
struct AutoCloseStack(Vec<StackItem>);
pub struct AutoCloseStack(Vec<StackItem>);
type StackItem = (QName, Span, VDepth, PDepth);