use std::cmp::Ordering;
use super::program::TokenRank;
use super::results::{TokenOrder, Tree, TreeType};
pub(super) fn token_rank(leaf: &Tree, tokens: TokenOrder<'_>) -> Option<TokenRank> {
if leaf.ty != TreeType::Token {
return None;
}
let rank = leaf.rank.or_else(|| {
leaf.kind.as_ref().map_or_else(
|| {
tokens
.bytes
.get(leaf.start..leaf.end)
.and_then(|text| tokens.ranks.literals.get(text))
.copied()
},
|kind| tokens.ranks.kinds.get(kind).copied(),
)
});
let Some(priority) = leaf.priority else {
return rank;
};
Some(TokenRank {
priority,
..rank.unwrap_or(TokenRank {
priority,
specificity: 0,
order: usize::MAX,
})
})
}
pub(super) fn token_conflict(a: &Tree, b: &Tree, tokens: TokenOrder<'_>) -> Ordering {
let ranks = if a.start == b.start {
token_rank(a, tokens).zip(token_rank(b, tokens))
} else {
None
};
if let Some((first, second)) = ranks
&& first.priority != second.priority
{
return first.priority.cmp(&second.priority);
}
if a.end != b.end && a.start == b.start && !(a.lexed.is_some() && a.lexed == b.lexed) {
let (shorter, longer, sign) = if a.end < b.end {
(a, b, Ordering::Greater)
} else {
(b, a, Ordering::Less)
};
if tokens.separator_text(shorter.start, shorter.end)
&& !tokens.separator_text(longer.start, longer.end)
{
return sign;
}
}
if a.end != b.end {
return a.end.cmp(&b.end);
}
match ranks {
None => Ordering::Equal,
Some((first, second)) if first == second => Ordering::Equal,
Some((first, second)) if first.specificity != second.specificity => {
first.specificity.cmp(&second.specificity)
}
Some((first, second)) => second.order.cmp(&first.order),
}
}
pub(super) fn starts_at_separator(leaf: &Tree, cover: &Tree, tokens: TokenOrder<'_>) -> bool {
if leaf.ty != TreeType::Token || cover.ty != TreeType::Token {
return false;
}
if tokens.separator_text(cover.start, cover.end) {
return false;
}
token_rank(leaf, tokens)
.zip(token_rank(cover, tokens))
.is_some_and(|(mine, theirs)| mine.priority > theirs.priority)
}
pub(super) fn same_literal(a: &Tree, b: &Tree, tokens: TokenOrder<'_>) -> bool {
if a.start != b.start || a.end != b.end || a.ty != TreeType::Token || b.ty != TreeType::Token {
return false;
}
token_rank(a, tokens)
.zip(token_rank(b, tokens))
.is_some_and(|(first, second)| {
first.specificity >= 2
&& first.priority == second.priority
&& first.specificity == second.specificity
})
}
pub(super) fn other_ranks(a: &Tree, b: &Tree) -> bool {
a.rank.zip(b.rank).is_some_and(|(first, second)| {
first.priority != second.priority || first.specificity != second.specificity
})
}