New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC RustDOT: graph! { A -- A -- B; A -- C; } #613
Comments
Discovering the sheer power of declarative macros has been an amazing journey. Where this stands, it’s not difficult to add edge defaults and a variant However there are some rough spots, which seem impossible to address:
All of this got me exploring the supposed dragon den of procedural macros. Seems they’re not as daunting as many claim. While it’s probably more work to implement it that way, all of the rough edges would go away. And it would be easy to get even closer to DOT, e.g. use petgraph::csr::digraph;
// IDE can help with exact type returned by macro
pub fn some_name() -> petgraph::csr::Csr<…> {
digraph! {
…
}
} With a new start I would separate this into two layers. A basic macro would return an abstract graph. Since the macro generates this tuple as literal compiler input, arrays don’t need a size declaration. It might look like: (bool, // directed
[(&'static str, N), …], // the nodes with their stringified ID
[(usize, usize, E), …], // the edges, with indices into the node array
) Now
Then other macros, like For example graph! {
foo [ label = "foobar" ];
foo -- foo [ label = "yes" ];
foo -- baz [ label = "maybe" ];
baz -- baz [ label = "no" ];
} could be turned into far less code than I generate now (and much of which could be a helper function, maybe with a let (_, nodes, edges) = (false,
[("foo", "foobar"), ("baz", "baz")],
[(0, 0, "yes"), (0, 1, "maybe"), (1, 1, "no")]
);
let mut graph = petgraph::graph::Graph::<_, _, petgraph::Undirected>::new_undirected();
let nodes: Vec<_> = nodes
.iter()
.map(|node| graph
.add_node(node.1))
.collect();
for edge in edges {
graph
.add_edge(nodes[edge.0], nodes[edge.1], edge.2);
}; |
This has materialised as crate rust_dot. Still a plain macro, not yet a proc-macro, but built around that technology. Turns out this also allows parsing strings or files. |
rust_dot.rs.gz
This is WIP, but already a quite a complete implementation of DOT as native Rust. E.g.:
TODO:
digraph! {}
need to be implemented. The latter is a fairly mechanical translation of half what I have, falling back to the 1st half of_graph!()
as is.&'static str
. That needs some syntax to get$id
intoconcat!("prefix ", $id, " postfix")
. Except the macro input can't specify$id
, which is internal. Maybe something likenode [label = $($prefix:literal)? id $($postfix:literal)?]
andedge [label = $($prefix:literal)? id.0 $($midfix:literal)? id.1 $($postfix:literal)?]
<_, _, Ty>
needs 3, sometimesnew
for undirected sometimesnew_undirected
). Either fix that upstream or (for the time being) do amatch stringify!($type)
, which should boil down to one constructor call at compile time.graph! { self = … }
Please look at the comments and tests at the end and share here what you think! I have tried to be as
'static
as possible, with only one temporary hashmap as an exception. Nonetheless I wonder if this could be more efficient.Edit: Concat ID, as shown above, caused a major rewrite. As a result this now also works better, more consistently. Module docs got a lot better.
The text was updated successfully, but these errors were encountered: