Skip to content

Commit 7385756

Browse files
from_dot feature (#1489)
* First draft Built a basic grammar and wired it up with functions to parse it and added it in the pymodule. Next steps: 1) Fix the grammar, make it robust. 2) Add tests and documentations * Updating grammar * from_dot feature * Updates based on comments 1) Added a new function to use stable graph with is_directed as a parameter to avoid creating python object every time. 2) Added more tests to include round trip from to_dot and from_dot for graph and digraph. * fixing clippy * Updates based on review * Fix Clippy warning * Keep MSRV * Add stubs * Only modify deps for pest --------- Co-authored-by: Ivan Carvalho <[email protected]>
1 parent 69af7ff commit 7385756

File tree

10 files changed

+616
-1
lines changed

10 files changed

+616
-1
lines changed

Cargo.lock

Lines changed: 144 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ serde_json = "1.0"
6464
smallvec = { version = "1.0", features = ["union"] }
6565
rustworkx-core = { path = "rustworkx-core", version = "=0.17.1" }
6666
flate2 = "1.0.35"
67+
pest = "=2.7.15"
68+
pest_derive = "=2.7.15"
6769

6870
[dependencies.pyo3]
6971
version = "0.24"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
features:
2+
- |
3+
Added feature for importing graphs from the GraphViz DOT format via the new
4+
:func:`~rustworkx.from_dot` function. This function takes a DOT string and
5+
constructs either a :class:`~rustworkx.PyGraph` or
6+
:class:`~rustworkx.PyDiGraph` object, automatically detecting the graph type
7+
from the DOT input. Node attributes, edge attributes, and graph-level
8+
attributes are preserved.For example::
9+
10+
import rustworkx
11+
12+
dot_str = '''
13+
digraph {
14+
0 [label="a", color=red];
15+
1 [label="b", color=blue];
16+
0 -> 1 [weight=1];
17+
}
18+
'''
19+
g = rustworkx.from_dot(dot_str)
20+
assert len(g.nodes()) == 2
21+
assert len(g.edges()) == 1

rustworkx-core/src/max_weight_matching.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ fn assign_label<E>(
9999
assign_label(
100100
endpoints[mate[&base]],
101101
1,
102-
mate.get(&base).map(|p| (p ^ 1)),
102+
mate.get(&base).map(|p| p ^ 1),
103103
num_nodes,
104104
in_blossoms,
105105
labels,

rustworkx/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ from .rustworkx import GraphMLKey as GraphMLKey
169169
from .rustworkx import digraph_node_link_json as digraph_node_link_json
170170
from .rustworkx import graph_node_link_json as graph_node_link_json
171171
from .rustworkx import from_node_link_json_file as from_node_link_json_file
172+
from .rustworkx import from_dot as from_dot
172173
from .rustworkx import parse_node_link_json as parse_node_link_json
173174
from .rustworkx import digraph_bellman_ford_shortest_paths as digraph_bellman_ford_shortest_paths
174175
from .rustworkx import graph_bellman_ford_shortest_paths as graph_bellman_ford_shortest_paths

rustworkx/rustworkx.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,9 @@ def from_node_link_json_file(
751751
node_attrs: Callable[[dict[str, str]], _S] | None = ...,
752752
edge_attrs: Callable[[dict[str, str]], _T] | None = ...,
753753
) -> PyDiGraph[_S, _T] | PyGraph[_S, _T]: ...
754+
def from_dot(
755+
dot_str: str,
756+
) -> PyDiGraph[_S, _T] | PyGraph[_S, _T]: ...
754757

755758
# Shortest Path
756759

src/dot_parser/dot.pest

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
2+
COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* | "//" ~ (!NEWLINE ~ ANY)* | "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
3+
NEWLINE = _{ "\n" | "\r\n" }
4+
5+
graph_file = { SOI ~ strict? ~ graph_type ~ id? ~ "{" ~ stmt_list? ~ "}" ~ EOI }
6+
7+
strict = { "strict" }
8+
graph_type = { "graph" | "digraph" }
9+
10+
id = _{ number | identifier | quoted_id | html_id }
11+
12+
number = @{ ASCII_DIGIT+ }
13+
identifier = @{ (ASCII_ALPHANUMERIC | "_" | "." | "/" | "\\" | "-")+ }
14+
quoted_id = @{ "\"" ~ (("\\\"" | (!"\"" ~ ANY))*) ~ "\"" }
15+
html_id = @{ "<" ~ (!">" ~ ANY)* ~ ">" }
16+
17+
stmt_list = { (stmt ~ (";" | NEWLINE)*)* }
18+
19+
stmt = _{
20+
edge_stmt
21+
| node_stmt
22+
| attr_stmt
23+
| assignment
24+
| subgraph
25+
}
26+
27+
node_stmt = { node_id ~ attr_list* }
28+
edge_stmt = { edge_point ~ (edge_op ~ edge_point)+ ~ attr_list* }
29+
30+
edge_op = { "->" | "--" }
31+
32+
edge_point = { node_id | subgraph }
33+
34+
attr_stmt = { ("graph" | "node" | "edge") ~ attr_list+ }
35+
36+
attr_list = { "[" ~ a_list? ~ "]" }
37+
a_list = { (id ~ ("=" ~ id)? ~ ("," | ";")?)* }
38+
39+
assignment = { id ~ "=" ~ id }
40+
41+
subgraph = { "subgraph" ~ id? ~ "{" ~ stmt_list? ~ "}" }
42+
43+
node_id = { id ~ (":" ~ id)? ~ (":" ~ id)? }

0 commit comments

Comments
 (0)