use std::collections::VecDeque;
use std::fmt::Display;
use controlled_option::ControlledOption;
use controlled_option::Niche;
use crate::arena::Deque;
use crate::arena::DequeArena;
use crate::arena::Handle;
use crate::arena::List;
use crate::arena::ListArena;
use crate::cycles::CycleDetector;
use crate::graph::Edge;
use crate::graph::Node;
use crate::graph::NodeID;
use crate::graph::StackGraph;
use crate::graph::Symbol;
use crate::utils::cmp_option;
use crate::utils::equals_option;
trait DisplayWithPaths {
fn prepare(&mut self, _graph: &StackGraph, _paths: &mut Paths) {}
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result;
}
fn display_with<'a, D>(
mut value: D,
graph: &'a StackGraph,
paths: &'a mut Paths,
) -> impl Display + 'a
where
D: DisplayWithPaths + 'a,
{
value.prepare(graph, paths);
DisplayWithPathsWrapper {
value,
graph,
paths,
}
}
fn display_prepared<'a, D>(value: D, graph: &'a StackGraph, paths: &'a Paths) -> impl Display + 'a
where
D: DisplayWithPaths + 'a,
{
DisplayWithPathsWrapper {
value,
graph,
paths,
}
}
#[doc(hidden)]
struct DisplayWithPathsWrapper<'a, D> {
value: D,
graph: &'a StackGraph,
paths: &'a Paths,
}
impl<'a, D> Display for DisplayWithPathsWrapper<'a, D>
where
D: DisplayWithPaths,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.value.display_with(self.graph, self.paths, f)
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ScopedSymbol {
pub symbol: Handle<Symbol>,
pub scopes: ControlledOption<ScopeStack>,
}
impl ScopedSymbol {
pub fn equals(&self, paths: &Paths, other: &ScopedSymbol) -> bool {
self.symbol == other.symbol
&& equals_option(
self.scopes.into_option(),
other.scopes.into_option(),
|a, b| a.equals(paths, &b),
)
}
pub fn cmp(
&self,
graph: &StackGraph,
paths: &Paths,
other: &ScopedSymbol,
) -> 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(paths, &b),
)
})
}
pub fn display<'a>(self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
}
impl DisplayWithPaths for ScopedSymbol {
fn prepare(&mut self, graph: &StackGraph, paths: &mut Paths) {
if let Some(mut scopes) = self.scopes.into_option() {
scopes.prepare(graph, paths);
self.scopes = scopes.into();
}
}
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
match self.scopes.into_option() {
Some(scopes) => write!(
f,
"{}/({})",
self.symbol.display(graph),
display_prepared(scopes, graph, paths),
),
None => write!(f, "{}", self.symbol.display(graph)),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Niche)]
pub struct SymbolStack {
#[niche]
list: List<ScopedSymbol>,
length: u32,
}
impl SymbolStack {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length as usize
}
pub fn empty() -> SymbolStack {
SymbolStack {
list: List::empty(),
length: 0,
}
}
pub fn equals(&self, paths: &Paths, other: &SymbolStack) -> bool {
self.list
.equals_with(&paths.symbol_stacks, other.list, |a, b| a.equals(paths, b))
}
pub fn cmp(
&self,
graph: &StackGraph,
paths: &Paths,
other: &SymbolStack,
) -> std::cmp::Ordering {
self.list
.cmp_with(&paths.symbol_stacks, other.list, |a, b| {
a.cmp(graph, paths, b)
})
}
pub fn push_front(&mut self, paths: &mut Paths, scoped_symbol: ScopedSymbol) {
self.length += 1;
self.list
.push_front(&mut paths.symbol_stacks, scoped_symbol);
}
pub fn pop_front(&mut self, paths: &Paths) -> Option<ScopedSymbol> {
let result = self.list.pop_front(&paths.symbol_stacks).copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn display<'a>(self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
pub fn iter<'a>(&'a self, paths: &'a Paths) -> impl Iterator<Item = ScopedSymbol> + 'a {
self.list.iter(&paths.symbol_stacks).copied()
}
}
impl DisplayWithPaths for SymbolStack {
fn prepare(&mut self, graph: &StackGraph, paths: &mut Paths) {
let stack = self;
while let Some(mut symbol) = stack.pop_front(paths) {
symbol.prepare(graph, paths);
}
}
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
for symbol in self.iter(paths) {
symbol.display_with(graph, paths, f)?;
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone, Copy, Niche)]
pub struct ScopeStack {
#[niche]
list: List<Handle<Node>>,
length: u32,
}
impl ScopeStack {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length as usize
}
pub fn empty() -> ScopeStack {
ScopeStack {
list: List::empty(),
length: 0,
}
}
pub fn equals(&self, paths: &Paths, other: &ScopeStack) -> bool {
self.list
.equals_with(&paths.scope_stacks, other.list, |a, b| *a == *b)
}
pub fn cmp(&self, paths: &Paths, other: &ScopeStack) -> std::cmp::Ordering {
self.list
.cmp_with(&paths.scope_stacks, other.list, |a, b| a.cmp(b))
}
pub fn push_front(&mut self, paths: &mut Paths, node: Handle<Node>) {
self.length += 1;
self.list.push_front(&mut paths.scope_stacks, node);
}
pub fn pop_front(&mut self, paths: &Paths) -> Option<Handle<Node>> {
let result = self.list.pop_front(&paths.scope_stacks).copied();
if result.is_some() {
self.length -= 1;
}
result
}
pub fn display<'a>(self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
pub fn iter<'a>(&'a self, paths: &'a Paths) -> impl Iterator<Item = Handle<Node>> + 'a {
self.list.iter(&paths.scope_stacks).copied()
}
}
impl DisplayWithPaths for ScopeStack {
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
for scope in self.iter(paths) {
write!(f, "{:#}", scope.display(graph))?;
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct PathEdge {
pub source_node_id: NodeID,
pub precedence: i32,
}
impl PathEdge {
pub fn shadows(self, other: PathEdge) -> bool {
self.source_node_id == other.source_node_id && self.precedence > other.precedence
}
pub fn display<'a>(self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
}
impl DisplayWithPaths for PathEdge {
fn display_with(
&self,
graph: &StackGraph,
_paths: &Paths,
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 PathEdgeList {
#[niche]
edges: Deque<PathEdge>,
length: u32,
}
impl PathEdgeList {
#[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, paths: &Paths) -> bool {
self.edges.have_reversal(&paths.path_edges)
}
pub fn empty() -> PathEdgeList {
PathEdgeList {
edges: Deque::empty(),
length: 0,
}
}
pub fn push_front(&mut self, paths: &mut Paths, edge: PathEdge) {
self.length += 1;
self.edges.push_front(&mut paths.path_edges, edge);
}
pub fn push_back(&mut self, paths: &mut Paths, edge: PathEdge) {
self.length += 1;
self.edges.push_back(&mut paths.path_edges, edge);
}
pub fn pop_front(&mut self, paths: &mut Paths) -> Option<PathEdge> {
let result = self.edges.pop_front(&mut paths.path_edges);
if result.is_some() {
self.length -= 1;
}
result.copied()
}
pub fn pop_back(&mut self, paths: &mut Paths) -> Option<PathEdge> {
let result = self.edges.pop_back(&mut paths.path_edges);
if result.is_some() {
self.length -= 1;
}
result.copied()
}
pub fn display<'a>(self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
pub fn shadows(mut self, paths: &mut Paths, mut other: PathEdgeList) -> bool {
while let Some(self_edge) = self.pop_front(paths) {
if let Some(other_edge) = other.pop_front(paths) {
if self_edge.shadows(other_edge) {
return true;
}
} else {
return false;
}
}
false
}
pub fn equals(mut self, paths: &mut Paths, mut other: PathEdgeList) -> bool {
while let Some(self_edge) = self.pop_front(paths) {
if let Some(other_edge) = other.pop_front(paths) {
if self_edge != other_edge {
return false;
}
} else {
return false;
}
}
other.edges.is_empty()
}
pub fn cmp(mut self, paths: &mut Paths, mut other: PathEdgeList) -> std::cmp::Ordering {
use std::cmp::Ordering;
while let Some(self_edge) = self.pop_front(paths) {
if let Some(other_edge) = other.pop_front(paths) {
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, paths: &'a mut Paths) -> impl Iterator<Item = PathEdge> + 'a {
self.edges.iter(&mut paths.path_edges).copied()
}
pub fn iter_unordered<'a>(&self, paths: &'a Paths) -> impl Iterator<Item = PathEdge> + 'a {
self.edges.iter_unordered(&paths.path_edges).copied()
}
}
impl DisplayWithPaths for PathEdgeList {
fn prepare(&mut self, graph: &StackGraph, paths: &mut Paths) {
self.edges.ensure_forwards(&mut paths.path_edges);
let mut edges = self.edges;
while let Some(mut edge) = edges.pop_front(&mut paths.path_edges).copied() {
edge.prepare(graph, paths);
}
}
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
for edge in self.edges.iter_reused(&paths.path_edges) {
edge.display_with(graph, paths, f)?;
}
Ok(())
}
}
#[repr(C)]
#[derive(Clone)]
pub struct Path {
pub start_node: Handle<Node>,
pub end_node: Handle<Node>,
pub symbol_stack: SymbolStack,
pub scope_stack: ScopeStack,
pub edges: PathEdgeList,
}
impl Path {
pub fn from_node(graph: &StackGraph, paths: &mut Paths, node: Handle<Node>) -> Option<Path> {
let mut symbol_stack = SymbolStack::empty();
let mut scope_stack = ScopeStack::empty();
match &graph[node] {
Node::PushScopedSymbol(node) => {
let scope = graph.node_for_id(node.scope)?;
scope_stack.push_front(paths, scope);
symbol_stack.push_front(
paths,
ScopedSymbol {
symbol: node.symbol,
scopes: ControlledOption::some(scope_stack),
},
);
}
Node::PushSymbol(node) => {
symbol_stack.push_front(
paths,
ScopedSymbol {
symbol: node.symbol,
scopes: ControlledOption::none(),
},
);
}
Node::PopScopedSymbol(_) => return None,
Node::PopSymbol(_) => return None,
_ => {}
};
Some(Path {
start_node: node,
end_node: node,
symbol_stack,
scope_stack,
edges: PathEdgeList::empty(),
})
}
pub fn shadows(&self, paths: &mut Paths, other: &Path) -> bool {
self.edges.shadows(paths, other.edges)
}
pub fn equals(&self, paths: &mut Paths, other: &Path) -> bool {
self.start_node == other.start_node
&& self.end_node == other.end_node
&& self.symbol_stack.equals(paths, &other.symbol_stack)
&& self.scope_stack.equals(paths, &other.scope_stack)
&& self.edges.equals(paths, other.edges)
}
pub fn cmp(&self, graph: &StackGraph, paths: &mut Paths, other: &Path) -> 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.cmp(graph, paths, &other.symbol_stack))
.then_with(|| self.scope_stack.cmp(paths, &other.scope_stack))
.then_with(|| self.edges.cmp(paths, other.edges))
}
pub fn is_complete(&self, graph: &StackGraph) -> bool {
if !graph[self.start_node].is_reference() {
return false;
} else if !graph[self.end_node].is_definition() {
return false;
} else if !self.symbol_stack.is_empty() {
return false;
} else if !self.scope_stack.is_empty() {
return false;
} else {
true
}
}
pub fn display<'a>(&'a self, graph: &'a StackGraph, paths: &'a mut Paths) -> impl Display + 'a {
display_with(self, graph, paths)
}
}
impl<'a> DisplayWithPaths for &'a Path {
fn prepare(&mut self, graph: &StackGraph, paths: &mut Paths) {
self.symbol_stack.clone().prepare(graph, paths);
self.scope_stack.clone().prepare(graph, paths);
}
fn display_with(
&self,
graph: &StackGraph,
paths: &Paths,
f: &mut std::fmt::Formatter,
) -> std::fmt::Result {
write!(
f,
"{} -> {}",
self.start_node.display(graph),
self.end_node.display(graph),
)?;
if !self.symbol_stack.is_empty() || !self.scope_stack.is_empty() {
write!(
f,
" <{}> ({})",
display_prepared(self.symbol_stack, graph, paths),
display_prepared(self.scope_stack, graph, paths),
)?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum PathResolutionError {
EmptyScopeStack,
EmptySymbolStack,
IncompatibleScopeStackVariables,
IncompatibleSymbolStackVariables,
IncorrectFile,
IncorrectPoppedSymbol,
IncorrectSourceNode,
MissingAttachedScopeList,
ScopeStackUnsatisfied,
SymbolStackUnsatisfied,
UnboundSymbolStackVariable,
UnboundScopeStackVariable,
UnexpectedAttachedScopeList,
UnknownAttachedScope,
}
impl Path {
pub fn append(
&mut self,
graph: &StackGraph,
paths: &mut Paths,
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 scoped_symbol = ScopedSymbol {
symbol: sink_symbol,
scopes: ControlledOption::none(),
};
self.symbol_stack.push_front(paths, scoped_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;
attached_scopes.push_front(paths, sink_scope);
let scoped_symbol = ScopedSymbol {
symbol: sink_symbol,
scopes: ControlledOption::some(attached_scopes),
};
self.symbol_stack.push_front(paths, scoped_symbol);
} else if let Node::PopSymbol(sink) = sink {
let top = match self.symbol_stack.pop_front(paths) {
Some(top) => top,
None => return Err(PathResolutionError::EmptySymbolStack),
};
if top.symbol != sink.symbol {
return Err(PathResolutionError::IncorrectPoppedSymbol);
}
if top.scopes.is_some() {
return Err(PathResolutionError::UnexpectedAttachedScopeList);
}
} else if let Node::PopScopedSymbol(sink) = sink {
let top = match self.symbol_stack.pop_front(paths) {
Some(top) => top,
None => return Err(PathResolutionError::EmptySymbolStack),
};
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 = new_scope_stack;
} else if let Node::DropScopes(_) = sink {
self.scope_stack = ScopeStack::empty();
}
self.end_node = edge.sink;
self.edges.push_back(
paths,
PathEdge {
source_node_id: graph[edge.source].id(),
precedence: edge.precedence,
},
);
Ok(())
}
pub fn resolve(
&mut self,
graph: &StackGraph,
paths: &mut Paths,
) -> Result<(), PathResolutionError> {
if !graph[self.end_node].is_jump_to() {
return Ok(());
}
let top_scope = match self.scope_stack.pop_front(paths) {
Some(scope) => scope,
None => return Err(PathResolutionError::EmptyScopeStack),
};
self.edges.push_back(
paths,
PathEdge {
source_node_id: graph[self.end_node].id(),
precedence: 0,
},
);
self.end_node = top_scope;
Ok(())
}
pub fn extend<R: Extend<Path>>(&self, graph: &StackGraph, paths: &mut Paths, result: &mut R) {
let extensions = graph.outgoing_edges(self.end_node);
result.reserve(extensions.size_hint().0);
for extension in extensions {
let mut new_path = self.clone();
if new_path.append(graph, paths, extension).is_err() {
continue;
}
if new_path.resolve(graph, paths).is_err() {
continue;
}
result.push(new_path);
}
}
}
impl Paths {
pub fn find_all_paths<I, F>(&mut self, graph: &StackGraph, starting_nodes: I, mut visit: F)
where
I: IntoIterator<Item = Handle<Node>>,
F: FnMut(&StackGraph, &mut Paths, Path),
{
let mut cycle_detector = CycleDetector::new();
let mut queue = starting_nodes
.into_iter()
.filter_map(|node| Path::from_node(graph, self, node))
.collect::<VecDeque<_>>();
while let Some(path) = queue.pop_front() {
if !cycle_detector.should_process_path(&path, |probe| probe.cmp(graph, self, &path)) {
continue;
}
path.extend(graph, self, &mut queue);
visit(graph, self, path);
}
}
}
pub trait Extend<T> {
fn reserve(&mut self, additional: usize);
fn push(&mut self, item: T);
}
impl<T> Extend<T> for Vec<T> {
fn reserve(&mut self, additional: usize) {
self.reserve(additional);
}
fn push(&mut self, item: T) {
self.push(item);
}
}
impl<T> Extend<T> for VecDeque<T> {
fn reserve(&mut self, additional: usize) {
self.reserve(additional);
}
fn push(&mut self, item: T) {
self.push_back(item);
}
}
impl Paths {
pub fn remove_shadowed_paths(&mut self, paths: &mut Vec<Path>) {
let mut keep = vec![true; paths.len()];
for (i, comparator) in paths.iter().enumerate() {
for (j, other) in paths.iter().enumerate() {
if i == j || !keep[j] {
continue;
}
if comparator.shadows(self, &other) {
keep[j] = false;
}
}
}
let mut iter = keep.iter().copied();
paths.retain(|_| iter.next().unwrap());
}
}
pub struct Paths {
pub(crate) scope_stacks: ListArena<Handle<Node>>,
pub(crate) symbol_stacks: ListArena<ScopedSymbol>,
pub(crate) path_edges: DequeArena<PathEdge>,
}
impl Paths {
pub fn new() -> Paths {
Paths {
scope_stacks: List::new_arena(),
symbol_stacks: List::new_arena(),
path_edges: Deque::new_arena(),
}
}
}