#graph #graphviz #dot

rust_dot

RustDOT 主要基于 Graphviz DOT 语言,并轻微地 Rust 化。

6 个版本 (破坏性更新)

0.6.0 2024 年 5 月 3 日
0.5.1 2024 年 4 月 8 日
0.4.0 2024 年 4 月 2 日
0.3.0 2024 年 3 月 27 日
0.1.0 2024 年 2 月 18 日

#464 in 解析器实现

每月 35 次下载

MIT/Apache

42KB
795

RustDOT 主要基于 Graphviz DOT 语言,并轻微地 Rust 化。它可以嵌入为宏或从字符串或文件中解析。目的是提取结构。布局提示目前不在此范围内。

let g1 = rust_dot! {
    graph {
        A -- B -- C; /* semicolon is optional */
        "B" -- D // quotes not needed here
    }
};
println!("{} {} \"{}\" {:?} {:?}", g1.strict, g1.directed, g1.name, g1.nodes, g1.edges);
// false false "" ["A", "B", "C", "D"] [(0, 1), (1, 2), (1, 3)]

let g2 = parse_string("digraph Didi { -1 -> 2 -> .3  2 -> 4.2 }");
println!("{} {} \"{}\" {:?} {:?}", g2.strict, g2.directed, g2.name, g2.nodes, g2.edges);
// false true "Didi" ["-1", "2", ".3", "4.2"] [(0, 1), (1, 2), (1, 3)]

返回值可以传递给 petgraph

let mut petgraph = petgraph::graph::Graph::new();
let nodes: Vec<_> = rust_dot_graph.nodes
    .iter()
    .map(|node| petgraph
        .add_node(node))
    .collect();
for edge in rust_dot_graph.edges {
    petgraph
        .add_edge(nodes[edge.0], nodes[edge.1], ());
};

graph/graph_builder

use graph::prelude::*;

let graph: DirectedCsrGraph<usize> = GraphBuilder::new()
    .csr_layout(CsrLayout::Sorted)
    .edges(rust_dot_graph.edges)
    .build();

这是一个正在进行中的工作。没有什么是稳定的!

待办事项

  • 实现 strict,目前被忽略/跳过

  • 在错误输入上返回 Err 而不是恐慌

  • 在词素上放置 Span,基于它们的输入,可能使用 crate 宏

  • 分离返回类型(目前为 Parser,应该是内部的)

  • 实现节点属性,目前被忽略/跳过

  • 实现节点默认值

  • 实现边属性,目前被忽略/跳过

  • 实现边默认值

  • 处理图属性,有和没有关键词 graph

  • rust_dot 重新实现为 proc-macro,在编译时将其输入转换为 const

  • 作为 DOT 的扩展,允许标签或权重来自 Rust 表达式

  • 作为 DOT 的扩展,允许标签或权重来自调用闭包

限制

Rust 宏由 Rust 词汇分析器分析,这与 Graphviz 微妙不同。为了保持一致性(和易于实现),parse_* 函数使用相同的词汇分析器。这些是后果

  • 宏必须是 UTF-8,而 parse_* 函数的输入可以是 UTF-16 或 Latin-1。你必须自己处理其他编码。
  • 双引号、括号、花括号和方括号必须平衡,并且某些字符是不允许的。作为一个变通方法,你可以首先将以下第一行更改为第二行。注释引号被 Rust 看到但被忽略作为 HTML(一旦实现)
    <<I>"</I> <B> )}] [{( </B> \\>
    <<I>"<!--"--></I> <B><!--"--> )}]  [{( <!--"--></B> <!--"-->\\<!--"-->>
    
  • HTML是一种部分考虑空格的语言,而Rust则不是。所以在宏层面,正确处理空格是不可能的,在运行时输入处理也会相当费力。因此,这里采用了一种对所有元素之间空格的启发式处理方法,除了在标签和实体内部以及以下代码(不适用于某些语言):
  • 当我们接收到字符串时,字符串尚未进行转义,但Rust词法分析器会对其进行验证。函数 parse_* 通过这种方法绕过了这个问题,但在 rust_dot! 中,如果字符串包含非Rust转义序列,则必须使用原始字符串,例如:
  • 注释与Rust注释完全相同。它们与DOT的不同之处在于,块注释可以嵌套。
  • 虽然不是官方注释,但同一行上位于 # 之后的所有内容也会被丢弃。与真实注释不同,这些由RustDOT在词法分析之后处理。这意味着该行的其余部分,例如上面的第一个点,必须是平衡的。它将仅在结束分隔符之后结束,因此您应该将其放在同一行上!在 rust_dot! 中,必须使用 // 代替!(只有夜间编译器才能在宏中访问行号。)
  • Rust应该接受有效的标识符。尽管(仅在 rust_dot! 中)混淆字母,如西里尔字母“о”或罕见脚本,如如尼文,会发出警告。
  • Rust应该接受有效的数字。并且浮点数不需要在十进制点之前有前导零。
  • RustDOT返回一个图,因此它希望输入一个图。语法没有明确说明每个文件可以包含多个图,但它们是可接受的。然而,它们会导致一个文件中无效地连接了2个svg,或者一个只显示第一个的png。同样,它也接受空文档——这不是RustDOT所希望的。

依赖关系

~80KB