DFS with repeats + cycle detection

This commit is contained in:
Noah Santschi-Cooney 2020-07-26 00:45:11 +01:00
parent ef72db8b6e
commit 0028e0ac9a
No known key found for this signature in database
GPG key ID: 3B22282472C8AE48
6 changed files with 304 additions and 53 deletions

37
server/Cargo.lock generated
View file

@ -23,6 +23,19 @@ name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-vec"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
@ -329,6 +342,24 @@ dependencies = [
"remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "1.0.1"
@ -379,6 +410,7 @@ name = "vscode-mc-shader"
version = "0.1.0"
dependencies = [
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chan 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
"fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -389,6 +421,7 @@ dependencies = [
"serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -435,6 +468,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chan 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "d14956a3dae065ffaa0d92ece848ab4ced88d32361e7fdfbfd653a5c454a1ed8"
@ -475,6 +510,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum serde_repr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
"checksum syn 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
"checksum thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"

View file

@ -6,8 +6,8 @@ edition = "2018"
[dependencies]
rust_lsp = { git = "https://github.com/Strum355/RustLSP", branch = "master" }
serde_json = "1.0.55"
serde = "1.0.111"
serde_json = "1.0.56"
serde = "1.0.114"
walkdir = "2.3.1"
petgraph = "0.5.1"
lazy_static = "1.4.0"
@ -16,6 +16,8 @@ chan = "0.1.23"
url = "2.1.1"
percent-encoding = "2.1.0"
anyhow = "1.0.31"
bit-set = "0.5.2"
thiserror = "1.0.20"
[dev-dependencies]
tempdir = "0.3.7"

View file

@ -1,17 +1,104 @@
use petgraph::stable_graph::NodeIndex;
use thiserror::Error;
use crate::graph::CachedStableGraph;
use anyhow::{Result, Context, format_err};
use std::fmt::{Debug, Display};
/// Performs a depth-first search with duplicates
pub struct Dfs<'a> {
stack: Vec<NodeIndex>,
graph: &'a CachedStableGraph
graph: &'a CachedStableGraph,
cycle: Vec<NodeIndex>
}
impl <'a> Dfs<'a> {
pub fn new(graph: &'a CachedStableGraph, start: NodeIndex) -> Self {
Dfs {
stack: vec![start],
graph
graph,
cycle: Vec::new()
}
}
pub fn next(&mut self) -> Option<Result<NodeIndex>> {
if let Some(node) = self.stack.pop() {
self.cycle.push(node);
let mut children = self.graph.child_node_indexes(node);
if !children.is_empty() {
// sort by line number in parent
children.sort_by(|x, y| {
let graph = &self.graph.graph;
let edge1 = graph.edge_weight(graph.find_edge(node, *x).unwrap()).unwrap();
let edge2 = graph.edge_weight(graph.find_edge(node, *y).unwrap()).unwrap();
edge2.line.cmp(&edge1.line)
});
match self.check_for_cycle(&children) {
Ok(_) => {}
Err(e) => return Some(Err(e)),
};
for child in children {
self.stack.push(child);
}
} else {
self.cycle.pop();
}
return Some(Ok(node));
}
None
}
fn check_for_cycle(&self, children: &[NodeIndex]) -> Result<()> {
for prev in &self.cycle {
for child in children {
if *prev == *child {
return Err(
format_err!("cycle detected")
).with_context(||
CycleError::new(&self.cycle, *child, self.graph)
);
}
}
}
Ok(())
}
}
#[derive(Debug, Error)]
pub struct CycleError(Vec<String>);
impl CycleError {
fn new(nodes: &[NodeIndex], current_node: NodeIndex, graph: &CachedStableGraph) -> Self {
let mut resolved_nodes: Vec<String> = nodes.iter().map(|i| graph.get_node(*i).clone()).collect();
resolved_nodes.push(graph.get_node(current_node).clone());
CycleError(resolved_nodes)
}
}
impl Display for CycleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
eprintln!("cycle path {:?}", self.0);
let mut disp = String::new();
disp.push_str(format!("Include cycle detected:\n{} imports ", self.0[0]).as_str());
for p in &self.0[1..self.0.len()-1] {
disp.push_str(format!("\n{}, which imports ", *p).as_str());
}
disp.push_str(format!("\n{}", self.0[self.0.len()-1]).as_str());
f.write_str(disp.as_str())
}
}
impl Into<String> for CycleError {
fn into(self) -> String {
format!("{}", self)
}
}

View file

