Skip to content
This repository was archived by the owner on Oct 17, 2021. It is now read-only.

Commit 03405c1

Browse files
committed
Escape DOT keywords
1 parent ece6fd1 commit 03405c1

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

Sources/DOT/DOT.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
The following is an abstract grammar defining the DOT language.
3+
4+
Terminals are shown in bold font and nonterminals in italics.
5+
Literal characters are given in single quotes.
6+
Parentheses ( and ) indicate grouping when needed.
7+
Square brackets [ and ] enclose optional items.
8+
Vertical bars | separate alternatives.
9+
10+
graph : [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}'
11+
stmt_list : [ stmt [ ';' ] stmt_list ]
12+
stmt : node_stmt
13+
| edge_stmt
14+
| attr_stmt
15+
| ID '=' ID
16+
| subgraph
17+
attr_stmt : (graph | node | edge) attr_list
18+
attr_list : '[' [ a_list ] ']' [ attr_list ]
19+
a_list : ID '=' ID [ (';' | ',') ] [ a_list ]
20+
edge_stmt : (node_id | subgraph) edgeRHS [ attr_list ]
21+
edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ]
22+
node_stmt : node_id [ attr_list ]
23+
node_id : ID [ port ]
24+
port : ':' ID [ ':' compass_pt ]
25+
| ':' compass_pt
26+
subgraph : [ subgraph [ ID ] ] '{' stmt_list '}'
27+
compass_pt : (n | ne | e | se | s | sw | w | nw | c | _)
28+
*/
29+
enum DOT {
30+
/// The keywords node, edge, graph, digraph, subgraph, and strict are case-independent.
31+
static let keywords: Set<String> = [
32+
"node",
33+
"edge",
34+
"graph",
35+
"digraph",
36+
"subgraph",
37+
"strict"
38+
]
39+
}

Sources/DOT/DOTEncoder.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ public struct DOTEncoder {
156156

157157
private func escape(_ string: String) -> String {
158158
var escapedString = string.replacingOccurrences(of: "\"", with: #"\""#)
159-
if string.contains(where: { !$0.isLetter && !$0.isNumber && $0 != "_" }) {
159+
if string.contains(where: { !$0.isLetter && !$0.isNumber && $0 != "_" }) ||
160+
DOT.keywords.contains(where: { escapedString.caseInsensitiveCompare($0) == .orderedSame })
161+
{
160162
escapedString = #""\#(escapedString)""#
161163
}
162164

Tests/GraphVizTests/NodeTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,13 @@ final class NodeTests: XCTestCase {
3131

3232
XCTAssertEqual(encoder.encode(node, in: graph), expected)
3333
}
34+
35+
func testNodeWithKeywordID() {
36+
var node = Node("Node")
37+
node.label = "Node"
38+
39+
let expected = #""Node" [label="Node"]"#
40+
41+
XCTAssertEqual(encoder.encode(node, in: graph), expected)
42+
}
3443
}

0 commit comments

Comments
 (0)