use std::cmp::Ordering;
use std::rc::Rc;
use super::ordering::reduction;
use super::program::compare_precedence;
use super::results::{Tree, TreeType};
use super::walk::{Walk, first_meaningful, items, leftmost_chain, meaningful, same_tree};
use crate::grammar::PrecedenceEntry;
pub(super) fn first_reduction(
a: &Rc<Tree>,
b: &Rc<Tree>,
orders: &[Vec<PrecedenceEntry>],
) -> Ordering {
fn steps(tree: &Rc<Tree>, out: &mut Vec<Rc<Tree>>) {
if tree.trivia {
return;
}
if tree.ty == TreeType::Node {
for child in items(&tree.children) {
steps(&child, out);
}
}
out.push(tree.clone());
}
let (mut first, mut second) = (Vec::new(), Vec::new());
steps(a, &mut first);
steps(b, &mut second);
let silent = |leaf: &Tree| {
let mut names: Vec<String> = leaf
.reduced_to
.iter()
.chain(&leaf.forked_to)
.map(ToString::to_string)
.collect();
names.push(leaf.alone.to_string());
names.join(" ")
};
let mut at = 0;
while let (Some(x), Some(y)) = (first.get(at), second.get(at)) {
if x.ty != y.ty || x.kind != y.kind || x.start != y.start || x.end != y.end {
break;
}
if x.ty != TreeType::Node && silent(x) != silent(y) {
break;
}
at += 1;
}
let (Some(x), Some(y)) = (first.get(at), second.get(at)) else {
return Ordering::Equal;
};
if x.ty != TreeType::Node
|| y.ty != TreeType::Node
|| x.end != y.end
|| at == 0
|| (Rc::ptr_eq(x, a) && Rc::ptr_eq(y, b))
{
return Ordering::Equal;
}
compare_precedence(&reduction(x), &reduction(y), orders)
}
pub(super) fn leading_dynamic(a: &Rc<Tree>, b: &Rc<Tree>) -> Ordering {
fn shares(node: &Tree, out: &mut Vec<(usize, i64)>) -> i64 {
let mut inner = 0;
for child in items(&node.children) {
if child.ty == TreeType::Node {
inner += shares(&child, out);
}
}
if node.dynamic != inner {
out.push((node.end, node.dynamic - inner));
}
node.dynamic
}
fn leaves(node: &Tree, starts: &mut Vec<usize>) {
for child in items(&node.children) {
if child.trivia {
continue;
}
if child.ty == TreeType::Node {
leaves(&child, starts);
} else {
starts.push(child.start);
}
}
}
let (mut first, mut second) = (Vec::new(), Vec::new());
shares(a, &mut first);
shares(b, &mut second);
if first.is_empty() && second.is_empty() {
return Ordering::Equal;
}
let mut starts = Vec::new();
leaves(a, &mut starts);
let sum = |list: &[(usize, i64)], at: usize| -> i64 {
list.iter()
.filter(|(end, _)| *end <= at)
.map(|(_, share)| share)
.sum()
};
let mut order = Ordering::Equal;
for at in starts {
let difference = sum(&first, at).cmp(&sum(&second, at));
if difference != Ordering::Equal {
order = difference;
}
}
order
}
pub(super) fn ending_reduction(
a: &Rc<Tree>,
b: &Rc<Tree>,
walk: &[Walk],
orders: &[Vec<PrecedenceEntry>],
) -> Ordering {
if a.ty != TreeType::Token || b.ty != TreeType::Node {
return Ordering::Equal;
}
let head = leftmost_chain(b)
.last()
.and_then(|last| first_meaningful(&last.children))
.unwrap_or_else(|| b.clone());
if !same_tree(&head, a) {
return Ordering::Equal;
}
let sibling_node = |item: &Rc<Tree>| item.ty == TreeType::Node && !item.trivia;
let mut sibling = None;
for step in walk.iter().rev().skip(1) {
match step {
Walk::Item(item) if sibling_node(item) => sibling = Some(item.clone()),
Walk::Item(_) => continue,
Walk::Part(part) => sibling = items(part).find(sibling_node),
Walk::End(_) => {}
}
if sibling.is_some() || matches!(step, Walk::End(_)) {
break;
}
}
let Some(sibling) = sibling.filter(|sibling| sibling.end == b.end) else {
return Ordering::Equal;
};
let rightmost = |node: &Rc<Tree>| {
let mut chain = Vec::new();
let mut current = Some(node.clone());
while let Some(next) = current.filter(|next| next.ty == TreeType::Node) {
current = meaningful(&next.children).pop();
chain.push(next);
}
chain
};
let (mine, theirs) = (rightmost(&sibling), rightmost(b));
let (mut i, mut j) = (mine.len(), theirs.len());
while i > 0 && j > 0 && same_tree(&mine[i - 1], &theirs[j - 1]) {
i -= 1;
j -= 1;
}
if i == 0 || j == 0 {
return Ordering::Equal;
}
compare_precedence(&reduction(&mine[i - 1]), &reduction(&theirs[j - 1]), orders)
}