Sources/OrgCore/Syntax/GreenTree.swift
92 lines · 2611 bytes
10 symbols in this file
1public struct GreenToken: Sendable, Equatable {
2 public let kind: SyntaxKind
3 public let text: String
4 /// Length in UTF-16 code units.
5 public let length: Int
6
7 public init(kind: SyntaxKind, text: String) {
8 self.kind = kind
9 self.text = text
10 self.length = text.utf16.count
11 }
12}
13
14/// An immutable node. Stores only kind, children and length, so unchanged subtrees can be
15/// shared between versions of a document.
16public final class GreenNode: Sendable, Equatable {
17 public let kind: SyntaxKind
18 public let children: [GreenElement]
19 /// Length in UTF-16 code units.
20 public let length: Int
21
22 public init(kind: SyntaxKind, children: [GreenElement]) {
23 self.kind = kind
24 self.children = children
25 self.length = children.reduce(0) { $0 + $1.length }
26 }
27
28 public static func == (lhs: GreenNode, rhs: GreenNode) -> Bool {
29 lhs === rhs || (lhs.kind == rhs.kind && lhs.children == rhs.children)
30 }
31
32 public var text: String {
33 var out = ""
34 write(to: &out)
35 return out
36 }
37
38 func write(to out: inout String) {
39 for child in children {
40 switch child {
41 case .node(let node): node.write(to: &out)
42 case .token(let token): out += token.text
43 }
44 }
45 }
46}
47
48public enum GreenElement: Sendable, Equatable {
49 case node(GreenNode)
50 case token(GreenToken)
51
52 public var length: Int {
53 switch self {
54 case .node(let node): node.length
55 case .token(let token): token.length
56 }
57 }
58}
59
60struct GreenBuilder {
61 private var stack: [(kind: SyntaxKind, children: [GreenElement])] = []
62 private var root: GreenNode?
63
64 mutating func start(_ kind: SyntaxKind) {
65 stack.append((kind, []))
66 }
67
68 mutating func token(_ kind: SyntaxKind, _ text: some StringProtocol) {
69 guard !text.isEmpty else { return }
70 stack[stack.count - 1].children.append(.token(GreenToken(kind: kind, text: String(text))))
71 }
72
73 /// Appends an already-built node, for reuse across versions.
74 mutating func node(_ green: GreenNode) {
75 stack[stack.count - 1].children.append(.node(green))
76 }
77
78 mutating func finish() {
79 let (kind, children) = stack.removeLast()
80 let node = GreenNode(kind: kind, children: children)
81 if stack.isEmpty {
82 root = node
83 } else {
84 stack[stack.count - 1].children.append(.node(node))
85 }
86 }
87
88 func build() -> GreenNode {
89 precondition(stack.isEmpty, "unfinished nodes")
90 return root!
91 }
92}