Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit 9a30497

Browse files
committed
Add Lua wrappers for edges
You can't do anything with them other than call `tostring` on them, but even that is helpful in test cases!
1 parent 9ae6a87 commit 9a30497

2 files changed

Lines changed: 164 additions & 34 deletions

File tree

stack-graphs/src/lua.rs

Lines changed: 127 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@
6868
//!
6969
//! The following Lua methods are available on a stack graph instance:
7070
//!
71+
//! #### `edges`
72+
//!
73+
//! ``` lua
74+
//! let edges = graph:edges()
75+
//! ```
76+
//!
77+
//! Returns an array containing all of the edges in the graph.
78+
//!
7179
//! #### `file`
7280
//!
7381
//! ``` lua
@@ -123,6 +131,14 @@
123131
//!
124132
//! Adds a new drop scopes node to this file.
125133
//!
134+
//! #### `edges`
135+
//!
136+
//! ``` lua
137+
//! let edges = file:edges()
138+
//! ```
139+
//!
140+
//! Returns an array containing all of the edges starting from or leaving a node in this file.
141+
//!
126142
//! #### `exported_scope_node`
127143
//!
128144
//! ``` lua
@@ -266,6 +282,14 @@
266282
//!
267283
//! Returns the local ID of this node within its file.
268284
//!
285+
//! #### `outgoing_edges`
286+
//!
287+
//! ``` lua
288+
//! let edges = node:outgoing_edges()
289+
//! ```
290+
//!
291+
//! Returns an array containing all of the edges leaving this node.
292+
//!
269293
//! #### `set_debug_info`
270294
//!
271295
//! ``` lua
@@ -381,12 +405,26 @@ use mlua::UserData;
381405
use mlua::UserDataMethods;
382406

383407
use crate::arena::Handle;
408+
use crate::graph::Edge;
384409
use crate::graph::File;
385410
use crate::graph::Node;
386411
use crate::graph::StackGraph;
387412

