use std::cmp::Ordering;
use std::rc::Rc;
use super::forking::{
GrammarFacts, Lead, declared_fork, forked_leaves, forked_order, items_order, lookahead_of,
shift_reduction, token_at,
};
use super::parting::{
chain_conflict, child_parting, extra_reduction, has_node, holds_first, one_token, parting_end,
reduced_first, same_tokens, silent_parting,
};
use super::program::{Associativity, PrecedenceTag, Settling, SettlingStep, compare_precedence};
use super::reducing::{ending_reduction, first_reduction, leading_dynamic};
use super::results::{Children, Res, TokenOrder, Tree, TreeType, join_children};
use super::token_conflicts::{other_ranks, same_literal, starts_at_separator, token_conflict};
use super::walk::{
TreeMemo, Walk, first_leaf_start, first_meaningful, items, leftmost_chain, meaningful,
next_item, open_node, open_part, same_tree, same_tree_in,
};
use crate::grammar::PrecedenceEntry;
pub(super) fn preferred_tokens(
result: &Children,
existing: &Children,
tokens: TokenOrder<'_>,
) -> Ordering {
let mut left = vec![Walk::Part(result.clone())];
let mut right = vec![Walk::Part(existing.clone())];
let mut skipped: [Vec<Rc<Tree>>; 2] = [Vec::new(), Vec::new()];
let mut pending = usize::MAX;
let mut decided = usize::MAX;
let lookahead = |tree: &Rc<Tree>, pending: usize, decided: &mut usize| {
if *decided == usize::MAX && !tree.trivia {
let start = first_leaf_start(tree);
if start >= pending {
*decided = start;
}
}
};
let covers = |leaf: &Tree, trivia: &[Rc<Tree>]| {
trivia.iter().any(|item| {
item.start == leaf.start
&& if item.kind.is_none() || item.kind == leaf.kind {
leaf.end >= item.end
} else {
item.ty == TreeType::Token
&& token_conflict(leaf, item, tokens) == Ordering::Greater
}
})
};
loop {
match (left.last(), right.last()) {
(None, None) => return Ordering::Equal,
(None, _) | (_, None) => {
let ended = left.is_empty();
let rest = if ended { &mut right } else { &mut left };
if next_item(rest).is_some_and(|first| {
first.ty == TreeType::Token
&& !first.trivia
&& covers(&first, &skipped[usize::from(!ended)])
}) {
return if ended {
Ordering::Less
} else {
Ordering::Greater
};
}
return scanned_after_end(result, existing, rest, decided, ended);
}
(Some(Walk::End(_)), _) => {
left.pop();
}
(_, Some(Walk::End(_))) => {
right.pop();
}
(Some(Walk::Part(a)), Some(Walk::Part(b)))
if Rc::ptr_eq(a, b) && (pending == usize::MAX || decided != usize::MAX) =>
{
if a.count > 0 {
skipped = [Vec::new(), Vec::new()];
}
left.pop();
right.pop();
}
(Some(Walk::Part(_)), _) => open_part(&mut left),
(_, Some(Walk::Part(_))) => open_part(&mut right),
(Some(Walk::Item(a)), Some(Walk::Item(b))) => {
let (a, b) = (a.clone(), b.clone());
let (a_node, b_node) = (a.ty == TreeType::Node, b.ty == TreeType::Node);
if Rc::ptr_eq(&a, &b) {
lookahead(&a, pending, &mut decided);
left.pop();
right.pop();
skipped = [Vec::new(), Vec::new()];
} else if a_node && b_node && (nests_first(&a, &b) || nests_first(&b, &a)) {
if nests_first(&a, &b) {
open_node(&mut left, false);
} else {
open_node(&mut right, false);
}
} else if a_node || b_node {
let (node, lone) = if a_node { (&a, &b) } else { (&b, &a) };
if lone.ty != TreeType::Node && !lone.trivia && reduced_first(node, lone) {
pending = pending.min(lone.end);
}
if a_node {
open_node(&mut left, false);
}
if b_node {
open_node(&mut right, false);
}
} else if a.trivia || b.trivia {
if a.trivia {
skipped[0].push(a);
left.pop();
}
if b.trivia {
skipped[1].push(b);
right.pop();
}
} else if a.start < b.start && covers(&a, &skipped[1]) {
return if starts_at_separator(&b, &a, tokens) {
Ordering::Less
} else {
Ordering::Greater
};
} else if b.start < a.start && covers(&b, &skipped[0]) {
return if starts_at_separator(&a, &b, tokens) {
Ordering::Greater
} else {
Ordering::Less
};
} else if a.end != b.end
|| (!one_token(&a, &b) && !same_literal(&a, &b, tokens))
|| other_ranks(&a, &b)
{
lookahead(&a, pending, &mut decided);
lookahead(&b, pending, &mut decided);
if a.start.min(b.start) > decided {
return Ordering::Equal;
}
return a
.scanned
.cmp(&b.scanned)
.then_with(|| token_conflict(&a, &b, tokens));
} else {
lookahead(&a, pending, &mut decided);
if a.alone != b.alone {
pending = pending.min(a.end);
}
left.pop();
right.pop();
skipped = [Vec::new(), Vec::new()];
}
}
}
}
}
fn scanned_after_end(
result: &Children,
existing: &Children,
rest: &mut Vec<Walk>,
decided: usize,
result_ended: bool,
) -> Ordering {
let (ended, other) = if result_ended {
(result, existing)
} else {
(existing, result)
};
if let Some(last) = meaningful(ended).pop()
&& (last.ty != TreeType::Node || !has_node(other, &last))
{
return Ordering::Equal;
}
while let Some(step) = rest.last() {
match step {
Walk::End(_) => {
rest.pop();
}
Walk::Part(_) => open_part(rest),
Walk::Item(item) if item.trivia => {
rest.pop();
}
Walk::Item(item) if item.ty == TreeType::Node => open_node(rest, false),
Walk::Item(item) => {
if item.start != item.end
|| !item.scanned
|| item.start > decided
|| item.start > parting_end(result, existing)
{
return Ordering::Equal;
}
return if result_ended {
Ordering::Less
} else {
Ordering::Greater
};
}
}
}
Ordering::Equal
}
pub(super) fn shift_order(
(result, existing): (&Children, &Children),
dynamic: Ordering,
orders: &[Vec<PrecedenceEntry>],
grammar: &GrammarFacts,
bytes: &[u8],
owner: Option<&PrecedenceTag>,
) -> Ordering {
let mut left = vec![Walk::Part(result.clone())];
let mut right = vec![Walk::Part(existing.clone())];
loop {
match (left.last(), right.last()) {
(None, _) | (_, None) => return Ordering::Equal,
(Some(Walk::End(_)), _) => {
left.pop();
}
(_, Some(Walk::End(_))) => {
right.pop();
}
(Some(Walk::Part(a)), Some(Walk::Part(b))) if Rc::ptr_eq(a, b) => {
left.pop();
right.pop();
}
(Some(Walk::Part(_)), _) => open_part(&mut left),
(_, Some(Walk::Part(_))) => open_part(&mut right),
(Some(Walk::Item(a)), _) if a.trivia => {
left.pop();
}
(_, Some(Walk::Item(b))) if b.trivia => {
right.pop();
}
(Some(Walk::Item(a)), Some(Walk::Item(b))) => {
let (a, b) = (a.clone(), b.clone());
let (a_node, b_node) = (a.ty == TreeType::Node, b.ty == TreeType::Node);
if Rc::ptr_eq(&a, &b)
|| (!a_node && !b_node && a.start == b.start && a.end == b.end)
{
if !Rc::ptr_eq(&a, &b) {
let parted = silent_parting(&a, &b, &right)
.then_with(|| silent_parting(&b, &a, &left).reverse());
if parted != Ordering::Equal {
return parted;
}
if same_parent(&left, &right) {
let forked = forked_leaves(&a, &b, grammar);
if forked != Ordering::Equal {
return dynamic.then(forked);
}
}
}
left.pop();
right.pop();
continue;
}
if a_node && b_node && a.start == b.start {
if a.end == b.end {
let reduced = first_reduction(&a, &b, orders);
if reduced != Ordering::Equal {
return reduced;
}
}
let split = chain_pair(&a, &b, true);
let whole = split.is_none()
&& (leftmost_chain(&b).iter().any(|node| same_tree(node, &a))
|| leftmost_chain(&a).iter().any(|node| same_tree(node, &b)));
let parting = split.filter(|(x, y)| x.end != y.end).or_else(|| {
if whole {
None
} else {
chain_pair(&a, &b, false)
}
});
if let Some((x, y)) = parting
&& x.end != y.end
{
let (x, y) = parted_pair(x, y);
let inner = child_parting(&x, &y, orders);
if inner != Ordering::Equal {
return inner;
}
let (long, short) = if x.end > y.end { (&x, &y) } else { (&y, &x) };
let lookahead = lookahead_of(token_at(long, short.end).as_ref(), bytes);
if declared_fork(grammar, short, lookahead.clone(), orders) {
return if x.end > y.end {
Ordering::Less
} else {
Ordering::Greater
};
}
return if x.end > y.end {
shift_preferred(&x, &y, orders, grammar, lookahead.as_ref())
} else {
shift_preferred(&y, &x, orders, grammar, lookahead.as_ref()).reverse()
};
}
}
if a_node
&& b_node
&& a.start == b.start
&& let Some((x, y)) = chain_pair(&a, &b, false)
{
let own = |node: &Tree, other: &Tree| {
node.reduced.clone().unwrap_or_else(|| {
PrecedenceTag::unranked(
node.reduced_to
.iter()
.find(|name| !other.reduced_to.contains(name))
.cloned(),
)
})
};
let order = compare_precedence(&own(&x, &y), &own(&y, &x), orders);
if order != Ordering::Equal {
return order;
}
}
if a_node
&& b_node
&& a.start == b.start
&& a.end == b.end
&& a.kind != b.kind
&& !holds_first(&a, &b)
&& !holds_first(&b, &a)
&& same_tokens(&a, &b)
{
let head = |node: &Rc<Tree>| {
leftmost_chain(node)
.last()
.and_then(|last| first_meaningful(&last.children))
};
if let (Some(x), Some(y)) = (head(&a), head(&b))
&& x.ty == TreeType::Token
&& y.ty == TreeType::Token
&& x.alone != y.alone
{
let lone = if x.alone {
lone_reduction(&b, &x, orders).reverse()
} else {
lone_reduction(&a, &y, orders)
};
if lone != Ordering::Equal {
return lone;
}
}
if let Some(forked) = forked_order(&a, &b, grammar, bytes) {
return dynamic.then_with(|| leading_dynamic(&a, &b)).then(forked);
}
let inside = shift_order(
(&a.children, &b.children),
dynamic,
orders,
grammar,
bytes,
owner,
);
if inside != Ordering::Equal {
return inside;
}
let (close_a, close_b) = (closing_reduction(&a), closing_reduction(&b));
let (own_a, own_b) = if PrecedenceTag::same(close_a.as_ref(), close_b.as_ref())
{
(None, None)
} else {
(close_a, close_b)
};
let order = compare_precedence(
&own_a.unwrap_or_else(|| reduction(&a)),
&own_b.unwrap_or_else(|| reduction(&b)),
orders,
);
if order != Ordering::Equal {
return order;
}
}
let ending = ending_reduction(&a, &b, &left, orders)
.then_with(|| ending_reduction(&b, &a, &right, orders).reverse());
if ending != Ordering::Equal {
return ending;
}
let lone = lone_reduction(&a, &b, orders)
.then_with(|| lone_reduction(&b, &a, orders).reverse());
if lone != Ordering::Equal {
return lone;
}
let reduced = extra_reduction(&a, &b, &right, orders, owner, grammar)
.then_with(|| extra_reduction(&b, &a, &left, orders, owner, grammar).reverse());
if reduced != Ordering::Equal {
return reduced;
}
if a_node && b_node && a.start == b.start {
let parted = chain_conflict(&a, &b, orders, grammar, bytes);
if parted != Ordering::Equal {
return parted;
}
}
if !a_node && !b_node {
return Ordering::Equal;
}
if a_node {
open_node(&mut left, true);
}
if b_node {
open_node(&mut right, true);
}
}
}
}
}
fn same_parent(left: &[Walk], right: &[Walk]) -> bool {
let parent = |walk: &[Walk]| {
walk.iter().rev().find_map(|step| match step {
Walk::End(node) => Some(node.clone()),
_ => None,
})
};
match (parent(left), parent(right)) {
(None, None) => true,
(Some(x), Some(y)) => Rc::ptr_eq(&x, &y) || (x.kind == y.kind && x.start == y.start),
_ => false,
}
}
fn closing_reduction(node: &Tree) -> Option<PrecedenceTag> {
let last = meaningful(&node.children).pop()?;
match last.ty {
TreeType::Token => last.reduced.clone().or_else(|| last.precedence.clone()),
TreeType::Node => last.closes.clone(),
_ => None,
}
}
pub(super) const fn by_associativity(associativity: Associativity) -> Ordering {
match associativity {
Associativity::Right => Ordering::Greater,
Associativity::Left => Ordering::Less,
Associativity::None => Ordering::Equal,
}
}
fn lone_reduction(a: &Rc<Tree>, b: &Rc<Tree>, orders: &[Vec<PrecedenceEntry>]) -> Ordering {
let closing = b.ty == TreeType::Node && b.closes.is_some();
if a.ty != TreeType::Node || (b.ty != TreeType::Token && !closing) {
return Ordering::Equal;
}
let mut progress = a.clone();
loop {
let Some(first) = first_meaningful(&progress.children) else {
return Ordering::Equal;
};
if same_tree(&first, b) {
break;
}
if first.ty != TreeType::Node {
return Ordering::Equal;
}
progress = first;
}
let shifted = progress
.precedence
.clone()
.unwrap_or_else(|| PrecedenceTag::unranked(progress.rule.clone()));
let reduced = if closing {
b.closes.clone()
} else {
b.reduced.clone().or_else(|| b.precedence.clone())
}
.unwrap_or(PrecedenceTag::unranked(None));
compare_precedence(&shifted, &reduced, orders)
.then_with(|| by_associativity(reduced.associativity))
}
fn chain_pair(a: &Rc<Tree>, b: &Rc<Tree>, distinct: bool) -> Option<(Rc<Tree>, Rc<Tree>)> {
let (mine, other) = (leftmost_chain(a), leftmost_chain(b));
let memo = TreeMemo::default();
let shared = |node: &Rc<Tree>, chain: &[Rc<Tree>]| {
distinct
&& chain
.iter()
.any(|peer| peer.end == node.end && same_tree_in(peer, node, &memo))
};
mine.iter()
.filter(|node| !shared(node, &other))
.find_map(|node| {
other
.iter()
.find(|candidate| {
candidate.kind == node.kind
&& candidate.start == node.start
&& !shared(candidate, &mine)
})
.map(|found| (node.clone(), found.clone()))
})
}
fn parted_pair(a: Rc<Tree>, b: Rc<Tree>) -> (Rc<Tree>, Rc<Tree>) {
let mut pair = (a, b);
loop {
let below = match (
first_meaningful(&pair.0.children),
first_meaningful(&pair.1.children),
) {
(Some(left), Some(right))
if left.ty == TreeType::Node && right.ty == TreeType::Node =>
{
chain_pair(&left, &right, false)
}
_ => None,
};
match below {
Some(below) if below.0.end != below.1.end => pair = below,
_ => return pair,
}
}
}
fn nests_first(outer: &Rc<Tree>, inner: &Rc<Tree>) -> bool {
outer.end > inner.end
&& leftmost_chain(outer).iter().any(|node| {
node.kind == inner.kind && node.start == inner.start && node.end == inner.end
})
}
pub(super) fn shift_preferred(
long: &Rc<Tree>,
short: &Rc<Tree>,
orders: &[Vec<PrecedenceEntry>],
grammar: &GrammarFacts,
lookahead: Option<&Lead>,
) -> Ordering {
let begin = first_leaf_start(short);
let mut progress = long.clone();
while let Some(inner) = items(&progress.children).find(|child| {
child.ty == TreeType::Node && child.start < short.end && child.end > short.end
}) {
if first_leaf_start(&inner) == begin {
return compare_precedence(&reduction(short), &PrecedenceTag::unranked(None), orders)
.reverse();
}
progress = inner;
}
let shifted = progress
.precedence
.clone()
.unwrap_or_else(|| PrecedenceTag::unranked(progress.rule.clone()));
let reduced = reduced_before(short, &progress);
if let Some(before) = shift_reduction(short, &progress, grammar) {
let order = compare_precedence(&PrecedenceTag::unranked(Some(before)), &reduced, orders);
if order != Ordering::Equal {
return order;
}
}
compare_precedence(&shifted, &reduced, orders)
.then_with(|| items_order(grammar, short, &reduced, lookahead, orders))
.then_with(|| by_associativity(reduced.associativity))
}
pub(super) fn reduction(node: &Tree) -> PrecedenceTag {
node.tail
.clone()
.or_else(|| node.precedence.clone())
.unwrap_or_else(|| PrecedenceTag::unranked(node.rule.clone()))
}
fn reduced_before(short: &Rc<Tree>, progress: &Tree) -> PrecedenceTag {
let mut closing = None;
if let Some(first) = first_meaningful(&progress.children) {
let mut node = short.clone();
while node.ty == TreeType::Node {
let Some(last) = node
.children
.iter()
.rev()
.find(|child| !child.trivia)
.cloned()
else {
break;
};
if same_tree(&last, &first) {
if last.ty == TreeType::Token
&& let Some(reduced) = &last.reduced
{
return reduced.clone();
}
if last.ty == TreeType::Token && last.alone && !first.alone {
return last
.precedence
.clone()
.unwrap_or(PrecedenceTag::unranked(None));
}
return reduction(&node);
}
node = last;
let closes = if node.ty == TreeType::Token {
node.reduced.as_ref().or(node.precedence.as_ref())
} else {
node.closes.as_ref()
};
if let Some(closes) = closes {
closing = Some(closes.clone());
}
}
}
closing.unwrap_or_else(|| reduction(short))
}
pub(super) fn settled_order(
a: (&Children, i64),
b: (&Children, i64),
settling: Settling,
tokens: Option<TokenOrder<'_>>,
owner: Option<&PrecedenceTag>,
) -> Ordering {
for step in settling.steps() {
let order = match step {
SettlingStep::Tokens => {
tokens.map_or(Ordering::Equal, |tokens| preferred_tokens(a.0, b.0, tokens))
}
SettlingStep::Precedence => tokens.map_or(Ordering::Equal, |tokens| {
shift_order(
(a.0, b.0),
a.1.cmp(&b.1),
tokens.orders,
tokens.grammar,
tokens.bytes,
owner,
)
}),
SettlingStep::Dynamic => a.1.cmp(&b.1),
SettlingStep::First | SettlingStep::Ambiguity => break,
};
if order != Ordering::Equal {
return order;
}
}
Ordering::Equal
}
pub(super) fn complete_order(
a: (&Res, &Children),
b: (&Res, &Children),
settling: Settling,
tokens: Option<TokenOrder<'_>>,
) -> Ordering {
if a.0.cost != b.0.cost {
return b.0.cost.cmp(&a.0.cost);
}
settled_order(
(&join_children(&a.0.children, a.1), a.0.dynamic),
(&join_children(&b.0.children, b.1), b.0.dynamic),
settling,
tokens,
None,
)
}