@ -50,9 +50,9 @@ impl CachedStableGraph {
}
}
/* pub fn get_node(&self, node: NodeIndex) -> &IncludePosition {
self.graph.node_weight(node).expect("node index not found in graph")
} */
pub fn get_node(&self, node: NodeIndex) -> &String {
&self.graph[node]
}
pub fn remove_node(&mut self, name: impl Into<String>) {
let idx = self.cache.remove(&name.into());
@ -100,7 +100,6 @@ impl CachedStableGraph {
pub fn collect_root_ancestors(&self, node: NodeIndex) -> Vec<NodeIndex> {
let mut visited = HashSet::new();
//visited.insert(node);
self.get_root_ancestors(node, node, &mut visited)
}

View file

@ -15,8 +15,6 @@ use std::ops::Add;
use std::process;
use std::rc::Rc;
use core::cmp::{Ordering, PartialOrd, PartialEq, Ord, Eq};
use anyhow::Result;
use chan::WaitGroup;
@ -84,29 +82,16 @@ struct MinecraftShaderLanguageServer {
struct Configuration {
glslang_validator_path: String,
shaderpacks_path: String,
}
impl Default for Configuration {
fn default() -> Self {
let shaderpacks_path = std::env::var("HOME").unwrap() + "/.minecraft/shaderpacks";
Configuration{
glslang_validator_path: "glslangValidator".into(),
shaderpacks_path
}
}
}
impl Configuration {
fn validate(&self) -> bool {
if self.glslang_validator_path == "" || self.shaderpacks_path == "" {
return false;
}
true
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct IncludePosition {
filepath: String,
@ -121,26 +106,16 @@ impl Display for IncludePosition {
}
}
impl PartialOrd for IncludePosition {
#[allow(clippy::comparison_chain)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.line < other.line { Some(Ordering::Less) }
else if self.line > other.line { Some(Ordering::Greater) }
else { Some(Ordering::Equal) }
}
}
impl Ord for IncludePosition {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
struct GLSLFile {
idx: petgraph::graph::NodeIndex,
includes: Vec<IncludePosition>,
}
struct MergeMeta {
node: NodeIndex,
last_slice: u64
}
impl MinecraftShaderLanguageServer {
pub fn error_not_available<DATA>(data: DATA) -> MethodError<DATA> {
let msg = "Functionality not implemented.".to_string();
@ -279,8 +254,22 @@ impl MinecraftShaderLanguageServer {
} else {
let mut lists = Vec::with_capacity(file_ancestors.len());
for root in file_ancestors {
let (_, views) = self.generate_merge_list(root);
lists.push(views);
match self.generate_merge_list(root) {
Ok((sources, views)) => lists.push(views),
Err(e) => {
eprintln!("cycle detected");
let e = e.downcast::<dfs::CycleError>().unwrap();
return Ok(vec![Diagnostic{
severity: Some(DiagnosticSeverity::Error),
range: Range::new(Position::new(0, 0), Position::new(0, 500)),
source: Some(SOURCE.into()),
message: e.into(),
code: None,
tags: None,
related_information: None,
}])
}
}
}
lists
};
@ -361,11 +350,25 @@ impl MinecraftShaderLanguageServer {
Ok(Some(roots))
}
fn generate_merge_list(&self, root: NodeIndex) -> (LinkedList<String>, LinkedList<&str>) {
fn generate_merge_list(&self, root: NodeIndex) -> Result<(LinkedList<String>, LinkedList<&str>)> {
let mut merge_list = LinkedList::new();
// need to return all sources along with the views to appease the lifetime god
let mut all_sources = LinkedList::new();
let graph_ref = self.graph.borrow();
let mut dfs = dfs::Dfs::new(&graph_ref, root);
//let slice_stack = Vec::new();
while let Some(n) = dfs.next() {
if n.is_err() {
return Err(n.err().unwrap());
}
/* let path = self.graph.borrow().get_node(n);
let file_content = String::from_utf8(std::fs::read(path).unwrap()); */
}
/* let children = self.graph.borrow().child_node_indexes(root);
// include positions sorted by earliest in file
@ -373,7 +376,7 @@ impl MinecraftShaderLanguageServer {
all_edges.sort();
eprintln!("include positions for {:?}: {:?} {:?}", self.graph.borrow().graph.node_weight(root).unwrap(), all_edges, children); */
(all_sources, merge_list)
Ok((all_sources, merge_list))
}
fn invoke_validator(&self, source: &str) -> Result<String> {
@ -495,13 +498,6 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
.as_str()
.unwrap()
.into();
self.config.shaderpacks_path = config.get("shaderpacksPath").unwrap().as_str().unwrap().into();
if !self.config.validate() {
self.endpoint
.send_notification("badConfig", None::<()>)
.unwrap();
}
eprintln!("{:?}", params.settings.as_object().unwrap());

View file

@ -143,10 +143,8 @@ fn test_01_initialize() {
fn test_graph_two_connected_nodes() {
let mut graph = graph::CachedStableGraph::new();
graph.add_node("sample");
graph.add_node("banana");
let idx1 = graph.find_node("sample").unwrap();
let idx2 = graph.find_node("banana").unwrap();
let idx1 = graph.add_node("sample");
let idx2 = graph.add_node("banana");
graph.add_edge(idx1, idx2, 3, 10, 50);
let children = graph.child_node_names(idx1);
@ -181,3 +179,135 @@ fn test_graph_two_connected_nodes() {
let neighbors = graph.child_node_names(idx2);
assert_eq!(neighbors.len(), 0);
}
#[test]
fn test_collect_root_ancestors() {
let mut graph = graph::CachedStableGraph::new();
let idx0 = graph.add_node("0");
let idx1 = graph.add_node("1");
let idx2 = graph.add_node("2");
graph.add_edge(idx0, idx1, 2, 0, 0);
graph.add_edge(idx1, idx2, 3, 0, 0);
graph.add_edge(idx2, idx0, 5, 0, 0);
let roots = graph.collect_root_ancestors(idx0);
assert_eq!(roots, vec![idx2]);
}
#[test]
fn test_graph_dfs() {
{
let mut graph = graph::CachedStableGraph::new();
let idx0 = graph.add_node("0");
let idx1 = graph.add_node("1");
let idx2 = graph.add_node("2");
let idx3 = graph.add_node("3");
let idx4 = graph.add_node("4");
let idx5 = graph.add_node("5");
let idx6 = graph.add_node("6");
let idx7 = graph.add_node("7");
graph.add_edge(idx0, idx1, 2, 0, 0);
graph.add_edge(idx0, idx2, 3, 0, 0);
graph.add_edge(idx1, idx3, 5, 0, 0);
graph.add_edge(idx1, idx4, 6, 0, 0);
graph.add_edge(idx2, idx4, 5, 0, 0);
graph.add_edge(idx2, idx5, 4, 0, 0);
graph.add_edge(idx3, idx6, 4, 0, 0);
graph.add_edge(idx4, idx6, 4, 0, 0);
graph.add_edge(idx6, idx7, 4, 0, 0);
let mut dfs = dfs::Dfs::new(&graph, idx0);
let mut collection = Vec::new();
while let Some(i) = dfs.next() {
assert_eq!(i.is_ok(), true);
collection.push(i.unwrap());
}
// 0
// / \
// 1 2
// / \ / \
// 3 4 5
// \ /
// 6 - 7
let expected = vec![idx0, idx1, idx3, idx6, idx7, idx4, idx6, idx7, idx2, idx5, idx4, idx6, idx7];
println!("{:?}\n{:?}", expected, collection);
collection.reverse();
for i in expected {
assert_eq!(i, collection.pop().unwrap());
}
assert!(!is_cyclic_directed(&graph.graph));
}
}
#[test]
fn test_graph_dfs_cycle() {
{
let mut graph = graph::CachedStableGraph::new();
let idx0 = graph.add_node("0");
let idx1 = graph.add_node("1");
let idx2 = graph.add_node("2");
let idx3 = graph.add_node("3");
let idx4 = graph.add_node("4");
let idx5 = graph.add_node("5");
let idx6 = graph.add_node("6");
let idx7 = graph.add_node("7");
graph.add_edge(idx0, idx1, 2, 0, 0);
graph.add_edge(idx0, idx2, 3, 0, 0);
graph.add_edge(idx1, idx3, 5, 0, 0);
graph.add_edge(idx1, idx4, 6, 0, 0);
graph.add_edge(idx2, idx4, 5, 0, 0);
graph.add_edge(idx2, idx5, 4, 0, 0);
graph.add_edge(idx3, idx6, 4, 0, 0);
graph.add_edge(idx4, idx6, 4, 0, 0);
graph.add_edge(idx6, idx7, 4, 0, 0);
graph.add_edge(idx7, idx4, 4, 0, 0);
let mut dfs = dfs::Dfs::new(&graph, idx0);
for _ in 0..5 {
if let Some(i) = dfs.next() {
assert_eq!(i.is_ok(), true);
}
}
// 0
// / \
// 1 2
// / \ / \
// 3 4 5
// \ / \
// 6 - 7
assert!(is_cyclic_directed(&graph.graph));
let next = dfs.next().unwrap();
assert_eq!(next.is_err(), true);
}
{
let mut graph = graph::CachedStableGraph::new();
let idx0 = graph.add_node("0");
let idx1 = graph.add_node("1");
graph.add_edge(idx0, idx1, 2, 0, 0);
graph.add_edge(idx1, idx0, 2, 0, 0);
let mut dfs = dfs::Dfs::new(&graph, idx1);
println!("{:?}", dfs.next());
println!("{:?}", dfs.next());
println!("{:?}", dfs.next());
}
}