388413
impl UserData for StackGraph {
389414
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
415+
methods.add_function("edges", |l, graph_ud: AnyUserData| {
416+
let graph = graph_ud.borrow::<StackGraph>()?;
417+
let mut edges = Vec::new();
418+
for node in graph.iter_nodes() {
419+
for edge in graph.outgoing_edges(node) {
420+
let edge_ud = l.create_userdata(edge)?;
421+
edge_ud.set_user_value(graph_ud.clone())?;
422+
edges.push(edge_ud);
423+
}
424+
}
425+
Ok(edges)
426+
});
427+
390428
methods.add_function("file", |l, (graph_ud, name): (AnyUserData, String)| {
391429
let file = {
392430
let mut graph = graph_ud.borrow_mut::<StackGraph>()?;
@@ -476,6 +514,39 @@ impl UserData for Handle<File> {
476514
Ok(node_ud)
477515
});
478516

517+
methods.add_function("edges", |l, file_ud: AnyUserData| {
518+
let file = *file_ud.borrow::<Handle<File>>()?;
519+
let graph_ud = file_ud.user_value::<AnyUserData>()?;
520+
let graph = graph_ud.borrow::<StackGraph>()?;
521+
let mut edges = Vec::new();
522+
// First find any edges from the singleton nodes _to_ a node in this file.
523+
for edge in graph.outgoing_edges(StackGraph::root_node()) {
524+
if !graph[edge.sink].file().map(|f| f == file).unwrap_or(false) {
525+
continue;
526+
}
527+
let edge_ud = l.create_userdata(edge)?;
528+
edge_ud.set_user_value(graph_ud.clone())?;
529+
edges.push(edge_ud);
530+
}
531+
for edge in graph.outgoing_edges(StackGraph::jump_to_node()) {
532+
if !graph[edge.sink].file().map(|f| f == file).unwrap_or(false) {
533+
continue;
534+
}
535+
let edge_ud = l.create_userdata(edge)?;
536+
edge_ud.set_user_value(graph_ud.clone())?;
537+
edges.push(edge_ud);
538+
}
539+
// Then find any edges _starting_ from a node in this file.
540+
for node in graph.nodes_for_file(file) {
541+
for edge in graph.outgoing_edges(node) {
542+
let edge_ud = l.create_userdata(edge)?;
543+
edge_ud.set_user_value(graph_ud.clone())?;
544+
edges.push(edge_ud);
545+
}
546+
}
547+
Ok(edges)
548+
});
549+
479550
methods.add_function("exported_scope_node", |l, file_ud: AnyUserData| {
480551
let file = *file_ud.borrow::<Handle<File>>()?;
481552
let graph_ud = file_ud.user_value::<AnyUserData>()?;
@@ -729,27 +800,45 @@ impl UserData for Handle<Node> {
729800
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
730801
methods.add_function(
731802
"add_edge_from",
732-
|_, (this_ud, from_ud, precedence): (AnyUserData, AnyUserData, Option<i32>)| {
803+
|l, (this_ud, from_ud, precedence): (AnyUserData, AnyUserData, Option<i32>)| {
733804
let this = *this_ud.borrow::<Handle<Node>>()?;
734805
let from = *from_ud.borrow::<Handle<Node>>()?;
735806
let graph_ud = this_ud.user_value::<AnyUserData>()?;
736-
let mut graph = graph_ud.borrow_mut::<StackGraph>()?;
737807
let precedence = precedence.unwrap_or(0);
738-
graph.add_edge(from, this, precedence);
739-
Ok(())
808+
{
809+
let mut graph = graph_ud.borrow_mut::<StackGraph>()?;
810+
graph.add_edge(from, this, precedence);
811+
}
812+
let edge = Edge {
813+
source: from,
814+
sink: this,
815+
precedence,
816+
};
817+
let edge_ud = l.create_userdata(edge)?;
818+
edge_ud.set_user_value(graph_ud)?;
819+
Ok(edge_ud)
740820
},
741821
);
742822

743823
methods.add_function(
744824
"add_edge_to",
745-
|_, (this_ud, to_ud, precedence): (AnyUserData, AnyUserData, Option<i32>)| {
825+
|l, (this_ud, to_ud, precedence): (AnyUserData, AnyUserData, Option<i32>)| {
746826
let this = *this_ud.borrow::<Handle<Node>>()?;
747827
let to = *to_ud.borrow::<Handle<Node>>()?;
748828
let graph_ud = this_ud.user_value::<AnyUserData>()?;
749-
let mut graph = graph_ud.borrow_mut::<StackGraph>()?;
750829
let precedence = precedence.unwrap_or(0);
751-
graph.add_edge(this, to, precedence);
752-
Ok(())
830+
{
831+
let mut graph = graph_ud.borrow_mut::<StackGraph>()?;
832+
graph.add_edge(this, to, precedence);
833+
}
834+
let edge = Edge {
835+
source: this,
836+
sink: to,
837+
precedence,
838+
};
839+
let edge_ud = l.create_userdata(edge)?;
840+
edge_ud.set_user_value(graph_ud)?;
841+
Ok(edge_ud)
753842
},
754843
);
755844

@@ -786,6 +875,19 @@ impl UserData for Handle<Node> {
786875
Ok(graph[node].id().local_id())
787876
});
788877

878+
methods.add_function("outgoing_edges", |l, node_ud: AnyUserData| {
879+
let node = *node_ud.borrow::<Handle<Node>>()?;
880+
let graph_ud = node_ud.user_value::<AnyUserData>()?;
881+
let graph = graph_ud.borrow::<StackGraph>()?;
882+
let mut edges = Vec::new();
883+
for edge in graph.outgoing_edges(node) {
884+
let edge_ud = l.create_userdata(edge)?;
885+
edge_ud.set_user_value(graph_ud.clone())?;
886+
edges.push(edge_ud);
887+
}
888+
Ok(edges)
889+
});
890+
789891
methods.add_function(
790892
"set_debug_info",
791893
|_, (node_ud, k, v): (AnyUserData, String, String)| {
@@ -894,3 +996,20 @@ impl UserData for Handle<Node> {
894996
});
895997
}
896998
}
999+
1000+
impl UserData for Edge {
1001+
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
1002+
methods.add_meta_function(mlua::MetaMethod::ToString, |_, edge_ud: AnyUserData| {
1003+
let edge = *edge_ud.borrow::<Edge>()?;
1004+
let graph_ud = edge_ud.user_value::<AnyUserData>()?;
1005+
let graph = graph_ud.borrow::<StackGraph>()?;
1006+
let display = format!(
1007+
"{} -{}-> {}",
1008+
edge.source.display(&graph),
1009+
edge.precedence,
1010+
edge.sink.display(&graph),
1011+
);
1012+
Ok(display)
1013+
});
1014+
}
1015+
}

stack-graphs/tests/it/lua.rs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
66
// ------------------------------------------------------------------------------------------------
77

8-
use std::collections::HashSet;
9-
10-
use maplit::hashset;
118
use stack_graphs::graph::NodeID;
129
use stack_graphs::graph::StackGraph;
1310

@@ -66,6 +63,11 @@ const TEST_PRELUDE: &str = r#"
6663
end
6764
end
6865
66+
function values(t)
67+
local i = 0
68+
return function() i = i + 1; return t[i] end
69+
end
70+
6971
function iter_tostring(...)
7072
local result = {}
7173
for element in ... do
@@ -225,34 +227,43 @@ fn can_create_edges_from_lua() -> Result<(), anyhow::Error> {
225227
&mut graph,
226228
r#"
227229
local graph = ...
230+
local root = graph:root_node()
228231
local file = graph:file("test.py")
229232
local n0 = file:internal_scope_node()
230233
local n1 = file:internal_scope_node()
231-
n0:add_edge_to(n1)
232-
n0:add_edge_from(n1, 10)
234+
local e0 = n0:add_edge_to(n1)
235+
local e1 = n0:add_edge_from(n1, 10)
236+
local e2 = n0:add_edge_to(root)
237+
local e3 = n0:add_edge_from(root)
238+
assert_eq("edge", "[test.py(0) scope] -0-> [test.py(1) scope]", tostring(e0))
239+
assert_eq("edge", "[test.py(1) scope] -10-> [test.py(0) scope]", tostring(e1))
240+
241+
assert_deepeq("node edges", {
242+
"[test.py(0) scope] -0-> [root]",
243+
"[test.py(0) scope] -0-> [test.py(1) scope]",
244+
}, iter_tostring(values(n0:outgoing_edges())))
245+
assert_deepeq("node edges", {
246+
"[test.py(1) scope] -10-> [test.py(0) scope]",
247+
}, iter_tostring(values(n1:outgoing_edges())))
248+
assert_deepeq("node edges", {
249+
"[root] -0-> [test.py(0) scope]",
250+
}, iter_tostring(values(root:outgoing_edges())))
251+
252+
assert_deepeq("file edges", {
253+
"[root] -0-> [test.py(0) scope]",
254+
"[test.py(0) scope] -0-> [root]",
255+
"[test.py(0) scope] -0-> [test.py(1) scope]",
256+
"[test.py(1) scope] -10-> [test.py(0) scope]",
257+
}, iter_tostring(values(file:edges())))
258+
259+
assert_deepeq("graph edges", {
260+
"[root] -0-> [test.py(0) scope]",
261+
"[test.py(0) scope] -0-> [root]",
262+
"[test.py(0) scope] -0-> [test.py(1) scope]",
263+
"[test.py(1) scope] -10-> [test.py(0) scope]",
264+
}, iter_tostring(values(graph:edges())))
233265
"#,
234266
)?;
235-
236-
let file = graph.get_file("test.py").expect("Cannot find file");
237-
let n0 = graph
238-
.node_for_id(NodeID::new_in_file(file, 0))
239-
.expect("Cannot find node 0");
240-
let n1 = graph
241-
.node_for_id(NodeID::new_in_file(file, 1))
242-
.expect("Cannot find node 1");
243-
244-
let edges_from_n0 = graph
245-
.outgoing_edges(n0)
246-
.map(|edge| (edge.sink, edge.precedence))
247-
.collect::<HashSet<_>>();
248-
assert_eq!(edges_from_n0, hashset! {(n1, 0)});
249-
250-
let edges_from_n1 = graph
251-
.outgoing_edges(n1)
252-
.map(|edge| (edge.sink, edge.precedence))
253-
.collect::<HashSet<_>>();
254-
assert_eq!(edges_from_n1, hashset! {(n0, 10)});
255-
256267
Ok(())
257268
}
258269

0 commit comments

Comments
 (0)