Skip to content

Commit 97e7b8a

Browse files
committed
Begin work on flatted DSLTree
1 parent 157f03c commit 97e7b8a

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// DSLList.swift
3+
// swift-experimental-string-processing
4+
//
5+
// Created by Nate Cook on 9/25/25.
6+
//
7+
8+
struct DSLList {
9+
var nodes: [DSLTree.Node]
10+
11+
init(_ initial: DSLTree.Node) {
12+
self.nodes = [initial]
13+
}
14+
15+
init(_ nodes: [DSLTree.Node]) {
16+
self.nodes = nodes
17+
}
18+
19+
init(root: DSLTree.Node) {
20+
self.nodes = Array(root)
21+
}
22+
}
23+
24+
extension DSLList {
25+
struct Children: Sequence {
26+
var nodes: [DSLTree.Node]
27+
var firstChildIndex: Int
28+
29+
struct Iterator: IteratorProtocol {
30+
var nodes: [DSLTree.Node]
31+
var currentIndex: Int
32+
var remainingCount: Int
33+
34+
mutating func next() -> DSLTree.Node? {
35+
guard remainingCount > 0 else { return nil }
36+
guard currentIndex < nodes.count else {
37+
// FIXME: assert?
38+
print("ERROR: index out of bounds")
39+
return nil
40+
}
41+
remainingCount -= 1
42+
var nextIndex = currentIndex
43+
var inc = nodes[currentIndex].directChildren + 1
44+
while inc > 0 {
45+
nextIndex += 1
46+
inc += nodes[nextIndex].directChildren - 1
47+
}
48+
49+
return nodes[currentIndex]
50+
}
51+
}
52+
53+
func makeIterator() -> Iterator {
54+
Iterator(nodes: nodes, currentIndex: firstChildIndex, remainingCount: nodes[firstChildIndex].directChildren)
55+
}
56+
}
57+
}
58+
59+
extension DSLTree.Node {
60+
var directChildren: Int {
61+
switch self {
62+
case .trivia, .empty, .quotedLiteral,
63+
.consumer, .matcher, .characterPredicate,
64+
.customCharacterClass, .atom:
65+
return 0
66+
67+
case .orderedChoice(let c), .concatenation(let c):
68+
return c.count
69+
70+
case .convertedRegexLiteral, .capture, .nonCapturingGroup,
71+
.quantification, .ignoreCapturesInTypedOutput, .conditional:
72+
return 1
73+
74+
case .absentFunction:
75+
return 0
76+
}
77+
}
78+
}
79+
80+
extension DSLTree.Node: Sequence {
81+
struct Iterator: Sequence, IteratorProtocol {
82+
typealias Element = DSLTree.Node
83+
private var stack: [Frame]
84+
private let getChildren: (Element) -> [Element]
85+
86+
private struct Frame {
87+
let node: Element
88+
let children: [Element]
89+
var nextIndex: Int = 0
90+
}
91+
92+
fileprivate init(
93+
root: Element,
94+
getChildren: @escaping (Element) -> [Element]
95+
) {
96+
self.getChildren = getChildren
97+
self.stack = [Frame(node: root, children: getChildren(root))]
98+
}
99+
100+
mutating func next() -> Element? {
101+
guard let top = stack.popLast() else { return nil }
102+
// Push children in reverse so leftmost comes out first.
103+
for child in top.children.reversed() {
104+
stack.append(Frame(node: child, children: getChildren(child)))
105+
}
106+
return top.node
107+
}
108+
}
109+
110+
func makeIterator() -> Iterator {
111+
Iterator(root: self, getChildren: { $0.children })
112+
}
113+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// DSLListTests.swift
3+
// swift-experimental-string-processing
4+
//
5+
// Created by Nate Cook on 9/25/25.
6+
//
7+
8+
import Testing
9+
@testable import _StringProcessing
10+
11+
@Suite
12+
struct DSLListTests {
13+
@Test(arguments: [(#/abc/#, 4), (#/a(?:b+)c*/#, 7)])
14+
func simple(regex: Regex<Substring>, nodeCount: Int) {
15+
let dslList = DSLList(root: regex.root)
16+
#expect(dslList.nodes.count == nodeCount)
17+
for (i, node) in dslList.nodes.enumerated() {
18+
print(i, node)
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)