diff --git a/src/token/mod.rs b/src/token/mod.rs index 440ac22..818b8b8 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -613,6 +613,50 @@ impl<'t, A> Token<'t, A> { .map_invariant(|invariant| T::once() + invariant) } + pub fn invariant_text_prefix(&self) -> (usize, String) { + #[derive(Clone, Debug)] + struct Prefix { + index: usize, + text: String, + } + + let mut head = Prefix { + index: 0, + text: String::new(), + }; + let mut checkpoint = None; + let tokens = self.conjunction().iter().peekable(); + // TODO: The more correct predicate is, "Does this token have a root and variant text?" + // However, `has_root` is not quite correct, so rooted tree wildcards are detected + // explicitly instead. See `has_root`. + if tokens.peek().map_or(false, |token| { + matches!(token.as_wildcard(), Wildcard::Tree { has_root: true, .. }) + }) { + head.text.push_str(Separator::INVARIANT_TEXT); + } + for (n, token) in tokens.enumerate() { + match token.variance::() { + Variance::Invariant(text) => { + head.index = n; + head.text.push_str(text.to_string().as_ref()); + if let Some(Boundary::Separator) = token.boundary() { + checkpoint = Some(head.clone()); + } + }, + _ => { + return match checkpoint { + Some(checkpoint) => (checkpoint.index + 1, checkpoint.text), + None => (0, String::new()), + }; + }, + } + } + (head.index + 1, head.text) + } + + // TODO: Is root an invariant? This query is a bit odd. It returns `true` for alternations and + // repetitions (even with a lower bound of zero). Either way, this should probably return + // `When`, not `bool`. pub fn has_root(&self) -> bool { self.walk().starting().any(|(_, token)| { token.as_leaf().map_or(false, |leaf| { diff --git a/src/token/variance/mod.rs b/src/token/variance/mod.rs index 56fd95e..369d222 100644 --- a/src/token/variance/mod.rs +++ b/src/token/variance/mod.rs @@ -260,74 +260,6 @@ where } } -// TODO: Is there some way to unify this with `invariant_text_prefix_upper_bound`? -pub fn invariant_text_prefix<'t, A, I>(tokens: I) -> String -where - A: 't, - I: IntoIterator>, -{ - let separator = &Separator::invariant_text(); - let mut tokens = tokens.into_iter().peekable(); - let mut prefix = String::new(); - if tokens - .peek() - .map_or(false, |token| !token.has_sub_tokens() && token.has_root()) - { - // Push a preceding separator if the first token has a root and is not a group. This - // ensures that initiating separators and tree wildcards express a root in invariant - // prefixes. - prefix.push_str(separator); - } - // TODO: Replace `map`, `take_while`, and `flatten` with `map_while` when it stabilizes. - prefix.push_str( - &token::components(tokens) - .map(|component| { - component - .variance::() - .as_invariant() - .map(Text::to_string) - .map(Cow::into_owned) - }) - .take_while(Option::is_some) - .flatten() - .join(separator), - ); - prefix -} - -pub fn invariant_text_prefix_upper_bound<'t, A, I>(tokens: I) -> usize -where - A: 't, - I: IntoIterator>, -{ - use crate::token::TokenKind::{Separator, Wildcard}; - use crate::token::Wildcard::Tree; - - let mut m = 0usize; - let mut separator = None; - for (n, token) in tokens.into_iter().map(Token::kind).enumerate() { - m = n; - match token { - Separator(_) => { - separator = Some(n); - }, - Wildcard(Tree { .. }) => { - return n; - }, - _ => { - if token.variance::().is_invariant() { - continue; - } - return match separator { - Some(n) => n + 1, - None => 0, - }; - }, - } - } - m + 1 -} - // TODO: Replace and remove this. /// Returns `true` if the token tree is exhaustive. ///