use core::ffi::c_void;
use core::ptr::{self, NonNull};
use crate::ffi::TSLanguage;
use super::super::error_costs::ERROR_STATE;
use super::super::language::language_write_symbol_as_dot_string;
use super::super::subtree::SubtreeArray;
use super::super::utils::Array;
use super::{fprintf, stderr_file, Stack, StackIterator, StackNode, StackStatus};
pub unsafe fn stack_print_dot_graph(
stack: &mut Stack,
language: *const TSLanguage,
mut f: *mut c_void,
) -> bool {
stack.materialize_window();
let arena = stack.arena();
stack.iterators.reserve(32);
if f.is_null() {
f = stderr_file();
}
fprintf(f, c"digraph stack {\n".as_ptr().cast::<i8>());
fprintf(f, c"rankdir=\"RL\";\n".as_ptr().cast::<i8>());
fprintf(f, c"edge [arrowhead=none]\n".as_ptr().cast::<i8>());
let mut visited_nodes: Array<NonNull<StackNode>> = Array::new();
stack.iterators.clear();
for i in 0..stack.heads.size {
if stack.head(i).is_halted() {
continue;
}
let node_count_since_error = stack.node_count_since_error(i);
let error_cost = stack.error_cost(i);
let head = stack.head(i);
fprintf(
f,
c"node_head_%u [shape=none, label=\"\"]\n"
.as_ptr()
.cast::<i8>(),
i,
);
fprintf(
f,
c"node_head_%u -> node_%p [".as_ptr().cast::<i8>(),
i,
head.node.as_ptr().cast::<c_void>(),
);
if head.status == StackStatus::Paused {
fprintf(f, c"color=red ".as_ptr().cast::<i8>());
}
fprintf(
f,
c"label=%u, fontcolor=blue, weight=10000, labeltooltip=\"node_count: %u\nerror_cost: %u".as_ptr().cast::<i8>(),
i,
node_count_since_error,
error_cost,
);
if let Some(summary) = head.summary {
fprintf(f, c"\nsummary:".as_ptr().cast::<i8>());
let summary = summary.as_ref();
for entry in summary.as_slice() {
fprintf(f, c" %u".as_ptr().cast::<i8>(), u32::from(entry.state));
}
}
if !head.last_external_token.is_null() {
let state = head.last_external_token.external_scanner_state(arena);
fprintf(f, c"\nexternal_scanner_state:".as_ptr().cast::<i8>());
for &byte in state.as_bytes(arena) {
fprintf(f, c" %2X".as_ptr().cast::<i8>(), u32::from(byte));
}
}
fprintf(f, c"\"]\n".as_ptr().cast::<i8>());
let iter = StackIterator {
node: head.node,
subtrees: SubtreeArray::new(),
subtree_count: 0,
};
stack.iterators.push(iter);
}
loop {
let mut all_iterators_done = true;
for i in 0..stack.iterators.size {
let iterator = ptr::read(stack.iterators.get_unchecked(i));
let node = iterator.node;
if visited_nodes.as_slice().contains(&node) {
continue;
}
all_iterators_done = false;
let node_ref = node.as_ref();
fprintf(
f,
c"node_%p [".as_ptr().cast::<i8>(),
node.as_ptr().cast::<c_void>(),
);
if node_ref.state == ERROR_STATE {
fprintf(f, c"label=\"?\"".as_ptr().cast::<i8>());
} else if node_ref.link_count == 1
&& !node_ref.links[0].subtree.is_null()
&& node_ref.links[0].subtree.extra(arena)
{
fprintf(f, c"shape=point margin=0 label=\"\"".as_ptr().cast::<i8>());
} else {
fprintf(
f,
c"label=\"%d\"".as_ptr().cast::<i8>(),
i32::from(node_ref.state),
);
}
fprintf(
f,
c" tooltip=\"position: %u,%u\nnode_count:%u\nerror_cost: %u\ndynamic_precedence: %d\"];\n".as_ptr().cast::<i8>(),
node_ref.position.extent.row + 1,
node_ref.position.extent.column,
node_ref.node_count,
node_ref.error_cost,
node_ref.dynamic_precedence,
);
for j in 0..node_ref.link_count as usize {
let link = node_ref.links[j];
fprintf(
f,
c"node_%p -> node_%p [".as_ptr().cast::<i8>(),
node.as_ptr().cast::<c_void>(),
link.node.as_ptr().cast::<c_void>(),
);
let subtree = link.subtree;
if !subtree.is_null() && subtree.extra(arena) {
fprintf(f, c"fontcolor=gray ".as_ptr().cast::<i8>());
}
if subtree.is_null() {
fprintf(f, c"color=red".as_ptr().cast::<i8>());
} else {
fprintf(f, c"label=\"".as_ptr().cast::<i8>());
let quoted = subtree.visible(arena) && !subtree.named(arena);
if quoted {
fprintf(f, c"'".as_ptr().cast::<i8>());
}
language_write_symbol_as_dot_string(language, f, subtree.symbol(arena));
if quoted {
fprintf(f, c"'".as_ptr().cast::<i8>());
}
fprintf(f, c"\"".as_ptr().cast::<i8>());
fprintf(
f,
c"labeltooltip=\"error_cost: %u\ndynamic_precedence: %d\""
.as_ptr()
.cast::<i8>(),
subtree.error_cost(arena),
subtree.dynamic_precedence(arena),
);
}
fprintf(f, c"];\n".as_ptr().cast::<i8>());
let next_iterator = if j == 0 {
stack.iterators.get_unchecked_mut(i)
} else {
stack.iterators.push(ptr::read(&iterator));
stack.iterators.last_unchecked_mut()
};
next_iterator.node = link.node;
}
visited_nodes.push(node);
}
if all_iterators_done {
break;
}
}
fprintf(f, c"}\n".as_ptr().cast::<i8>());
visited_nodes.delete();
true
}