tamer: xir::tree::attr_parser_from: Do not take ownership over iter

The previous implementation took ownership over the provided iterator, which
was an oversight, considering that this is intended to be used in contexts
where doing so is not possible.  A good example where isolated test cases
aren't necessarily painting the correct picture.

`scan` takes owned values, so this instead uses the same parsing method as
`parse_attrs`, but using a `FromFn` iterator to avoid having to create a
whole new iterator type.  This will work well so long as we don't need to
store the type returned by this (while also wanting to avoid boxing).

DEV-11062
main
Mike Gerwitz 2021-11-05 10:54:05 -04:00
parent 428d508be4
commit 1f01833d30
2 changed files with 39 additions and 21 deletions

View File

@ -196,7 +196,7 @@
use super::{AttrValue, QName, Text, Token, TokenResultStream, TokenStream};
use crate::span::Span;
use std::{fmt::Display, mem::take};
use std::{fmt::Display, iter, mem::take};
mod attr;
pub use attr::{Attr, AttrList, AttrParts, SimpleAttr};
@ -1099,27 +1099,45 @@ pub fn parse_attrs<'a>(
/// To parse an entire stream as a tree,
/// see [`parser_from`].
///
/// This parser does not take ownership over the iterator,
/// allowing parsing to continue on the underlying token stream after
/// attribute parsing has completed.
/// Once attribute parsing is finished,
/// parsing is able to continue on the underlying token stream as if the
/// attributes were never present in XIR at all;
/// this also allows this parser to be used as an attribute filter while
/// ensuring that the attributes are syntactically valid.
///
/// For more information on contexts,
/// and the parser in general,
/// see the [module-level documentation](self).
pub fn attr_parser_from(
toks: impl TokenStream,
) -> impl Iterator<Item = Result<Attr>> {
toks.scan(ParserState::with(Stack::IsolatedAttrEmpty), parse)
.filter_map(|parsed| match parsed {
Ok(Parsed::Attr(attr)) => Some(Ok(attr)),
Ok(Parsed::Incomplete) => None,
Err(err) => Some(Err(err)),
#[inline]
pub fn attr_parser_from<'a>(
toks: &'a mut impl TokenStream,
) -> impl Iterator<Item = Result<Attr>> + 'a {
let mut state = ParserState::with(Stack::IsolatedAttrEmpty);
// AttrEnd must have been encountered.
Ok(Parsed::Done) => None,
iter::from_fn(move || {
loop {
match toks.next().and_then(|tok| parse(&mut state, tok)) {
None => return None,
Some(Err(err)) => return Some(Err(err)),
Some(Ok(Parsed::Attr(attr))) => return Some(Ok(attr)),
Some(Ok(Parsed::Incomplete)) => continue,
// These make no sense in this context and should never occur.
Ok(x @ (Parsed::Tree(_) | Parsed::AttrList(_))) => unreachable!(
"unexpected yield by XIRT (Attr expected): {:?}",
x
),
})
// AttrEnd must have been encountered.
Some(Ok(Parsed::Done)) => return None,
// These make no sense in this context and should never occur.
Some(Ok(x @ (Parsed::Tree(_) | Parsed::AttrList(_)))) => {
unreachable!(
"unexpected yield by XIRT (Attr expected): {:?}",
x
)
}
}
}
})
}
#[cfg(test)]

View File

@ -462,9 +462,9 @@ fn parse_attrs_isolated() {
#[test]
fn attr_parser_with_non_attr_token() {
let name = "unexpected".unwrap_into();
let toks = [Token::Open(name, *S)].into_iter();
let mut toks = [Token::Open(name, *S)].into_iter();
let mut sut = attr_parser_from(toks);
let mut sut = attr_parser_from(&mut toks);
assert_eq!(
sut.next(),
@ -479,7 +479,7 @@ fn parser_attr_multiple() {
let val1 = AttrValue::Escaped("val1".into());
let val2 = AttrValue::Escaped("val2".into());
let toks = [
let mut toks = [
Token::AttrName(attr1, *S),
Token::AttrValue(val1, *S2),
Token::AttrName(attr2, *S2),
@ -488,7 +488,7 @@ fn parser_attr_multiple() {
]
.into_iter();
let mut sut = attr_parser_from(toks);
let mut sut = attr_parser_from(&mut toks);
assert_eq!(sut.next(), Some(Ok(Attr::new(attr1, val1, (*S, *S2)))));
assert_eq!(sut.next(), Some(Ok(Attr::new(attr2, val2, (*S2, *S3)))));