DOT 语言
终结符以粗体显示,非终结符以斜体显示。文字字符用单引号给出。圆括号 (
和 )
在需要时表示分组。方括号 [
和 ]
括起可选项目。竖线 |
分隔备选方案。
graph | : | [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}' |
stmt_list | : | [ stmt [ ';' ] stmt_list ] |
stmt | : | node_stmt |
| | edge_stmt | |
| | attr_stmt | |
| | ID '=' ID | |
| | subgraph | |
attr_stmt | : | (graph | node | edge) attr_list |
attr_list | : | '[' [ a_list ] ']' [ attr_list ] |
a_list | : | ID '=' ID [ (';' | ',') ] [ a_list ] |
edge_stmt | : | (node_id | subgraph) edgeRHS [ attr_list ] |
edgeRHS | : | edgeop (node_id | subgraph) [ edgeRHS ] |
node_stmt | : | node_id [ attr_list ] |
node_id | : | ID [ port ] |
port | : | ':' ID [ ':' compass_pt ] |
| | ':' compass_pt | |
subgraph | : | [ subgraph [ ID ] ] '{' stmt_list '}' |
compass_pt | : | n | ne | e | se | s | sw | w | nw | c | _ |
关键字 node、edge、graph、digraph、subgraph 和 strict 不区分大小写。还要注意,允许的罗盘点值不是关键字,因此这些字符串可以在其他地方用作普通标识符,反之亦然,解析器实际上会接受任何标识符。
ID
ID 是以下之一
- 任何字母 (
[a-zA-Z\200-\377]
)、下划线 ('_'
) 或数字 ([0-9]
) 字符串,但不能以数字开头; - 数字 [
-
]?(.
[0
-9
]⁺|
[0
-9
]⁺(.
[0
-9
]*)? ); - 任何带双引号的字符串 (
"..."
),可能包含转义引号 (\"
)¹; - HTML 字符串 (
<...>
)。
ID 只是一个字符串;前两种形式中缺少引号字符只是为了简单起见。abc_2
和 "abc_2"
之间没有语义差异,2.34
和 "2.34"
之间也没有语义差异。显然,要使用关键字作为 ID,它必须用引号括起来。
HTML 字符串
请注意,在 HTML 字符串中,尖括号必须成对出现,并且允许换行符和其他格式空白字符。此外,内容必须是合法的 XML,因此可能需要使用 ", &, < 和 > 的特殊 XML 转义序列才能将这些字符嵌入属性值或原始文本中。作为 ID,HTML 字符串可以是任何合法的 XML 字符串。但是,如果用作 label 属性,它会以特殊方式解释,并且必须遵循 HTML 类标签 的语法。
带引号的字符串和 HTML 字符串都作为一个单元进行扫描,因此任何嵌入的注释都将被视为字符串的一部分。
边操作 (edgeops)
edgeop 在有向图中为 ->
,在无向图中为 --
。
注释和可选格式
该语言支持 C++ 风格的注释:/* */
和 //
。此外,以 '#' 字符开头的行被认为是来自 C 预处理器的行输出(例如,# 34 表示第 34 行),并且会被丢弃。
分号和逗号有助于可读性,但不是必需的。此外,可以在终结符之间插入任意数量的空格。
作为另一个提高可读性的辅助手段,dot 允许带双引号的字符串使用标准 C 约定(在换行符之前直接加反斜杠)跨越多个物理行²。此外,带双引号的字符串可以使用 '+' 运算符连接。由于 HTML 字符串可以包含换行符,这些换行符仅用于格式化,因此该语言不允许在其中使用转义换行符或连接运算符。
子图和集群
子图在 Graphviz 中起着三种作用。首先,子图可以用来表示图结构,表明某些节点和边应该分组在一起。这是子图的通常作用,通常指定有关图组件的语义信息。它还可以提供边缘的便捷简写。边缘语句允许子图出现在边缘运算符的左侧和右侧。发生这种情况时,将从左侧的每个节点创建到右侧的每个节点的边。例如,规范
A -> {B C}
等同于
A -> B
A -> C
在第二种作用中,子图可以提供设置属性的上下文。例如,子图可以指定蓝色是其中定义的所有节点的默认颜色。在图形绘制的上下文中,一个更有趣的例子是
subgraph {
rank = same; A; B; C;
}
这个(匿名)子图指定节点 A、B 和 C 应该在使用 dot 绘制时全部放置在同一层级上。
子图的第三种作用直接涉及某些布局引擎如何布局图形。如果子图的名称以 cluster
开头,Graphviz 会将子图标记为特殊的 集群 子图。如果支持,布局引擎将进行布局,以便属于集群的节点绘制在一起,整个集群的绘制包含在一个边界矩形内。请注意,好坏参半,集群子图不是 DOT 语言的一部分,而仅仅是某些布局引擎遵守的句法约定。
词法和语义说明
必须将图指定为**有向图**或**无向图**。语义上,这表示边的一个节点到另一个节点是否有自然方向。词法上,有向图必须使用边运算符->
指定边,而无向图必须使用--
。在操作上,这种区别用于定义不同的默认渲染属性。例如,默认情况下,有向图中的边将使用指向头部节点的箭头绘制。对于普通图,默认情况下,边不带箭头绘制。
图也可以描述为**严格**的。这禁止创建多边,即在有向情况下,最多只能有一条边具有给定的尾节点和头节点。对于无向图,最多只能有一条边连接到同一对节点。随后使用同一对节点的边语句将识别先前定义的边,并应用边语句中给出的任何属性。例如,图
strict graph {
a -- b
a -- b
b -- a [color=blue]
}
将有一条连接节点a
和b
的边,其颜色为蓝色。
如果使用**节点**、**边**或**图**语句定义了默认属性,或通过未附加到节点或边的属性赋值定义了默认属性,则随后定义的任何相应类型的对象将继承此属性值。这将持续到默认属性被设置为新值,从那时起,将使用新值。在设置默认属性之前定义的对象,一旦默认属性定义完成,将具有附加到属性的空字符串值。
请注意,子图在定义时会接收其父图的属性设置。这很有用;例如,可以为根图分配字体,所有子图也将使用该字体。但是,对于某些属性,此属性不可取。如果将标签附加到根图,则使用所有子图的标签可能不是预期效果。与其在图的顶部列出图属性,并在子图中根据需要重置属性,不如简单地推迟图中的属性定义,直到定义了适当的子图。
如果边属于集群,则其端点属于该集群。因此,放置边的位置会影响布局,因为集群有时会递归地布局。
子图和集群有一些限制。首先,目前,图及其子图的名称共享同一个命名空间。因此,每个子图都必须具有唯一的名称。其次,尽管节点可以属于任意数量的子图,但假设集群在被视为节点和边的子集时形成严格的层次结构。
字符编码
DOT 语言至少假设使用ASCII 字符集。普通字符串和 HTML 风格的字符串都可以包含非 ASCII 字符。在大多数情况下,这些字符串未解释:它们只是作为未经修改传递的唯一标识符或值。但是,标签旨在显示,这需要软件能够计算文本的大小并确定适当的字形。为此,它需要知道使用的是什么字符编码。
默认情况下,DOT 假设使用UTF-8 字符编码。它还接受Latin1 (ISO-8859-1) 字符集,假设输入图使用**charset** 属性来指定这一点。对于使用其他字符集的图,通常会有像iconv
这样的程序可以将字符集从一种转换为另一种。
避免标签中出现非 ASCII 字符的另一种方法是使用特殊字符的 HTML 实体。在标签评估期间,这些实体将被转换为底层字符。此表格显示了支持的实体,以及它们的 Unicode 值、典型字形和 HTML 实体名称。因此,要在字符串中包含小写希腊字母 beta,可以使用 ASCII 序列β
。通常,应只使用输出字符集中允许的实体,并且对于这些实体,字体中存在字形。
- 在 DOT 中的引号字符串中,唯一转义的字符是双引号
"
。也就是说,在引号字符串中,二元组\"
将转换为"
;所有其他字符保持不变。特别是,\\
仍然是\\
。布局引擎可能会应用其他转义序列。 - 在 2.30 之前,该语言允许在 HTML 字符串之外的任何地方使用转义的换行符。新的基于 lex 的扫描器使得这难以实现。鉴于人们认为这种通用性缺乏用处,我们已将此功能限制在双引号字符串中,在这些字符串中,它实际上可能会有帮助。