use std::fmt::Display;
use std::ops::Deref;
use std::ops::Index;
use std::ops::IndexMut;
use either::Either;
use fxhash::FxHashMap;
use smallvec::SmallVec;
use crate::arena::Arena;
use crate::arena::Handle;
use crate::arena::SupplementalArena;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Symbol {
symbol: String,
}
impl Symbol {
pub fn as_str(&self) -> &str {
&self.symbol
}
}
impl AsRef<str> for Symbol {
fn as_ref(&self) -> &str {
&self.symbol
}
}
impl Deref for Symbol {
type Target = str;
fn deref(&self) -> &str {
&self.symbol
}
}
impl Display for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.symbol)
}
}
impl PartialEq<&str> for Symbol {
fn eq(&self, other: &&str) -> bool {
self.symbol == **other
}
}
impl StackGraph {
pub fn add_symbol<S: AsRef<str> + ?Sized>(&mut self, symbol: &S) -> Handle<Symbol> {
let symbol = symbol.as_ref();
if let Some(handle) = self.symbol_handles.get(symbol) {
return *handle;
}
let symbol_value = symbol.to_string();
let symbol = Symbol {
symbol: symbol_value.clone(),
};
let handle = self.symbols.add(symbol);
self.symbol_handles.insert(symbol_value, handle);
handle
}
pub fn iter_symbols(&self) -> impl Iterator<Item = Handle<Symbol>> {
self.symbols.iter_handles()
}
}
impl Index<Handle<Symbol>> for StackGraph {
type Output = Symbol;
#[inline(always)]
fn index(&self, handle: Handle<Symbol>) -> &Symbol {
&self.symbols.get(handle)
}
}
#[doc(hidden)]
pub struct DisplaySymbol<'a> {
wrapped: Handle<Symbol>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplaySymbol<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.graph[self.wrapped])
}
}
impl Handle<Symbol> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplaySymbol {
wrapped: self,
graph,
}
}
}
pub struct File {
pub name: String,
}
impl StackGraph {
pub fn add_file<S: AsRef<str> + ?Sized>(
&mut self,
name: &S,
) -> Result<Handle<File>, Handle<File>> {
let name = name.as_ref();
if let Some(handle) = self.file_handles.get(name) {
return Err(*handle);
}
let name_value = name.to_string();
let file = File {
name: name_value.clone(),
};
let handle = self.files.add(file);
self.file_handles.insert(name_value, handle);
Ok(handle)
}
#[inline(always)]
pub fn get_or_create_file<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> Handle<File> {
self.add_file(name).unwrap_or_else(|handle| handle)
}
}
impl StackGraph {
pub fn iter_files(&self) -> impl Iterator<Item = Handle<File>> + '_ {
self.files.iter_handles()
}
}
impl Display for File {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl Index<Handle<File>> for StackGraph {
type Output = File;
#[inline(always)]
fn index(&self, handle: Handle<File>) -> &File {
&self.files.get(handle)
}
}
#[doc(hidden)]
pub struct DisplayFile<'a> {
wrapped: Handle<File>,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.graph[self.wrapped])
}
}
impl Handle<File> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayFile {
wrapped: self,
graph,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NodeID {
pub file: Handle<File>,
pub local_id: u32,
}
#[doc(hidden)]
pub struct DisplayNodeID<'a> {
wrapped: NodeID,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNodeID<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}({})",
self.wrapped.file.display(self.graph),
self.wrapped.local_id,
)
}
}
impl NodeID {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNodeID {
wrapped: self,
graph,
}
}
}
pub enum Node {
DropScopes(DropScopesNode),
ExportedScope(ExportedScopeNode),
InternalScope(InternalScopeNode),
JumpTo(JumpToNode),
PushScopedSymbol(PushScopedSymbolNode),
PushSymbol(PushSymbolNode),
PopScopedSymbol(PopScopedSymbolNode),
PopSymbol(PopSymbolNode),
Root(RootNode),
Unknown(UnknownNode),
}
impl Node {
#[inline(always)]
pub fn is_definition(&self) -> bool {
match self {
Node::PopScopedSymbol(node) => node.is_definition,
Node::PopSymbol(node) => node.is_definition,
_ => false,
}
}
#[inline(always)]
pub fn is_reference(&self) -> bool {
match self {
Node::PushScopedSymbol(node) => node.is_reference,
Node::PushSymbol(node) => node.is_reference,
_ => false,
}
}
#[inline(always)]
pub fn is_jump_to(&self) -> bool {
matches!(self, Node::JumpTo(_))
}
#[inline(always)]
pub fn is_root(&self) -> bool {
matches!(self, Node::Root(_))
}
pub fn symbol(&self) -> Option<Handle<Symbol>> {
match self {
Node::PushScopedSymbol(node) => Some(node.symbol),
Node::PushSymbol(node) => Some(node.symbol),
Node::PopScopedSymbol(node) => Some(node.symbol),
Node::PopSymbol(node) => Some(node.symbol),
_ => None,
}
}
pub fn id(&self) -> Option<NodeID> {
match self {
Node::DropScopes(node) => Some(node.id),
Node::ExportedScope(node) => Some(node.id),
Node::InternalScope(node) => Some(node.id),
Node::PushScopedSymbol(node) => Some(node.id),
Node::PushSymbol(node) => Some(node.id),
Node::PopScopedSymbol(node) => Some(node.id),
Node::PopSymbol(node) => Some(node.id),
Node::Unknown(node) => Some(node.id),
_ => None,
}
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayNode {
wrapped: self,
graph,
}
}
}
impl StackGraph {
#[inline(always)]
pub fn jump_to_node(&self) -> Handle<Node> {
self.jump_to_node
}
#[inline(always)]
pub fn root_node(&self) -> Handle<Node> {
self.root_node
}
pub fn new_node_id(&mut self, file: Handle<File>) -> NodeID {
self.node_id_handles.unused_id(file)
}
pub fn iter_nodes(&self) -> impl Iterator<Item = Handle<Node>> {
self.nodes.iter_handles()
}
pub fn node_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
self.node_id_handles.try_handle_for_id(node_id)
}
fn add_node(&mut self, node_id: NodeID, node: Node) -> Option<Handle<Node>> {
if let Some(_) = self.node_id_handles.handle_for_id(node_id) {
return None;
}
let handle = self.nodes.add(node);
self.node_id_handles.set_handle_for_id(node_id, handle);
Some(handle)
}
}
#[doc(hidden)]
pub struct DisplayNode<'a> {
wrapped: &'a Node,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.wrapped {
Node::DropScopes(node) => node.display(self.graph).fmt(f),
Node::ExportedScope(node) => node.display(self.graph).fmt(f),
Node::InternalScope(node) => node.display(self.graph).fmt(f),
Node::JumpTo(node) => node.fmt(f),
Node::PushScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PushSymbol(node) => node.display(self.graph).fmt(f),
Node::PopScopedSymbol(node) => node.display(self.graph).fmt(f),
Node::PopSymbol(node) => node.display(self.graph).fmt(f),
Node::Root(node) => node.fmt(f),
Node::Unknown(node) => node.display(self.graph).fmt(f),
}
}
}
impl Handle<Node> {
pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
DisplayNode {
wrapped: &graph[self],
graph,
}
}
}
impl Index<Handle<Node>> for StackGraph {
type Output = Node;
#[inline(always)]
fn index(&self, handle: Handle<Node>) -> &Node {
self.nodes.get(handle)
}
}
impl IndexMut<Handle<Node>> for StackGraph {
#[inline(always)]
fn index_mut(&mut self, handle: Handle<Node>) -> &mut Node {
self.nodes.get_mut(handle)
}
}
pub struct DropScopesNode {
pub id: NodeID,
}
impl From<DropScopesNode> for Node {
fn from(node: DropScopesNode) -> Node {
Node::DropScopes(node)
}
}
impl DropScopesNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayDropScopesNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayDropScopesNode<'a> {
wrapped: &'a DropScopesNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayDropScopesNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(f, "[{} drop scopes]", self.wrapped.id.display(self.graph))
}
}
}
pub struct ExportedScopeNode {
pub id: NodeID,
}
impl From<ExportedScopeNode> for Node {
fn from(node: ExportedScopeNode) -> Node {
Node::ExportedScope(node)
}
}
impl ExportedScopeNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayExportedScopeNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayExportedScopeNode<'a> {
wrapped: &'a ExportedScopeNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayExportedScopeNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} exported scope]",
self.wrapped.id.display(self.graph),
)
}
}
}
pub struct InternalScopeNode {
pub id: NodeID,
}
impl From<InternalScopeNode> for Node {
fn from(node: InternalScopeNode) -> Node {
Node::InternalScope(node)
}
}
impl InternalScopeNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayInternalScopeNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayInternalScopeNode<'a> {
wrapped: &'a InternalScopeNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayInternalScopeNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} internal scope]",
self.wrapped.id.display(self.graph),
)
}
}
}
pub struct JumpToNode;
impl From<JumpToNode> for Node {
fn from(node: JumpToNode) -> Node {
Node::JumpTo(node)
}
}
impl Display for JumpToNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[jump to scope]")
}
}
pub struct PopScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub is_definition: bool,
}
impl From<PopScopedSymbolNode> for Node {
fn from(node: PopScopedSymbolNode) -> Node {
Node::PopScopedSymbol(node)
}
}
impl PopScopedSymbolNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopScopedSymbolNode<'a> {
wrapped: &'a PopScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"scoped definition"
} else {
"pop scoped"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
pub struct PopSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub is_definition: bool,
}
impl From<PopSymbolNode> for Node {
fn from(node: PopSymbolNode) -> Node {
Node::PopSymbol(node)
}
}
impl PopSymbolNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPopSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPopSymbolNode<'a> {
wrapped: &'a PopSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPopSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_definition {
"definition"
} else {
"pop"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
pub struct PushScopedSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub scope: Handle<Node>,
pub is_reference: bool,
}
impl From<PushScopedSymbolNode> for Node {
fn from(node: PushScopedSymbolNode) -> Node {
Node::PushScopedSymbol(node)
}
}
impl PushScopedSymbolNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushScopedSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushScopedSymbolNode<'a> {
wrapped: &'a PushScopedSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushScopedSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"scoped reference"
} else {
"push scoped"
},
self.wrapped.symbol.display(self.graph),
self.wrapped.scope.display(self.graph),
)
}
}
}
pub struct PushSymbolNode {
pub id: NodeID,
pub symbol: Handle<Symbol>,
pub is_reference: bool,
}
impl From<PushSymbolNode> for Node {
fn from(node: PushSymbolNode) -> Node {
Node::PushSymbol(node)
}
}
impl PushSymbolNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayPushSymbolNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayPushSymbolNode<'a> {
wrapped: &'a PushSymbolNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayPushSymbolNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(
f,
"[{} {} {}]",
self.wrapped.id.display(self.graph),
if self.wrapped.is_reference {
"reference"
} else {
"push"
},
self.wrapped.symbol.display(self.graph),
)
}
}
}
pub struct RootNode;
impl From<RootNode> for Node {
fn from(node: RootNode) -> Node {
Node::Root(node)
}
}
impl Display for RootNode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[root]")
}
}
pub struct UnknownNode {
pub id: NodeID,
}
impl From<UnknownNode> for Node {
fn from(node: UnknownNode) -> Node {
Node::Unknown(node)
}
}
impl UnknownNode {
pub fn add_to_graph(self, graph: &mut StackGraph) -> Option<Handle<Node>> {
graph.add_node(self.id, self.into())
}
pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
DisplayUnknownNode {
wrapped: self,
graph,
}
}
}
#[doc(hidden)]
pub struct DisplayUnknownNode<'a> {
wrapped: &'a UnknownNode,
graph: &'a StackGraph,
}
impl<'a> Display for DisplayUnknownNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if f.alternate() {
write!(f, "[{}]", self.wrapped.id.display(self.graph))
} else {
write!(f, "[{} unknown]", self.wrapped.id.display(self.graph))
}
}
}
impl StackGraph {
pub fn resolve_unknown_node(&mut self, node: Node) -> Result<(), &Node> {
let id = node.id().unwrap();
let handle = self.node_id_handles.handle_for_id(id).unwrap();
let arena_node = &mut self[handle];
if !matches!(arena_node, Node::Unknown(_)) {
return Err(arena_node);
}
*arena_node = node;
Ok(())
}
}
struct NodeIDHandles {
files: SupplementalArena<File, Vec<Option<Handle<Node>>>>,
}
impl NodeIDHandles {
fn new() -> NodeIDHandles {
NodeIDHandles {
files: SupplementalArena::new(),
}
}
fn try_handle_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = self.files.get(node_id.file)?;
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
return None;
}
file_entry[node_index]
}
fn handle_for_id(&mut self, node_id: NodeID) -> Option<Handle<Node>> {
let file_entry = &mut self.files[node_id.file];
let node_index = node_id.local_id as usize;
if node_index >= file_entry.len() {
file_entry.resize(node_index + 1, None);
}
file_entry[node_index]
}
fn set_handle_for_id(&mut self, node_id: NodeID, handle: Handle<Node>) {
let file_entry = &mut self.files[node_id.file];
let node_index = node_id.local_id as usize;
file_entry[node_index] = Some(handle);
}
fn unused_id(&mut self, file: Handle<File>) -> NodeID {
let local_id = self
.files
.get(file)
.map(|file_entry| file_entry.len() as u32)
.unwrap_or(0);
NodeID { file, local_id }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Edge {
pub source: Handle<Node>,
pub sink: Handle<Node>,
}
impl StackGraph {
pub fn add_edge(&mut self, edge: Edge) {
let edges = &mut self.outgoing_edges[edge.source];
if let Err(index) = edges.binary_search(&edge.sink) {
edges.insert(index, edge.sink);
}
}
pub fn remove_edge(&mut self, edge: Edge) {
let edges = &mut self.outgoing_edges[edge.source];
if let Ok(index) = edges.binary_search(&edge.sink) {
edges.remove(index);
}
}
pub fn outgoing_edges(&self, source: Handle<Node>) -> impl Iterator<Item = Edge> + '_ {
match self.outgoing_edges.get(source) {
Some(edges) => Either::Right(edges.iter().map(move |sink| Edge {
source,
sink: *sink,
})),
None => Either::Left(std::iter::empty()),
}
}
}
pub struct StackGraph {
symbols: Arena<Symbol>,
symbol_handles: FxHashMap<String, Handle<Symbol>>,
files: Arena<File>,
file_handles: FxHashMap<String, Handle<File>>,
nodes: Arena<Node>,
node_id_handles: NodeIDHandles,
jump_to_node: Handle<Node>,
root_node: Handle<Node>,
outgoing_edges: SupplementalArena<Node, SmallVec<[Handle<Node>; 8]>>,
}
impl StackGraph {
pub fn new() -> StackGraph {
let mut nodes = Arena::new();
let root_node = nodes.add(RootNode.into());
let jump_to_node = nodes.add(JumpToNode.into());
StackGraph {
symbols: Arena::new(),
symbol_handles: FxHashMap::default(),
files: Arena::new(),
file_handles: FxHashMap::default(),
nodes,
node_id_handles: NodeIDHandles::new(),
jump_to_node,
root_node,
outgoing_edges: SupplementalArena::new(),
}
}
}