Skip to content
This repository has been archived by the owner on Mar 2, 2023. It is now read-only.

Commit

Permalink
Escape " as \" in places where double quotes are introduced
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanweixin committed Jan 23, 2023
1 parent 81e463b commit fa3673a
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/dotted/graph.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import std/options
import options

# https://graphviz.org/docs/layouts/
type
Expand Down
24 changes: 16 additions & 8 deletions src/dotted/operations.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import graph
import std/options
import options
import sequtils
import std/strformat
import std/sets
import strformat
import sets
import strutils

# the string, lblString and escString attr types need double quoting.
# see https://graphviz.org/docs/attr-types/string/
Expand All @@ -25,14 +26,21 @@ proc node*(g: Graph, name: string, label: Option[string]=none(string), attrs: op
proc needsDoubleQuoting(attrName: string) : bool =
return attrName in stringAttrNames or attrName in escStringAttrNames or attrName in lblStringAttrNames

# https://graphviz.org/doc/info/lang.html
# In quoted strings in DOT, the only escaped character is double-quote ". That is, in quoted strings, the dyad \" is converted to "; all other characters are left unchanged. In particular, \\ remains \\. Layout engines may apply additional escape sequences.
func quote(s:string) : string =
result.add "\""
result.add s.replace("\"", "\\\"")
result.add "\""

proc addAttrs(s: var string, attrs: openarray[AttrValue]) =
var fst = true
for _,(a,v) in attrs:
if not fst:
s.add " "
fst = false
if needsDoubleQuoting(a):
s.add &"{a}=\"{v}\""
s.add &"{a}={quote v}"
else:
s.add &"{a}={v}"

Expand Down Expand Up @@ -69,12 +77,12 @@ proc render*(g: Graph) : string =
graphAttrs.add "] "
if g.isDirected:
if g.name.isSome:
result.add "digraph \"{g.name.get}\" {graphAttrs}{{\n".fmt
result.add "digraph {quote g.name.get} {graphAttrs}{{\n".fmt
else:
result.add "digraph {graphAttrs}{{\n".fmt
else:
if g.name.isSome:
result.add "graph \"{g.name.get}\" {graphAttrs}{{\n".fmt
result.add "graph {quote g.name.get} {graphAttrs}{{\n".fmt
else:
result.add "graph {graphAttrs}{{\n".fmt

Expand All @@ -92,10 +100,10 @@ proc render*(g: Graph) : string =
result.add "\tconcentrate=true\n"

if g.engine.isSome:
result.add "\tlayout=\"{g.engine.get}\"\n".fmt
result.add "\tlayout={quote g.engine.get}\n".fmt

if g.charset.isSome:
result.add "\tcharset=\"{g.charset.get}\"\n".fmt
result.add "\tcharset={quote g.charset.get}\n".fmt

for node in g.nodes:
renderNode(result, node)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_render.nim
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@ test "global attributes":
g.engine = some("neato") # https://graphviz.org/docs/attrs/layout/
discard g.node("A")
whitespaceIndiffCmp(expected, g.render())

test "digraph, escape double quotes in label":
let expected =
"""digraph "test" {
A [label="hello=\"world\""]
}"""
var g = newGraph(true)
g.name = some("test")
let label = "hello=\"world\""
discard g.node("A", some label)
whitespaceIndiffCmp(expected, g.render())

0 comments on commit fa3673a

Please sign in to comment.