Skip to content

Commit 84e7452

Browse files
committed
iterative traversal parser
1 parent 63a6201 commit 84e7452

File tree

4 files changed

+43
-35
lines changed

4 files changed

+43
-35
lines changed

SwiftDraw/Parser.XML.Element.swift

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -111,33 +111,30 @@ extension XMLParser {
111111
return ge
112112
}
113113

114-
func parseContainerChildren(_ e: XML.Element) throws -> [DOM.GraphicsElement] {
115-
guard e.name == "svg" ||
116-
e.name == "clipPath" ||
117-
e.name == "pattern" ||
118-
e.name == "mask" ||
119-
e.name == "defs" ||
120-
e.name == "switch" ||
121-
e.name == "g" ||
122-
e.name == "a" else {
123-
throw Error.invalid
124-
}
114+
func parseGraphicsElements(_ elements: [XML.Element]) throws -> [DOM.GraphicsElement] {
115+
var result = [DOM.GraphicsElement]()
116+
var stack: [(XML.Element, parent: ContainerElement?)] = elements
117+
.reversed()
118+
.map { ($0, parent: nil) }
119+
120+
while let (element, parent) = stack.popLast() {
121+
guard let ge = try parseGraphicsElement(element) else {
122+
continue
123+
}
125124

126-
var children = [DOM.GraphicsElement]()
127-
128-
for n in e.children {
129-
do {
130-
if let ge = try parseGraphicsElement(n) {
131-
children.append(ge)
132-
}
133-
} catch let error {
134-
if let parseError = parseError(for: error, parsing: n, with: options) {
135-
throw parseError
136-
}
125+
if var parent {
126+
parent.childElements.append(ge)
127+
} else {
128+
result.append(ge)
137129
}
130+
131+
if let container = ge as? ContainerElement {
132+
stack.append(contentsOf: element.children.reversed().map { ($0, container) })
133+
}
134+
138135
}
139136

140-
return children
137+
return result
141138
}
142139

143140
func parseError(for error: Swift.Error, parsing element: XML.Element, with options: Options) -> XMLParser.Error? {
@@ -165,19 +162,15 @@ extension XMLParser {
165162
throw Error.invalid
166163
}
167164

168-
let group = DOM.Group()
169-
group.childElements = try parseContainerChildren(e)
170-
return group
165+
return DOM.Group()
171166
}
172167

173168
func parseSwitch(_ e: XML.Element) throws -> DOM.Switch {
174169
guard e.name == "switch" else {
175170
throw Error.invalid
176171
}
177172

178-
let node = DOM.Switch()
179-
node.childElements = try parseContainerChildren(e)
180-
return node
173+
return DOM.Switch()
181174
}
182175

183176
func parseAttributes(_ e: XML.Element) throws -> Attributes {

SwiftDraw/Parser.XML.SVG.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extension XMLParser {
5858
let svg = DOM.SVG(width: DOM.Length(w), height: DOM.Length(h))
5959
svg.x = try att.parseCoordinate("x")
6060
svg.y = try att.parseCoordinate("y")
61-
svg.childElements = try parseContainerChildren(e)
61+
svg.childElements = try parseGraphicsElements(e.children)
6262
svg.viewBox = try parseViewBox(try att.parseString("viewBox"))
6363

6464
svg.defs = try parseSVGDefs(e)
@@ -121,7 +121,7 @@ extension XMLParser {
121121
}
122122

123123
var defs = Dictionary<String, DOM.GraphicsElement>()
124-
let elements = try parseContainerChildren(e)
124+
let elements = try parseGraphicsElements(e.children)
125125

126126
for e in elements {
127127
guard let id = e.id else {
@@ -153,7 +153,7 @@ extension XMLParser {
153153
let att = try parseAttributes(e)
154154
let id: String = try att.parseString("id")
155155

156-
let children = try parseContainerChildren(e)
156+
let children = try parseGraphicsElements(e.children)
157157
return DOM.ClipPath(id: id, childElements: children)
158158
}
159159

@@ -176,7 +176,7 @@ extension XMLParser {
176176
let att = try parseAttributes(e)
177177
let id: String = try att.parseString("id")
178178

179-
let children = try parseContainerChildren(e)
179+
let children = try parseGraphicsElements(e.children)
180180
return DOM.Mask(id: id, childElements: children)
181181
}
182182

@@ -198,7 +198,7 @@ extension XMLParser {
198198

199199
let att = try parseAttributes(e)
200200
var pattern = try parsePattern(att)
201-
pattern.childElements = try parseContainerChildren(e)
201+
pattern.childElements = try parseGraphicsElements(e.children)
202202
return pattern
203203
}
204204
}

SwiftDraw/Parser.XML.Text.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ extension XMLParser {
4646
func parseAnchor(_ att: AttributeParser, element: XML.Element) throws -> DOM.Anchor? {
4747
let anchor = DOM.Anchor()
4848
anchor.href = try att.parseUrl("href")
49-
anchor.childElements = try parseContainerChildren(element)
5049
return anchor
5150
}
5251

SwiftDrawTests/Parser.SVGTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,20 @@ final class ParserSVGTests: XCTestCase {
151151
let circle = try XCTUnwrap(svg.firstGraphicsElement(with: "b") as? DOM.Circle)
152152
XCTAssertEqual(circle.id, "b")
153153
}
154+
155+
func testDeepNestingParses() async {
156+
let groupOpen = String(repeating: "<g>", count: 500)
157+
let groupClose = String(repeating: "</g>", count: 500)
158+
159+
XCTAssertNoThrow(
160+
try DOM.SVG.parse(xml: #"""
161+
<?xml version="1.0" encoding="UTF-8"?>
162+
<svg width="64" height="64" version="1.1">
163+
\#(groupOpen)
164+
<circle id="b" cx="50" cy="60" r="70" />
165+
\#(groupClose)
166+
</svg>
167+
"""#)
168+
)
169+
}
154170
}

0 commit comments

Comments
 (0)