tamer: obj::xmle::xir: Extract ElemWrap into ir::xir::iter

This generalizes it a bit and provides tests, which was always the intent;
the existing code was POC to determine if this could be done without
performance degradation (see that commit for more information).
main
Mike Gerwitz 2021-10-11 10:33:24 -04:00
parent cde08b125c
commit bc5e8ebe75
3 changed files with 92 additions and 28 deletions

View File

@ -216,7 +216,7 @@ impl TryFrom<&str> for LocalPart {
}
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Whitespace(SymbolId);
impl Deref for Whitespace {
@ -415,7 +415,7 @@ impl AttrValue {
/// such as whether a node is open,
/// and so this IR can be processed by a simple state machine
/// (see [`writer::WriterState`]).
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Token {
/// Opening tag of an element.
Open(QName, Span),

View File

@ -18,21 +18,54 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! XIR [`Token`] stream iterators.
//!
//! These iterators are provided for convenience in constructing token
//! streams.
//!
//! - [`elem_wrap`] wraps a token stream iterator as the body of an
//! element of the given name.
use super::Token;
use super::{QName, Token};
use crate::span::Span;
use std::iter::{once, Chain, Once};
/// Wrap an inner [`Token`] stream iterator in an element.
///
/// This produces a [`Token::Open`] before the `inner` iterator and a
/// [`Token::Close`] after `inner` completes.
/// The provided two-[`Span`] is associated,
/// respectively,
/// with the opening and closing tags.
///
/// The inner iterator will be in the proper context to produce attributes.
#[inline]
pub fn elem_wrap<S, I>(name: QName, span: S, inner: I) -> ElemWrapIter<I>
where
S: Into<(Span, Span)>,
I: Iterator<Item = Token>,
{
let twospan: (Span, Span) = span.into();
ElemWrapIter::new(
Token::Open(name, twospan.0),
inner,
Token::Close(Some(name), twospan.1),
)
}
/// An iterator that wraps a [`Token`] iterator in an element.
///
/// This introduces an opening and closing token before and after the
/// iterator.
///
/// See [`elem_wrap`] to construct this iterator.
pub struct ElemWrapIter<I: Iterator<Item = Token>>(
Chain<Chain<Once<Token>, I>, Once<Token>>,
);
impl<I: Iterator<Item = Token>> ElemWrapIter<I> {
#[inline]
pub fn new(open: Token, inner: I, close: Token) -> Self {
fn new(open: Token, inner: I, close: Token) -> Self {
Self(once(open).chain(inner).chain(once(close)))
}
}
@ -45,3 +78,35 @@ impl<I: Iterator<Item = Token>> Iterator for ElemWrapIter<I> {
self.0.next()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{convert::ExpectInto, ir::xir::Token, span::DUMMY_SPAN};
#[test]
fn elem_wrap_iter() {
let inner = vec![
Token::Open("foo".unwrap_into(), DUMMY_SPAN),
Token::Close(None, DUMMY_SPAN),
];
let elem_name = "element".unwrap_into();
let twospan = (
DUMMY_SPAN.offset_add(1).unwrap(),
DUMMY_SPAN.offset_add(2).unwrap(),
);
let result = elem_wrap(elem_name, twospan, inner.clone().into_iter());
assert_eq!(
result.collect::<Vec<_>>(),
vec![
Token::Open(elem_name, twospan.0),
inner[0].clone(),
inner[1].clone(),
Token::Close(Some(elem_name), twospan.1),
]
);
}
}

View File

@ -24,7 +24,10 @@ use crate::{
asg::{
IdentKind, IdentObject, IdentObjectData, Sections, SectionsIter,
},
xir::{iter::ElemWrapIter, AttrValue, QName, Text, Token},
xir::{
iter::{elem_wrap, ElemWrapIter},
AttrValue, QName, Text, Token,
},
},
ld::LSPAN,
sym::{st::*, SymbolId},
@ -376,40 +379,36 @@ pub fn lower_iter<'a, T: IdentObjectData>(
pkg_name: SymbolId,
relroot: SymbolId,
) -> LowerIter<'a, T> {
LowerIter(ElemWrapIter::new(
Token::Open(QN_PACKAGE, LSPAN),
LowerIter(elem_wrap(
QN_PACKAGE,
LSPAN,
header(pkg_name, relroot)
.chain(ElemWrapIter::new(
Token::Open(QN_L_DEP, LSPAN),
.chain(elem_wrap(
QN_L_DEP,
LSPAN,
DepListIter::new(sections, relroot),
Token::Close(Some(QN_L_DEP), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_MAP_FROM, LSPAN),
MapFromsIter::new(sections),
Token::Close(Some(QN_L_MAP_FROM), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_MAP_EXEC, LSPAN),
.chain(elem_wrap(QN_L_MAP_FROM, LSPAN, MapFromsIter::new(sections)))
.chain(elem_wrap(
QN_L_MAP_EXEC,
LSPAN,
FragmentIter::new(sections.iter_map()),
Token::Close(Some(QN_L_MAP_EXEC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_RETMAP_EXEC, LSPAN),
.chain(elem_wrap(
QN_L_RETMAP_EXEC,
LSPAN,
FragmentIter::new(sections.iter_retmap()),
Token::Close(Some(QN_L_RETMAP_EXEC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_STATIC, LSPAN),
.chain(elem_wrap(
QN_L_STATIC,
LSPAN,
FragmentIter::new(sections.iter_static()),
Token::Close(Some(QN_L_STATIC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_EXEC, LSPAN),
.chain(elem_wrap(
QN_L_EXEC,
LSPAN,
FragmentIter::new(sections.iter_exec()),
Token::Close(Some(QN_L_EXEC), LSPAN),
)),
Token::Close(Some(QN_PACKAGE), LSPAN),
))
}