use std::collections::VecDeque;
use std::convert::TryFrom;
use std::fmt::Display;
use std::num::NonZeroU32;
use controlled_option::ControlledOption;
use controlled_option::Niche;
use smallvec::SmallVec;
use crate::arena::Deque;
use crate::arena::DequeArena;
use crate::arena::Handle;
use crate::cycles::CycleDetector;
use crate::graph::Edge;
use crate::graph::File;
use crate::graph::Node;
use crate::graph::NodeID;
use crate::graph::StackGraph;
use crate::graph::Symbol;
use crate::paths::Extend;
use crate::paths::Path;
use crate::paths::PathEdge;
use crate::paths::PathEdgeList;
use crate::paths::PathResolutionError;
use crate::paths::Paths;
use crate::paths::ScopeStack;
use crate::paths::ScopedSymbol;
use crate::paths::SymbolStack;
use crate::utils::cmp_option;
use crate::utils::equals_option;
trait DisplayWithPartialPaths {
fn prepare(&mut self, _graph: &StackGraph, _partials: &mut PartialPaths) {}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result;
}
fn display_with<'a, D>(
mut value: D,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a
where
D: DisplayWithPartialPaths + 'a,
{
value.prepare(graph, partials);
DisplayWithPartialPathsWrapper {
value,
graph,
partials,
}
}
fn display_prepared<'a, D>(
value: D,
graph: &'a StackGraph,
partials: &'a PartialPaths,
) -> impl Display + 'a
where
D: DisplayWithPartialPaths + 'a,
{
DisplayWithPartialPathsWrapper {
value,
graph,
partials,
}
}
#[doc(hidden)]
struct DisplayWithPartialPathsWrapper<'a, D> {
value: D,
graph: &'a StackGraph,
partials: &'a PartialPaths,
}
impl<'a, D> Display for DisplayWithPartialPathsWrapper<'a, D>
where
D: DisplayWithPartialPaths,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.value.display_with(self.graph, self.partials, f)
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, Niche, Ord, PartialEq, PartialOrd)]
pub struct SymbolStackVariable(#[niche] NonZeroU32);
impl SymbolStackVariable {
pub fn new(variable: u32) -> Option<SymbolStackVariable> {
NonZeroU32::new(variable).map(SymbolStackVariable)
}
fn initial() -> SymbolStackVariable {
SymbolStackVariable(unsafe { NonZeroU32::new_unchecked(1) })
}
pub fn with_offset(self, symbol_variable_offset: u32) -> SymbolStackVariable {
let offset_value = self.0.get() + symbol_variable_offset;
SymbolStackVariable(unsafe { NonZeroU32::new_unchecked(offset_value) })
}
fn as_u32(self) -> u32 {
self.0.get()
}
fn as_usize(self) -> usize {
self.0.get() as usize
}
}
impl Display for SymbolStackVariable {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "%{}", self.0.get())
}
}
impl Into<u32> for SymbolStackVariable {
fn into(self) -> u32 {
self.0.get()
}
}
impl TryFrom<u32> for SymbolStackVariable {
type Error = ();
fn try_from(value: u32) -> Result<SymbolStackVariable, ()> {
let value = NonZeroU32::new(value).ok_or(())?;
Ok(SymbolStackVariable(value))
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, Niche, Ord, PartialEq, PartialOrd)]
pub struct ScopeStackVariable(#[niche] NonZeroU32);
impl ScopeStackVariable {
pub fn new(variable: u32) -> Option<ScopeStackVariable> {
NonZeroU32::new(variable).map(ScopeStackVariable)
}
fn initial() -> ScopeStackVariable {
ScopeStackVariable(unsafe { NonZeroU32::new_unchecked(1) })
}
fn fresher_than(max_used: u32) -> ScopeStackVariable {
ScopeStackVariable(unsafe { NonZeroU32::new_unchecked(max_used + 1) })
}
pub fn with_offset(self, scope_variable_offset: u32) -> ScopeStackVariable {
let offset_value = self.0.get() + scope_variable_offset;
ScopeStackVariable(unsafe { NonZeroU32::new_unchecked(offset_value) })
}
fn as_u32(self) -> u32 {
self.0.get()
}
fn as_usize(self) -> usize {
self.0.get() as usize
}
}
impl Display for ScopeStackVariable {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "${}", self.0.get())
}
}
impl Into<u32> for ScopeStackVariable {
fn into(self) -> u32 {
self.0.get()
}
}
impl TryFrom<u32> for ScopeStackVariable {
type Error = ();
fn try_from(value: u32) -> Result<ScopeStackVariable, ()> {
let value = NonZeroU32::new(value).ok_or(())?;
Ok(ScopeStackVariable(value))
}
}
pub struct SymbolStackBindings {
bindings: SmallVec<[Option<SymbolStack>; 4]>,
}
impl SymbolStackBindings {
pub fn new() -> SymbolStackBindings {
SymbolStackBindings {
bindings: SmallVec::new(),
}
}
pub fn get(&self, variable: SymbolStackVariable) -> Result<SymbolStack, PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
return Err(PathResolutionError::UnboundSymbolStackVariable);
}
self.bindings[index - 1].ok_or(PathResolutionError::UnboundSymbolStackVariable)
}
pub fn add(
&mut self,
variable: SymbolStackVariable,
symbols: SymbolStack,
) -> Result<(), PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
self.bindings.resize_with(index, || None);
}
if self.bindings[index - 1].is_some() {
return Err(PathResolutionError::IncompatibleSymbolStackVariables);
}
self.bindings[index - 1] = Some(symbols);
Ok(())
}
}
pub struct ScopeStackBindings {
bindings: SmallVec<[Option<ScopeStack>; 4]>,
}
impl ScopeStackBindings {
pub fn new() -> ScopeStackBindings {
ScopeStackBindings {
bindings: SmallVec::new(),
}
}
pub fn get(&self, variable: ScopeStackVariable) -> Result<ScopeStack, PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
return Err(PathResolutionError::UnboundScopeStackVariable);
}
self.bindings[index - 1].ok_or(PathResolutionError::UnboundScopeStackVariable)
}
pub fn add(
&mut self,
variable: ScopeStackVariable,
scopes: ScopeStack,
) -> Result<(), PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
self.bindings.resize_with(index, || None);
}
if self.bindings[index - 1].is_some() {
return Err(PathResolutionError::IncompatibleScopeStackVariables);
}
self.bindings[index - 1] = Some(scopes);
Ok(())
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PartialScopedSymbol {
pub symbol: Handle<Symbol>,
pub scopes: ControlledOption<PartialScopeStack>,
}
impl PartialScopedSymbol {
pub fn with_offset(mut self, scope_variable_offset: u32) -> PartialScopedSymbol {
let scopes = self
.scopes
.into_option()
.map(|stack| stack.with_offset(scope_variable_offset));
self.scopes = ControlledOption::from_option(scopes);
self
}
pub fn match_symbol(
self,
graph: &StackGraph,
symbol: ScopedSymbol,
scope_bindings: &mut ScopeStackBindings,
) -> Result<(), PathResolutionError> {
if graph[self.symbol] != graph[symbol.symbol] {
return Err(PathResolutionError::SymbolStackUnsatisfied);
}
if !equals_option(
self.scopes.into_option(),
symbol.scopes.into_option(),
|pre, sym| pre.match_stack(sym, scope_bindings).is_ok(),
) {
return Err(PathResolutionError::SymbolStackUnsatisfied);
}
Ok(())
}
pub fn unify(
&mut self,
partials: &mut PartialPaths,
rhs: PartialScopedSymbol,
scope_bindings: &mut PartialScopeStackBindings,
) -> Result<(), PathResolutionError> {
if self.symbol != rhs.symbol {
return Err(PathResolutionError::SymbolStackUnsatisfied);
}
match (self.scopes.into_option(), rhs.scopes.into_option()) {
(Some(lhs), Some(rhs)) => {
let unified = lhs.unify(partials, rhs, scope_bindings)?;
self.scopes = ControlledOption::some(unified);
}
(None, None) => {}
_ => return Err(PathResolutionError::SymbolStackUnsatisfied),
}
Ok(())
}
pub fn matches(self, partials: &mut PartialPaths, postcondition: PartialScopedSymbol) -> bool {
if self.symbol != postcondition.symbol {
return false;
}
if self.scopes.is_none() != postcondition.scopes.is_none() {
return false;
}
if let Some(precondition_scopes) = self.scopes.into_option() {
if let Some(postcondition_scopes) = postcondition.scopes.into_option() {
return precondition_scopes.matches(partials, postcondition_scopes);
}
}
true
}
pub fn apply_bindings(
self,
paths: &mut Paths,
partials: &mut PartialPaths,
scope_bindings: &ScopeStackBindings,
) -> Result<ScopedSymbol, PathResolutionError> {
let scopes = match self.scopes.into_option() {
Some(scopes) => Some(scopes.apply_bindings(paths, partials, scope_bindings)?),
None => None,
};
Ok(ScopedSymbol {
symbol: self.symbol,
scopes: scopes.into(),
})
}
pub fn apply_partial_bindings(
mut self,
partials: &mut PartialPaths,
scope_bindings: &PartialScopeStackBindings,
) -> Result<PartialScopedSymbol, PathResolutionError> {
let scopes = match self.scopes.into_option() {
Some(scopes) => Some(scopes.apply_partial_bindings(partials, scope_bindings)?),
None => None,
};
self.scopes = scopes.into();
Ok(self)
}
pub fn equals(&self, partials: &mut PartialPaths, other: &PartialScopedSymbol) -> bool {
self.symbol == other.symbol
&& equals_option(
self.scopes.into_option(),
other.scopes.into_option(),
|a, b| a.equals(partials, b),
)
}
pub fn cmp(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
other: &PartialScopedSymbol,
) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
.then_with(|| graph[self.symbol].cmp(&graph[other.symbol]))
.then_with(|| {
cmp_option(
self.scopes.into_option(),
other.scopes.into_option(),
|a, b| a.cmp(partials, b),
)
})
}
pub fn display<'a>(
self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
}
impl DisplayWithPartialPaths for PartialScopedSymbol {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
if let Some(mut scopes) = self.scopes.into_option() {
scopes.prepare(graph, partials);
self.scopes = scopes.into();
}
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
if let Some(scopes) = self.scopes.into_option() {
write!(
f,
"{}/({})",
self.symbol.display(graph),
display_prepared(scopes, graph, partials)
)
} else {
write!(f, "{}", self.symbol.display(graph))
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Niche)]
pub struct PartialSymbolStack {
#[niche]
symbols: Deque<PartialScopedSymbol>,
length: u32,
variable: ControlledOption<SymbolStackVariable>,
}
impl PartialSymbolStack {
#[inline(always)]
pub fn can_match_empty(&self) -> bool {
self.symbols.is_empty()
}
#[inline(always)]
pub fn can_only_match_empty(&self) -> bool {
self.symbols.is_empty() && self.variable.is_none()
}
#[inline(always)]
pub fn contains_symbols(&self) -> bool {
!self.symbols.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length as usize
}
pub fn empty() -> PartialSymbolStack {
PartialSymbolStack {
symbols: Deque::empty(),
length: 0,
variable: ControlledOption::none(),
}
}
pub fn from_variable(variable: SymbolStackVariable) -> PartialSymbolStack {
PartialSymbolStack {
symbols: Deque::empty(),
length: 0,
variable: ControlledOption::some(variable),
}
}
pub fn have_reversal(&self, partials: &PartialPaths) -> bool {
self.symbols.have_reversal(&partials.partial_symbol_stacks)
}
pub fn with_offset(
mut self,
partials: &mut PartialPaths,
symbol_variable_offset: u32,
scope_variable_offset: u32,
) -> PartialSymbolStack {
let mut result = match self.variable.into_option() {
Some(variable) => Self::from_variable(variable.with_offset(symbol_variable_offset)),
None => Self::empty(),
};
while let Some(symbol) = self.pop_front(partials) {
result.push_back(partials, symbol.with_offset(scope_variable_offset));
}
result
}
fn prepend(&mut self, partials: &mut PartialPaths, mut head: Deque<PartialScopedSymbol>) {
while let Some(head) = head.pop_back(&mut partials.partial_symbol_stacks).copied() {
self.push_front(partials, head);
}
}
pub fn push_front(&mut self, partials: &mut PartialPaths, symbol: PartialScopedSymbol) {
self.length += 1;
self.symbols
.push_front(&mut partials.partial_symbol_stacks, symbol);
}
pub fn push_back(&mut self, partials: &mut PartialPaths, symbol: PartialScopedSymbol) {
self.length += 1;
self.symbols
.push_back(&mut partials.partial_symbol_stacks, symbol);
}
pub fn pop_front(&mut self, partials: &mut PartialPaths) -> Option<PartialScopedSymbol> {
let result = self
.symbols
.pop_front(&mut partials.partial_symbol_stacks)
.copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn pop_back(&mut self, partials: &mut PartialPaths) -> Option<PartialScopedSymbol> {
let result = self
.symbols
.pop_back(&mut partials.partial_symbol_stacks)
.copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn display<'a>(
self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
pub fn match_stack(
mut self,
graph: &StackGraph,
paths: &Paths,
partial_paths: &mut PartialPaths,
mut stack: SymbolStack,
symbol_bindings: &mut SymbolStackBindings,
scope_bindings: &mut ScopeStackBindings,
) -> Result<(), PathResolutionError> {
while let Some(precondition_symbol) = self.pop_front(partial_paths) {
match stack.pop_front(paths) {
Some(symbol) => precondition_symbol.match_symbol(graph, symbol, scope_bindings)?,
None => return Err(PathResolutionError::SymbolStackUnsatisfied),
}
}
match self.variable.into_option() {
Some(variable) => symbol_bindings.add(variable, stack),
None if !stack.is_empty() => Err(PathResolutionError::SymbolStackUnsatisfied),
_ => Ok(()),
}
}
pub fn matches(mut self, partials: &mut PartialPaths, mut other: PartialSymbolStack) -> bool {
while let Some(self_element) = self.pop_front(partials) {
if let Some(other_element) = other.pop_front(partials) {
if !self_element.matches(partials, other_element) {
return false;
}
} else {
return false;
}
}
if other.contains_symbols() {
return false;
}
self.variable.into_option() == other.variable.into_option()
}
pub fn apply_bindings(
mut self,
paths: &mut Paths,
partials: &mut PartialPaths,
symbol_bindings: &SymbolStackBindings,
scope_bindings: &ScopeStackBindings,
) -> Result<SymbolStack, PathResolutionError> {
let mut result = match self.variable.into_option() {
Some(variable) => symbol_bindings.get(variable)?,
None => SymbolStack::empty(),
};
while let Some(partial_symbol) = self.pop_back(partials) {
let symbol = partial_symbol.apply_bindings(paths, partials, scope_bindings)?;
result.push_front(paths, symbol);
}
Ok(result)
}
pub fn apply_partial_bindings(
mut self,
partials: &mut PartialPaths,
symbol_bindings: &PartialSymbolStackBindings,
scope_bindings: &PartialScopeStackBindings,
) -> Result<PartialSymbolStack, PathResolutionError> {
let mut result = match self.variable.into_option() {
Some(variable) => match symbol_bindings.get(variable) {
Some(bound) => bound,
None => PartialSymbolStack::from_variable(variable),
},
None => PartialSymbolStack::empty(),
};
while let Some(partial_symbol) = self.pop_back(partials) {
let partial_symbol = partial_symbol.apply_partial_bindings(partials, scope_bindings)?;
result.push_front(partials, partial_symbol);
}
Ok(result)
}
pub fn unify(
self,
partials: &mut PartialPaths,
mut rhs: PartialSymbolStack,
symbol_bindings: &mut PartialSymbolStackBindings,
scope_bindings: &mut PartialScopeStackBindings,
) -> Result<PartialSymbolStack, PathResolutionError> {
let mut lhs = self;
let mut head = Deque::empty();
while lhs.contains_symbols() && rhs.contains_symbols() {
let mut lhs_front = lhs.pop_front(partials).unwrap();
let rhs_front = rhs.pop_front(partials).unwrap();
lhs_front.unify(partials, rhs_front, scope_bindings)?;
head.push_back(&mut partials.partial_symbol_stacks, lhs_front);
}
if !lhs.contains_symbols() && !rhs.contains_symbols() {
match (lhs.variable.into_option(), rhs.variable.into_option()) {
(None, None) => {
lhs.prepend(partials, head);
return Ok(lhs);
}
(None, Some(var)) => {
symbol_bindings.add(
partials,
var,
PartialSymbolStack::empty(),
scope_bindings,
)?;
rhs.prepend(partials, head);
return Ok(rhs);
}
(Some(var), None) => {
symbol_bindings.add(
partials,
var,
PartialSymbolStack::empty(),
scope_bindings,
)?;
lhs.prepend(partials, head);
return Ok(lhs);
}
(Some(lhs_var), Some(rhs_var)) => {
symbol_bindings.add(
partials,
rhs_var,
PartialSymbolStack::from_variable(lhs_var),
scope_bindings,
)?;
lhs.prepend(partials, head);
return Ok(lhs);
}
}
}
if !lhs.contains_symbols() && lhs.variable.is_none() {
return Err(PathResolutionError::SymbolStackUnsatisfied);
}
if !rhs.contains_symbols() && rhs.variable.is_none() {
return Err(PathResolutionError::SymbolStackUnsatisfied);
}
if lhs.contains_symbols() {
let rhs_variable = rhs.variable.into_option().unwrap();
symbol_bindings.add(partials, rhs_variable, lhs, scope_bindings)?;
lhs.prepend(partials, head);
return Ok(lhs);
}
if rhs.contains_symbols() {
let lhs_variable = lhs.variable.into_option().unwrap();
symbol_bindings.add(partials, lhs_variable, rhs, scope_bindings)?;
rhs.prepend(partials, head);
return Ok(rhs);
}
unreachable!();
}
pub fn equals(mut self, partials: &mut PartialPaths, mut other: PartialSymbolStack) -> bool {
while let Some(self_symbol) = self.pop_front(partials) {
if let Some(other_symbol) = other.pop_front(partials) {
if !self_symbol.equals(partials, &other_symbol) {
return false;
}
} else {
return false;
}
}
if !other.symbols.is_empty() {
return false;
}
equals_option(
self.variable.into_option(),
other.variable.into_option(),
|a, b| a == b,
)
}
pub fn cmp(
mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
mut other: PartialSymbolStack,
) -> std::cmp::Ordering {
use std::cmp::Ordering;
while let Some(self_symbol) = self.pop_front(partials) {
if let Some(other_symbol) = other.pop_front(partials) {
match self_symbol.cmp(graph, partials, &other_symbol) {
Ordering::Equal => continue,
result @ _ => return result,
}
} else {
return Ordering::Greater;
}
}
if !other.symbols.is_empty() {
return Ordering::Less;
}
cmp_option(
self.variable.into_option(),
other.variable.into_option(),
|a, b| a.cmp(&b),
)
}
pub fn iter<'a>(
&self,
partials: &'a mut PartialPaths,
) -> impl Iterator<Item = PartialScopedSymbol> + 'a {
self.symbols
.iter(&mut partials.partial_symbol_stacks)
.copied()
}
pub fn iter_unordered<'a>(
&self,
partials: &'a PartialPaths,
) -> impl Iterator<Item = PartialScopedSymbol> + 'a {
self.symbols
.iter_unordered(&partials.partial_symbol_stacks)
.copied()
}
fn ensure_both_directions(&mut self, partials: &mut PartialPaths) {
self.symbols
.ensure_backwards(&mut partials.partial_symbol_stacks);
self.symbols
.ensure_forwards(&mut partials.partial_symbol_stacks);
}
}
impl DisplayWithPartialPaths for PartialSymbolStack {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
self.symbols
.ensure_forwards(&mut partials.partial_symbol_stacks);
let mut symbols = self.symbols;
while let Some(mut symbol) = symbols
.pop_front(&mut partials.partial_symbol_stacks)
.copied()
{
symbol.prepare(graph, partials);
}
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
for symbol in self.symbols.iter_reused(&partials.partial_symbol_stacks) {
symbol.display_with(graph, partials, f)?;
}
if let Some(variable) = self.variable.into_option() {
if self.symbols.is_empty() {
write!(f, "{}", variable)?;
} else {
write!(f, ",{}", variable)?;
}
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone, Copy, Niche)]
pub struct PartialScopeStack {
#[niche]
scopes: Deque<Handle<Node>>,
length: u32,
variable: ControlledOption<ScopeStackVariable>,
}
impl PartialScopeStack {
#[inline(always)]
pub fn can_match_empty(&self) -> bool {
self.scopes.is_empty()
}
#[inline(always)]
pub fn can_only_match_empty(&self) -> bool {
self.scopes.is_empty() && self.variable.is_none()
}
#[inline(always)]
pub fn contains_scopes(&self) -> bool {
!self.scopes.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length as usize
}
pub fn empty() -> PartialScopeStack {
PartialScopeStack {
scopes: Deque::empty(),
length: 0,
variable: ControlledOption::none(),
}
}
pub fn from_variable(variable: ScopeStackVariable) -> PartialScopeStack {
PartialScopeStack {
scopes: Deque::empty(),
length: 0,
variable: ControlledOption::some(variable),
}
}
pub fn have_reversal(&self, partials: &PartialPaths) -> bool {
self.scopes.have_reversal(&partials.partial_scope_stacks)
}
pub fn with_offset(mut self, scope_variable_offset: u32) -> PartialScopeStack {
match self.variable.into_option() {
Some(variable) => {
self.variable = ControlledOption::some(variable.with_offset(scope_variable_offset));
}
None => {}
};
self
}
pub fn match_stack(
&self,
stack: ScopeStack,
bindings: &mut ScopeStackBindings,
) -> Result<(), PathResolutionError> {
assert!(self.scopes.is_empty());
match self.variable.into_option() {
Some(variable) => bindings.add(variable, stack),
None if !stack.is_empty() => Err(PathResolutionError::ScopeStackUnsatisfied),
_ => Ok(()),
}
}
pub fn matches(mut self, partials: &mut PartialPaths, mut other: PartialScopeStack) -> bool {
while let Some(self_element) = self.pop_front(partials) {
if let Some(other_element) = other.pop_front(partials) {
if self_element != other_element {
return false;
}
} else {
return false;
}
}
if other.contains_scopes() {
return false;
}
self.variable.into_option() == other.variable.into_option()
}
pub fn apply_bindings(
mut self,
paths: &mut Paths,
partials: &mut PartialPaths,
bindings: &ScopeStackBindings,
) -> Result<ScopeStack, PathResolutionError> {
let mut result = match self.variable.into_option() {
Some(variable) => bindings.get(variable)?,
None => ScopeStack::empty(),
};
while let Some(scope) = self.pop_back(partials) {
result.push_front(paths, scope);
}
Ok(result)
}
pub fn apply_partial_bindings(
mut self,
partials: &mut PartialPaths,
scope_bindings: &PartialScopeStackBindings,
) -> Result<PartialScopeStack, PathResolutionError> {
let mut result = match self.variable.into_option() {
Some(variable) => match scope_bindings.get(variable) {
Some(bound) => bound,
None => PartialScopeStack::from_variable(variable),
},
None => PartialScopeStack::empty(),
};
while let Some(scope) = self.pop_back(partials) {
result.push_front(partials, scope);
}
Ok(result)
}
pub fn unify(
self,
partials: &mut PartialPaths,
mut rhs: PartialScopeStack,
bindings: &mut PartialScopeStackBindings,
) -> Result<PartialScopeStack, PathResolutionError> {
let mut lhs = self;
let original_rhs = rhs;
while lhs.contains_scopes() && rhs.contains_scopes() {
let lhs_front = lhs.pop_front(partials).unwrap();
let rhs_front = rhs.pop_front(partials).unwrap();
if lhs_front != rhs_front {
return Err(PathResolutionError::ScopeStackUnsatisfied);
}
}
if !lhs.contains_scopes() && !rhs.contains_scopes() {
match (lhs.variable.into_option(), rhs.variable.into_option()) {
(None, None) => return Ok(self),
(None, Some(var)) => {
bindings.add(partials, var, PartialScopeStack::empty())?;
return Ok(original_rhs);
}
(Some(var), None) => {
bindings.add(partials, var, PartialScopeStack::empty())?;
return Ok(self);
}
(Some(lhs_var), Some(rhs_var)) => {
bindings.add(partials, rhs_var, PartialScopeStack::from_variable(lhs_var))?;
return Ok(self);
}
}
}
if !lhs.contains_scopes() && lhs.variable.is_none() {
return Err(PathResolutionError::ScopeStackUnsatisfied);
}
if !rhs.contains_scopes() && rhs.variable.is_none() {
return Err(PathResolutionError::ScopeStackUnsatisfied);
}
if lhs.contains_scopes() {
let rhs_variable = rhs.variable.into_option().unwrap();
bindings.add(partials, rhs_variable, lhs)?;
return Ok(self);
}
if rhs.contains_scopes() {
let lhs_variable = lhs.variable.into_option().unwrap();
bindings.add(partials, lhs_variable, rhs)?;
return Ok(original_rhs);
}
unreachable!();
}
pub fn push_front(&mut self, partials: &mut PartialPaths, node: Handle<Node>) {
self.length += 1;
self.scopes
.push_front(&mut partials.partial_scope_stacks, node);
}
pub fn push_back(&mut self, partials: &mut PartialPaths, node: Handle<Node>) {
self.length += 1;
self.scopes
.push_back(&mut partials.partial_scope_stacks, node);
}
pub fn pop_front(&mut self, partials: &mut PartialPaths) -> Option<Handle<Node>> {
let result = self
.scopes
.pop_front(&mut partials.partial_scope_stacks)
.copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn pop_back(&mut self, partials: &mut PartialPaths) -> Option<Handle<Node>> {
let result = self
.scopes
.pop_back(&mut partials.partial_scope_stacks)
.copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn variable(&self) -> Option<ScopeStackVariable> {
self.variable.into_option()
}
pub fn equals(self, partials: &mut PartialPaths, other: PartialScopeStack) -> bool {
self.scopes
.equals_with(&mut partials.partial_scope_stacks, other.scopes, |a, b| {
*a == *b
})
&& equals_option(
self.variable.into_option(),
other.variable.into_option(),
|a, b| a == b,
)
}
pub fn cmp(self, partials: &mut PartialPaths, other: PartialScopeStack) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
.then_with(|| {
self.scopes
.cmp_with(&mut partials.partial_scope_stacks, other.scopes, |a, b| {
a.cmp(b)
})
})
.then_with(|| {
cmp_option(
self.variable.into_option(),
other.variable.into_option(),
|a, b| a.cmp(&b),
)
})
}
pub fn iter_scopes<'a>(
&self,
partials: &'a mut PartialPaths,
) -> impl Iterator<Item = Handle<Node>> + 'a {
self.scopes
.iter(&mut partials.partial_scope_stacks)
.copied()
}
pub fn iter_unordered<'a>(
&self,
partials: &'a PartialPaths,
) -> impl Iterator<Item = Handle<Node>> + 'a {
self.scopes
.iter_unordered(&partials.partial_scope_stacks)
.copied()
}
pub fn display<'a>(
self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
fn ensure_both_directions(&mut self, partials: &mut PartialPaths) {
self.scopes
.ensure_backwards(&mut partials.partial_scope_stacks);
self.scopes
.ensure_forwards(&mut partials.partial_scope_stacks);
}
}
impl DisplayWithPartialPaths for PartialScopeStack {
fn prepare(&mut self, _graph: &StackGraph, partials: &mut PartialPaths) {
self.scopes
.ensure_forwards(&mut partials.partial_scope_stacks);
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
let mut first = true;
for scope in self.scopes.iter_reused(&partials.partial_scope_stacks) {
if first {
first = false;
} else {
write!(f, ",")?;
}
write!(f, "{:#}", scope.display(graph))?;
}
if let Some(variable) = self.variable.into_option() {
if self.scopes.is_empty() {
write!(f, "{}", variable)?;
} else {
write!(f, ",{}", variable)?;
}
}
Ok(())
}
}
pub struct PartialSymbolStackBindings {
bindings: SmallVec<[Option<PartialSymbolStack>; 4]>,
}
impl PartialSymbolStackBindings {
pub fn new() -> PartialSymbolStackBindings {
PartialSymbolStackBindings {
bindings: SmallVec::new(),
}
}
pub fn get(&self, variable: SymbolStackVariable) -> Option<PartialSymbolStack> {
let index = variable.as_usize();
if self.bindings.len() < index {
return None;
}
self.bindings[index - 1]
}
pub fn add(
&mut self,
partials: &mut PartialPaths,
variable: SymbolStackVariable,
mut symbols: PartialSymbolStack,
scope_bindings: &mut PartialScopeStackBindings,
) -> Result<(), PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
self.bindings.resize_with(index, || None);
}
if let Some(old_binding) = self.bindings[index - 1] {
symbols = symbols.unify(partials, old_binding, self, scope_bindings)?;
}
self.bindings[index - 1] = Some(symbols);
Ok(())
}
pub fn display<'a>(
&'a mut self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
}
impl<'a> DisplayWithPartialPaths for &'a mut PartialSymbolStackBindings {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
for binding in &mut self.bindings {
if let Some(binding) = binding.as_mut() {
binding.prepare(graph, partials);
}
}
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
write!(f, "{{")?;
let mut first = true;
for (idx, binding) in self.bindings.iter().enumerate() {
if let Some(binding) = binding.as_ref() {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(
f,
"%{} => <{}>",
idx + 1,
display_prepared(*binding, graph, partials)
)?;
}
}
write!(f, "}}")
}
}
pub struct PartialScopeStackBindings {
bindings: SmallVec<[Option<PartialScopeStack>; 4]>,
}
impl PartialScopeStackBindings {
pub fn new() -> PartialScopeStackBindings {
PartialScopeStackBindings {
bindings: SmallVec::new(),
}
}
pub fn get(&self, variable: ScopeStackVariable) -> Option<PartialScopeStack> {
let index = variable.as_usize();
if self.bindings.len() < index {
return None;
}
self.bindings[index - 1]
}
pub fn add(
&mut self,
partials: &mut PartialPaths,
variable: ScopeStackVariable,
mut scopes: PartialScopeStack,
) -> Result<(), PathResolutionError> {
let index = variable.as_usize();
if self.bindings.len() < index {
self.bindings.resize_with(index, || None);
}
if let Some(old_binding) = self.bindings[index - 1] {
scopes = scopes.unify(partials, old_binding, self)?;
}
self.bindings[index - 1] = Some(scopes);
Ok(())
}
pub fn display<'a>(
&'a mut self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
}
impl<'a> DisplayWithPartialPaths for &'a mut PartialScopeStackBindings {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
for binding in &mut self.bindings {
if let Some(binding) = binding.as_mut() {
binding.prepare(graph, partials);
}
}
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
write!(f, "{{")?;
let mut first = true;
for (idx, binding) in self.bindings.iter().enumerate() {
if let Some(binding) = binding.as_ref() {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(
f,
"${} => ({})",
idx + 1,
display_prepared(*binding, graph, partials)
)?;
}
}
write!(f, "}}")
}
}
#[repr(C)]
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PartialPathEdge {
pub source_node_id: NodeID,
pub precedence: i32,
}
impl From<PartialPathEdge> for PathEdge {
fn from(other: PartialPathEdge) -> PathEdge {
PathEdge {
source_node_id: other.source_node_id,
precedence: other.precedence,
}
}
}
impl PartialPathEdge {
pub fn shadows(self, other: PartialPathEdge) -> bool {
self.source_node_id == other.source_node_id && self.precedence > other.precedence
}
pub fn display<'a>(
self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
}
impl DisplayWithPartialPaths for PartialPathEdge {
fn display_with(
&self,
graph: &StackGraph,
_partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
match graph.node_for_id(self.source_node_id) {
Some(node) => write!(f, "{:#}", node.display(graph))?,
None => write!(f, "[missing]")?,
}
if self.precedence != 0 {
write!(f, "({})", self.precedence)?;
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone, Copy, Niche)]
pub struct PartialPathEdgeList {
#[niche]
edges: Deque<PartialPathEdge>,
length: u32,
}
impl PartialPathEdgeList {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.edges.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length as usize
}
pub fn have_reversal(&self, partials: &PartialPaths) -> bool {
self.edges.have_reversal(&partials.partial_path_edges)
}
pub fn empty() -> PartialPathEdgeList {
PartialPathEdgeList {
edges: Deque::empty(),
length: 0,
}
}
pub fn push_front(&mut self, partials: &mut PartialPaths, edge: PartialPathEdge) {
self.length += 1;
self.edges
.push_front(&mut partials.partial_path_edges, edge);
}
pub fn push_back(&mut self, partials: &mut PartialPaths, edge: PartialPathEdge) {
self.length += 1;
self.edges.push_back(&mut partials.partial_path_edges, edge);
}
pub fn pop_front(&mut self, partials: &mut PartialPaths) -> Option<PartialPathEdge> {
let result = self.edges.pop_front(&mut partials.partial_path_edges);
if result.is_some() {
self.length -= 1;
}
result.copied()
}
pub fn pop_back(&mut self, partials: &mut PartialPaths) -> Option<PartialPathEdge> {
let result = self.edges.pop_back(&mut partials.partial_path_edges);
if result.is_some() {
self.length -= 1;
}
result.copied()
}
pub fn display<'a>(
self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
pub fn shadows(mut self, partials: &mut PartialPaths, mut other: PartialPathEdgeList) -> bool {
while let Some(self_edge) = self.pop_front(partials) {
if let Some(other_edge) = other.pop_front(partials) {
if self_edge.shadows(other_edge) {
return true;
}
} else {
return false;
}
}
false
}
pub fn equals(mut self, partials: &mut PartialPaths, mut other: PartialPathEdgeList) -> bool {
while let Some(self_edge) = self.pop_front(partials) {
if let Some(other_edge) = other.pop_front(partials) {
if self_edge != other_edge {
return false;
}
} else {
return false;
}
}
other.edges.is_empty()
}
pub fn cmp(
mut self,
partials: &mut PartialPaths,
mut other: PartialPathEdgeList,
) -> std::cmp::Ordering {
use std::cmp::Ordering;
while let Some(self_edge) = self.pop_front(partials) {
if let Some(other_edge) = other.pop_front(partials) {
match self_edge.cmp(&other_edge) {
Ordering::Equal => continue,
result @ _ => return result,
}
} else {
return Ordering::Greater;
}
}
if other.edges.is_empty() {
Ordering::Equal
} else {
Ordering::Less
}
}
pub fn iter<'a>(
&self,
partials: &'a mut PartialPaths,
) -> impl Iterator<Item = PartialPathEdge> + 'a {
self.edges.iter(&mut partials.partial_path_edges).copied()
}
pub fn iter_unordered<'a>(
&self,
partials: &'a PartialPaths,
) -> impl Iterator<Item = PartialPathEdge> + 'a {
self.edges
.iter_unordered(&partials.partial_path_edges)
.copied()
}
fn ensure_both_directions(&mut self, partials: &mut PartialPaths) {
self.edges
.ensure_backwards(&mut partials.partial_path_edges);
self.edges.ensure_forwards(&mut partials.partial_path_edges);
}
}
impl DisplayWithPartialPaths for PartialPathEdgeList {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
self.edges.ensure_forwards(&mut partials.partial_path_edges);
let mut edges = self.edges;
while let Some(mut edge) = edges.pop_front(&mut partials.partial_path_edges).copied() {
edge.prepare(graph, partials);
}
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
for edge in self.edges.iter_reused(&partials.partial_path_edges) {
edge.display_with(graph, partials, f)?;
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone)]
pub struct PartialPath {
pub start_node: Handle<Node>,
pub end_node: Handle<Node>,
pub symbol_stack_precondition: PartialSymbolStack,
pub symbol_stack_postcondition: PartialSymbolStack,
pub scope_stack_precondition: PartialScopeStack,
pub scope_stack_postcondition: PartialScopeStack,
pub edges: PartialPathEdgeList,
}
impl PartialPath {
pub fn from_node(
graph: &StackGraph,
partials: &mut PartialPaths,
node: Handle<Node>,
) -> Result<PartialPath, PathResolutionError> {
let initial_symbol_stack = SymbolStackVariable::initial();
let initial_scope_stack = ScopeStackVariable::initial();
let symbol_stack_precondition = PartialSymbolStack::from_variable(initial_symbol_stack);
let mut symbol_stack_postcondition =
PartialSymbolStack::from_variable(initial_symbol_stack);
let mut scope_stack_precondition = PartialScopeStack::from_variable(initial_scope_stack);
let mut scope_stack_postcondition = PartialScopeStack::from_variable(initial_scope_stack);
let inner_node = &graph[node];
if let Node::PushScopedSymbol(inner_node) = inner_node {
scope_stack_precondition = PartialScopeStack::empty();
scope_stack_postcondition = PartialScopeStack::empty();
let scope = graph
.node_for_id(inner_node.scope)
.ok_or(PathResolutionError::UnknownAttachedScope)?;
scope_stack_postcondition.push_front(partials, scope);
let initial_symbol = PartialScopedSymbol {
symbol: inner_node.symbol,
scopes: ControlledOption::some(scope_stack_postcondition),
};
symbol_stack_postcondition.push_front(partials, initial_symbol);
} else if let Node::PushSymbol(inner_node) = inner_node {
scope_stack_precondition = PartialScopeStack::empty();
scope_stack_postcondition = PartialScopeStack::empty();
let initial_symbol = PartialScopedSymbol {
symbol: inner_node.symbol,
scopes: ControlledOption::none(),
};
symbol_stack_postcondition.push_front(partials, initial_symbol);
}
Ok(PartialPath {
start_node: node,
end_node: node,
symbol_stack_precondition,
symbol_stack_postcondition,
scope_stack_precondition,
scope_stack_postcondition,
edges: PartialPathEdgeList::empty(),
})
}
pub fn shadows(&self, partials: &mut PartialPaths, other: &PartialPath) -> bool {
self.edges.shadows(partials, other.edges)
}
pub fn equals(&self, partials: &mut PartialPaths, other: &PartialPath) -> bool {
self.start_node == other.start_node
&& self.end_node == other.end_node
&& self
.symbol_stack_precondition
.equals(partials, other.symbol_stack_precondition)
&& self
.symbol_stack_postcondition
.equals(partials, other.symbol_stack_postcondition)
&& self
.scope_stack_precondition
.equals(partials, other.scope_stack_precondition)
&& self
.scope_stack_postcondition
.equals(partials, other.scope_stack_postcondition)
&& self.edges.equals(partials, other.edges)
}
pub fn cmp(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
other: &PartialPath,
) -> std::cmp::Ordering {
std::cmp::Ordering::Equal
.then_with(|| self.start_node.cmp(&other.start_node))
.then_with(|| self.end_node.cmp(&other.end_node))
.then_with(|| {
self.symbol_stack_precondition
.cmp(graph, partials, other.symbol_stack_precondition)
})
.then_with(|| {
self.symbol_stack_postcondition.cmp(
graph,
partials,
other.symbol_stack_postcondition,
)
})
.then_with(|| {
self.scope_stack_precondition
.cmp(partials, other.scope_stack_precondition)
})
.then_with(|| {
self.scope_stack_postcondition
.cmp(partials, other.scope_stack_postcondition)
})
.then_with(|| self.edges.cmp(partials, other.edges))
}
pub fn starts_at_reference(&self, graph: &StackGraph) -> bool {
graph[self.start_node].is_reference()
&& self.symbol_stack_precondition.can_match_empty()
&& self.scope_stack_precondition.can_match_empty()
}
pub fn ends_at_definition(&self, graph: &StackGraph) -> bool {
graph[self.end_node].is_definition()
&& self.symbol_stack_postcondition.can_match_empty()
&& self.scope_stack_postcondition.can_match_empty()
}
pub fn is_complete(&self, graph: &StackGraph) -> bool {
self.starts_at_reference(graph) && self.ends_at_definition(graph)
}
pub fn is_complete_as_possible(&self, graph: &StackGraph) -> bool {
match &graph[self.start_node] {
Node::Root(_) => (),
Node::ExportedScope(_) => (),
node @ Node::PushScopedSymbol(_) | node @ Node::PushSymbol(_) => {
if !node.is_reference() {
return false;
} else if !self.symbol_stack_precondition.can_match_empty() {
return false;
}
}
_ => return false,
}
match &graph[self.end_node] {
Node::Root(_) => (),
Node::JumpTo(_) => (),
node @ Node::PopScopedSymbol(_) | node @ Node::PopSymbol(_) => {
if !node.is_definition() {
return false;
} else if !self.symbol_stack_postcondition.can_match_empty() {
return false;
}
}
_ => return false,
}
true
}
pub fn is_productive(&self, partials: &mut PartialPaths) -> bool {
if self.start_node != self.end_node {
return true;
}
if !self
.symbol_stack_precondition
.matches(partials, self.symbol_stack_postcondition)
{
return true;
}
if !self
.scope_stack_precondition
.matches(partials, self.scope_stack_postcondition)
{
return true;
}
false
}
pub fn ensure_both_directions(&mut self, partials: &mut PartialPaths) {
self.symbol_stack_precondition
.ensure_both_directions(partials);
self.symbol_stack_postcondition
.ensure_both_directions(partials);
self.scope_stack_precondition
.ensure_both_directions(partials);
self.scope_stack_postcondition
.ensure_both_directions(partials);
self.edges.ensure_both_directions(partials);
let mut stack = self.symbol_stack_precondition;
while let Some(symbol) = stack.pop_front(partials) {
if let Some(mut scopes) = symbol.scopes.into_option() {
scopes.ensure_both_directions(partials);
}
}
let mut stack = self.symbol_stack_postcondition;
while let Some(symbol) = stack.pop_front(partials) {
if let Some(mut scopes) = symbol.scopes.into_option() {
scopes.ensure_both_directions(partials);
}
}
}
pub fn largest_symbol_stack_variable(&self) -> u32 {
self.symbol_stack_precondition
.variable
.into_option()
.map(SymbolStackVariable::as_u32)
.unwrap_or(0)
}
pub fn largest_scope_stack_variable(&self, partials: &PartialPaths) -> u32 {
let symbol_stack_precondition_variables = self
.symbol_stack_precondition
.iter_unordered(partials)
.filter_map(|symbol| symbol.scopes.into_option())
.filter_map(|scopes| scopes.variable.into_option())
.map(ScopeStackVariable::as_u32);
let scope_stack_precondition_variables = self
.scope_stack_precondition
.variable
.into_option()
.map(ScopeStackVariable::as_u32);
std::iter::empty()
.chain(symbol_stack_precondition_variables)
.chain(scope_stack_precondition_variables)
.max()
.unwrap_or(0)
}
pub fn fresh_scope_stack_variable(&self, partials: &PartialPaths) -> ScopeStackVariable {
ScopeStackVariable::fresher_than(self.largest_scope_stack_variable(partials))
}
pub fn display<'a>(
&'a self,
graph: &'a StackGraph,
partials: &'a mut PartialPaths,
) -> impl Display + 'a {
display_with(self, graph, partials)
}
}
impl<'a> DisplayWithPartialPaths for &'a PartialPath {
fn prepare(&mut self, graph: &StackGraph, partials: &mut PartialPaths) {
self.symbol_stack_precondition
.clone()
.prepare(graph, partials);
self.symbol_stack_postcondition
.clone()
.prepare(graph, partials);
self.scope_stack_precondition
.clone()
.prepare(graph, partials);
self.scope_stack_postcondition
.clone()
.prepare(graph, partials);
}
fn display_with(
&self,
graph: &StackGraph,
partials: &PartialPaths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
write!(
f,
"<{}> ({}) {} -> {} <{}> ({})",
display_prepared(self.symbol_stack_precondition, graph, partials),
display_prepared(self.scope_stack_precondition, graph, partials),
self.start_node.display(graph),
self.end_node.display(graph),
display_prepared(self.symbol_stack_postcondition, graph, partials),
display_prepared(self.scope_stack_postcondition, graph, partials),
)
}
}
impl PartialPath {
pub fn ensure_no_overlapping_variables(
&mut self,
partials: &mut PartialPaths,
other: &PartialPath,
) {
let symbol_variable_offset = other.largest_symbol_stack_variable();
let scope_variable_offset = other.largest_scope_stack_variable(partials);
self.symbol_stack_precondition = self.symbol_stack_precondition.with_offset(
partials,
symbol_variable_offset,
scope_variable_offset,
);
self.symbol_stack_postcondition = self.symbol_stack_postcondition.with_offset(
partials,
symbol_variable_offset,
scope_variable_offset,
);
self.scope_stack_precondition = self
.scope_stack_precondition
.with_offset(scope_variable_offset);
self.scope_stack_postcondition = self
.scope_stack_postcondition
.with_offset(scope_variable_offset);
}
pub fn append(
&mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
edge: Edge,
) -> Result<(), PathResolutionError> {
if edge.source != self.end_node {
return Err(PathResolutionError::IncorrectSourceNode);
}
let sink = &graph[edge.sink];
if let Node::PushSymbol(sink) = sink {
let sink_symbol = sink.symbol;
let postcondition_symbol = PartialScopedSymbol {
symbol: sink_symbol,
scopes: ControlledOption::none(),
};
self.symbol_stack_postcondition
.push_front(partials, postcondition_symbol);
} else if let Node::PushScopedSymbol(sink) = sink {
let sink_symbol = sink.symbol;
let sink_scope = graph
.node_for_id(sink.scope)
.ok_or(PathResolutionError::UnknownAttachedScope)?;
let mut attached_scopes = self.scope_stack_postcondition;
attached_scopes.push_front(partials, sink_scope);
let postcondition_symbol = PartialScopedSymbol {
symbol: sink_symbol,
scopes: ControlledOption::some(attached_scopes),
};
self.symbol_stack_postcondition
.push_front(partials, postcondition_symbol);
} else if let Node::PopSymbol(sink) = sink {
if let Some(top) = self.symbol_stack_postcondition.pop_front(partials) {
if top.symbol != sink.symbol {
return Err(PathResolutionError::IncorrectPoppedSymbol);
}
if top.scopes.is_some() {
return Err(PathResolutionError::UnexpectedAttachedScopeList);
}
} else {
let precondition_symbol = PartialScopedSymbol {
symbol: sink.symbol,
scopes: ControlledOption::none(),
};
self.symbol_stack_precondition
.push_back(partials, precondition_symbol);
}
} else if let Node::PopScopedSymbol(sink) = sink {
if let Some(top) = self.symbol_stack_postcondition.pop_front(partials) {
if top.symbol != sink.symbol {
return Err(PathResolutionError::IncorrectPoppedSymbol);
}
let new_scope_stack = match top.scopes.into_option() {
Some(scopes) => scopes,
None => return Err(PathResolutionError::MissingAttachedScopeList),
};
self.scope_stack_postcondition = new_scope_stack;
} else {
let scope_stack_variable = self.fresh_scope_stack_variable(partials);
let precondition_symbol = PartialScopedSymbol {
symbol: sink.symbol,
scopes: ControlledOption::some(PartialScopeStack::from_variable(
scope_stack_variable,
)),
};
self.symbol_stack_precondition
.push_back(partials, precondition_symbol);
self.scope_stack_postcondition =
PartialScopeStack::from_variable(scope_stack_variable);
}
} else if let Node::DropScopes(_) = sink {
self.scope_stack_postcondition = PartialScopeStack::empty();
}
self.end_node = edge.sink;
self.edges.push_back(
partials,
PartialPathEdge {
source_node_id: graph[edge.source].id(),
precedence: edge.precedence,
},
);
Ok(())
}
pub fn resolve(
&mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
) -> Result<(), PathResolutionError> {
if !graph[self.end_node].is_jump_to() {
return Ok(());
}
if self.scope_stack_postcondition.can_only_match_empty() {
return Err(PathResolutionError::EmptyScopeStack);
}
if !self.scope_stack_postcondition.contains_scopes() {
return Ok(());
}
let top_scope = self.scope_stack_postcondition.pop_front(partials).unwrap();
self.edges.push_back(
partials,
PartialPathEdge {
source_node_id: graph[self.end_node].id(),
precedence: 0,
},
);
self.end_node = top_scope;
Ok(())
}
pub fn extend_from_file<R: Extend<PartialPath>>(
&self,
graph: &StackGraph,
partials: &mut PartialPaths,
file: Handle<File>,
result: &mut R,
) {
let extensions = graph.outgoing_edges(self.end_node);
result.reserve(extensions.size_hint().0);
for extension in extensions {
if !graph[extension.sink].is_in_file(file) {
continue;
}
let mut new_path = self.clone();
if new_path.append(graph, partials, extension).is_err() {
continue;
}
if new_path.resolve(graph, partials).is_err() {
continue;
}
result.push(new_path);
}
}
}
impl PartialPaths {
pub fn find_all_partial_paths_in_file<F>(
&mut self,
graph: &StackGraph,
file: Handle<File>,
mut visit: F,
) where
F: FnMut(&StackGraph, &mut PartialPaths, PartialPath),
{
let mut cycle_detector = CycleDetector::new();
let mut queue = VecDeque::new();
queue.push_back(PartialPath::from_node(graph, self, graph.root_node()).unwrap());
queue.extend(
graph
.nodes_for_file(file)
.filter(|node| match graph[*node] {
Node::PushScopedSymbol(_) => true,
Node::PushSymbol(_) => true,
Node::ExportedScope(_) => true,
_ => false,
})
.map(|node| PartialPath::from_node(graph, self, node).unwrap()),
);
while let Some(path) = queue.pop_front() {
if !cycle_detector.should_process_path(&path, |probe| probe.cmp(graph, self, &path)) {
continue;
}
path.extend_from_file(graph, self, file, &mut queue);
visit(graph, self, path);
}
}
}
impl Path {
pub fn from_partial_path(
graph: &StackGraph,
paths: &mut Paths,
partials: &mut PartialPaths,
partial_path: &PartialPath,
) -> Option<Path> {
let mut path = Path {
start_node: partial_path.start_node,
end_node: partial_path.start_node,
symbol_stack: SymbolStack::empty(),
scope_stack: ScopeStack::empty(),
edges: PathEdgeList::empty(),
};
path.append_partial_path(graph, paths, partials, partial_path)
.ok()?;
Some(path)
}
pub fn append_partial_path(
&mut self,
graph: &StackGraph,
paths: &mut Paths,
partials: &mut PartialPaths,
partial_path: &PartialPath,
) -> Result<(), PathResolutionError> {
if partial_path.start_node != self.end_node {
return Err(PathResolutionError::IncorrectSourceNode);
}
let mut symbol_bindings = SymbolStackBindings::new();
let mut scope_bindings = ScopeStackBindings::new();
partial_path
.scope_stack_precondition
.match_stack(self.scope_stack, &mut scope_bindings)?;
partial_path.symbol_stack_precondition.match_stack(
graph,
paths,
partials,
self.symbol_stack,
&mut symbol_bindings,
&mut scope_bindings,
)?;
self.symbol_stack = partial_path.symbol_stack_postcondition.apply_bindings(
paths,
partials,
&symbol_bindings,
&scope_bindings,
)?;
self.scope_stack = partial_path.scope_stack_postcondition.apply_bindings(
paths,
partials,
&scope_bindings,
)?;
let mut edges = partial_path.edges;
while let Some(edge) = edges.pop_front(partials) {
self.edges.push_back(paths, edge.into());
}
self.end_node = partial_path.end_node;
Ok(())
}
}
impl PartialPath {
#[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
pub fn concatenate(
&mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
rhs: &PartialPath,
) -> Result<(), PathResolutionError> {
let lhs = self;
if lhs.end_node != rhs.start_node {
return Err(PathResolutionError::IncorrectSourceNode);
}
let mut symbol_bindings = PartialSymbolStackBindings::new();
let mut scope_bindings = PartialScopeStackBindings::new();
let unified_scope_stack = lhs.scope_stack_postcondition.unify(
partials,
rhs.scope_stack_precondition,
&mut scope_bindings,
)?;
let unified_symbol_stack = lhs.symbol_stack_postcondition.unify(
partials,
rhs.symbol_stack_precondition,
&mut symbol_bindings,
&mut scope_bindings,
)?;
#[cfg(feature = "copious-debugging")]
{
let unified_symbol_stack = unified_symbol_stack.display(graph, partials).to_string();
let unified_scope_stack = unified_scope_stack.display(graph, partials).to_string();
let symbol_bindings = symbol_bindings.display(graph, partials).to_string();
let scope_bindings = scope_bindings.display(graph, partials).to_string();
copious_debugging!(
" via <{}> ({}) {} {}",
unified_symbol_stack,
unified_scope_stack,
symbol_bindings,
scope_bindings,
);
}
lhs.symbol_stack_precondition = lhs.symbol_stack_precondition.apply_partial_bindings(
partials,
&symbol_bindings,
&scope_bindings,
)?;
lhs.symbol_stack_postcondition = rhs.symbol_stack_postcondition.apply_partial_bindings(
partials,
&symbol_bindings,
&scope_bindings,
)?;
lhs.scope_stack_precondition = lhs
.scope_stack_precondition
.apply_partial_bindings(partials, &scope_bindings)?;
lhs.scope_stack_postcondition = rhs
.scope_stack_postcondition
.apply_partial_bindings(partials, &scope_bindings)?;
let mut edges = rhs.edges;
while let Some(edge) = edges.pop_front(partials) {
lhs.edges.push_back(partials, edge);
}
lhs.end_node = rhs.end_node;
Ok(())
}
}
pub struct PartialPaths {
pub(crate) partial_symbol_stacks: DequeArena<PartialScopedSymbol>,
pub(crate) partial_scope_stacks: DequeArena<Handle<Node>>,
pub(crate) partial_path_edges: DequeArena<PartialPathEdge>,
}
impl PartialPaths {
pub fn new() -> PartialPaths {
PartialPaths {
partial_symbol_stacks: Deque::new_arena(),
partial_scope_stacks: Deque::new_arena(),
partial_path_edges: Deque::new_arena(),
}
}
}