Skip to content

Commit fb3c83d

Browse files
Adding switch statement support (#53)
1 parent 4c3cfcd commit fb3c83d

33 files changed

+1001
-57
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/SyntaxSparrow/Internal/Collectors/DeclarationCollection.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public final class DeclarationCollection {
5656
/// The collected internal(set) public variable declarations.
5757
public internal(set) var variables: [Variable] = []
5858

59+
/// The collected switch expression declarations.
60+
public internal(set) var switches: [SwitchExpression] = []
61+
5962
func reset() {
6063
actors = []
6164
classes = []
@@ -73,6 +76,7 @@ public final class DeclarationCollection {
7376
subscripts = []
7477
typealiases = []
7578
variables = []
79+
switches = []
7680
}
7781

7882
func collect(from collection: DeclarationCollection) {
@@ -92,5 +96,6 @@ public final class DeclarationCollection {
9296
subscripts = collection.subscripts
9397
typealiases = collection.typealiases
9498
variables = collection.variables
99+
switches = collection.switches
95100
}
96101
}

Sources/SyntaxSparrow/Internal/Collectors/RootDeclarationCollector.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,13 @@ class RootDeclarationCollector: SyntaxVisitor {
203203
if let entryNode = entryNode, node.id == entryNode.id { return .visitChildren }
204204
let declaration = Variable(node: node)
205205
declarationCollection.variables.append(declaration)
206-
return .visitChildren
206+
return .skipChildren
207+
}
208+
209+
override func visit(_ node: SwitchExprSyntax) -> SyntaxVisitorContinueKind {
210+
if let entryNode = entryNode, node.id == entryNode.id { return .visitChildren }
211+
let declaration = SwitchExpression(node: node)
212+
declarationCollection.switches.append(declaration)
213+
return .skipChildren
207214
}
208215
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// SwitchExpressionInnerBindingMemberCollector.swift
3+
//
4+
//
5+
// Copyright (c) CheekyGhost Labs 2024. All Rights Reserved.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
11+
class SwitchExpressionInnerBindingMemberCollector: SkipByDefaultVisitor {
12+
13+
struct Binding {
14+
var keyword: String = ""
15+
var value: String = ""
16+
}
17+
18+
// MARK: - Properties
19+
20+
private(set) var memberName: String?
21+
22+
private(set) var bindings: [Binding] = []
23+
24+
private(set) var focusedFunctionNode: FunctionCallExprSyntax?
25+
26+
private(set) var focusedElementListNode: LabeledExprListSyntax?
27+
28+
// MARK: - Helpers
29+
30+
func collect(_ node: SwitchCaseItemSyntax) -> SwitchExpression.SwitchCase.Item? {
31+
walk(node)
32+
guard let memberName else { return nil }
33+
var bindingMap: [String: String] = [:]
34+
bindings.forEach { bindingMap[$0.keyword] = $0.value }
35+
return .innerValueBindingMember(name: memberName, elements: bindingMap)
36+
}
37+
38+
// MARK: - Overrides
39+
40+
override func visit(_ node: SwitchCaseItemSyntax) -> SyntaxVisitorContinueKind {
41+
return .visitChildren
42+
}
43+
44+
override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
45+
return .visitChildren
46+
}
47+
48+
override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind {
49+
guard focusedFunctionNode == nil else { return .skipChildren }
50+
focusedFunctionNode = node
51+
return .visitChildren
52+
}
53+
54+
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
55+
guard let focusedNode = focusedFunctionNode, node.parent?.id == focusedNode.id else { return .skipChildren }
56+
memberName = node.declName.baseName.text.trimmed
57+
return .skipChildren
58+
}
59+
60+
override func visit(_ node: LabeledExprListSyntax) -> SyntaxVisitorContinueKind {
61+
guard node.parent?.id == focusedFunctionNode?.id else { return .skipChildren }
62+
guard focusedElementListNode == nil else { return .skipChildren }
63+
focusedElementListNode = node
64+
return .visitChildren
65+
}
66+
67+
override func visit(_ node: LabeledExprSyntax) -> SyntaxVisitorContinueKind {
68+
guard let focusedElements = focusedElementListNode, node.parent?.id == focusedElements.id else { return .skipChildren }
69+
return .visitChildren
70+
}
71+
72+
override func visit(_ node: PatternExprSyntax) -> SyntaxVisitorContinueKind {
73+
guard let focusedElements = focusedElementListNode, node.parent?.parent?.id == focusedElements.id else { return .skipChildren }
74+
return .visitChildren
75+
}
76+
77+
override func visit(_ node: ValueBindingPatternSyntax) -> SyntaxVisitorContinueKind {
78+
let binding = Binding(
79+
keyword: node.bindingSpecifier.text.trimmed,
80+
value: node.pattern.trimmedDescription
81+
)
82+
bindings.append(binding)
83+
return .skipChildren
84+
}
85+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// SwitchExpressionValueBindingCollector.swift
3+
//
4+
//
5+
// Copyright (c) CheekyGhost Labs 2024. All Rights Reserved.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
11+
class SwitchExpressionIsTypeCollector: SkipByDefaultVisitor {
12+
13+
// MARK: - Properties
14+
15+
private(set) var type: String?
16+
17+
// MARK: - Helpers
18+
19+
func collect(_ node: SwitchCaseItemSyntax) -> SwitchExpression.SwitchCase.Item? {
20+
walk(node)
21+
guard let type else { return nil }
22+
return .isTypePattern(type: type)
23+
}
24+
25+
// MARK: - Overrides
26+
27+
override func visit(_ node: SwitchCaseItemSyntax) -> SyntaxVisitorContinueKind {
28+
return .visitChildren
29+
}
30+
31+
override func visit(_ node: IsTypePatternSyntax) -> SyntaxVisitorContinueKind {
32+
return .visitChildren
33+
}
34+
35+
override func visit(_ node: IdentifierTypeSyntax) -> SyntaxVisitorContinueKind {
36+
type = node.description
37+
return .skipChildren
38+
}
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// SwitchExpressionMemberCollector.swift
3+
//
4+
//
5+
// Copyright (c) CheekyGhost Labs 2024. All Rights Reserved.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
11+
class SwitchExpressionMemberCollector: SkipByDefaultVisitor {
12+
13+
// MARK: - Properties
14+
15+
private(set) var memberName: String?
16+
17+
private(set) var focusedExpressionNode: ExpressionPatternSyntax?
18+
19+
// MARK: - Helpers
20+
21+
func collect(_ node: SwitchCaseItemSyntax) -> SwitchExpression.SwitchCase.Item? {
22+
walk(node)
23+
guard let memberName else { return nil }
24+
return .member(name: memberName)
25+
}
26+
27+
// MARK: - Overrides
28+
29+
override func visit(_ node: SwitchCaseItemSyntax) -> SyntaxVisitorContinueKind {
30+
return .visitChildren
31+
}
32+
33+
override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
34+
guard focusedExpressionNode == nil else { return .skipChildren }
35+
focusedExpressionNode = node
36+
return .visitChildren
37+
}
38+
39+
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
40+
guard let focusedNode = focusedExpressionNode, node.parent?.id == focusedNode.id else { return .skipChildren }
41+
memberName = node.declName.baseName.text.trimmed
42+
return .skipChildren
43+
}
44+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// SwitchExpressionTupleCollector.swift
3+
//
4+
//
5+
// Copyright (c) CheekyGhost Labs 2024. All Rights Reserved.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
11+
class SwitchExpressionTupleCollector: SkipByDefaultVisitor {
12+
13+
// MARK: - Properties
14+
15+
private(set) var memberName: String?
16+
17+
private(set) var elements: [String]?
18+
19+
private(set) var focusedExpressionNode: ExpressionPatternSyntax?
20+
21+
// MARK: - Helpers
22+
23+
func collect(_ node: SwitchCaseItemSyntax) -> SwitchExpression.SwitchCase.Item? {
24+
walk(node)
25+
guard let elements, memberName == nil else { return nil }
26+
return .tuple(elements: elements)
27+
}
28+
29+
// MARK: - Overrides
30+
31+
override func visit(_ node: SwitchCaseItemSyntax) -> SyntaxVisitorContinueKind {
32+
return .visitChildren
33+
}
34+
35+
override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
36+
guard focusedExpressionNode == nil else { return .skipChildren }
37+
focusedExpressionNode = node
38+
return .visitChildren
39+
}
40+
41+
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
42+
// This is a fallback as there should be no member syntax. If there is the result will return nil.
43+
memberName = node.declName.baseName.text.trimmed
44+
return .skipChildren
45+
}
46+
47+
override func visit(_ node: TupleExprSyntax) -> SyntaxVisitorContinueKind {
48+
guard let focusedExpressionNode, node.parent?.id == focusedExpressionNode.id, elements == nil else { return .skipChildren }
49+
elements = node.elements.trimmedDescription.components(separatedBy: ",").map(\.trimmed)
50+
return .skipChildren
51+
}
52+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//
2+
// SwitchExpressionValueBindingCollector.swift
3+
//
4+
//
5+
// Copyright (c) CheekyGhost Labs 2024. All Rights Reserved.
6+
//
7+
8+
import Foundation
9+
import SwiftSyntax
10+
11+
class SwitchExpressionValueBindingCollector: SkipByDefaultVisitor {
12+
13+
// MARK: - Properties
14+
15+
private(set) var memberName: String?
16+
17+
private(set) var keyword: String?
18+
19+
private(set) var sequenceElements: [String] = []
20+
21+
private(set) var focusedBindingNode: ValueBindingPatternSyntax?
22+
23+
private(set) var focusedFunctionNode: FunctionCallExprSyntax?
24+
25+
// MARK: - Helpers
26+
27+
func collect(_ node: SwitchCaseItemSyntax) -> SwitchExpression.SwitchCase.Item? {
28+
walk(node)
29+
guard let keyword, memberName == nil else { return nil }
30+
return .valueBinding(keyWord: keyword, elements: sequenceElements)
31+
}
32+
33+
// MARK: - Overrides
34+
35+
override func visit(_ node: SwitchCaseItemSyntax) -> SyntaxVisitorContinueKind {
36+
return .visitChildren
37+
}
38+
39+
override func visit(_ node: ValueBindingPatternSyntax) -> SyntaxVisitorContinueKind {
40+
guard focusedBindingNode == nil else { return .skipChildren }
41+
focusedBindingNode = node
42+
keyword = node.bindingSpecifier.trimmedDescription
43+
return .visitChildren
44+
}
45+
46+
override func visit(_ node: ExpressionPatternSyntax) -> SyntaxVisitorContinueKind {
47+
guard let focusedNode = focusedBindingNode, node.parent?.id == focusedNode.id else { return .skipChildren }
48+
return .visitChildren
49+
}
50+
51+
override func visit(_ node: MemberAccessExprSyntax) -> SyntaxVisitorContinueKind {
52+
// This is a fallback as there should be no member syntax. If there is the result will return nil.
53+
memberName = node.declName.baseName.text.trimmed
54+
return .skipChildren
55+
}
56+
57+
override func visit(_ node: SequenceExprSyntax) -> SyntaxVisitorContinueKind {
58+
return .visitChildren
59+
}
60+
61+
override func visit(_ node: ExprListSyntax) -> SyntaxVisitorContinueKind {
62+
sequenceElements = node.map(\.trimmedDescription)
63+
return .skipChildren
64+
}
65+
66+
/*
67+
Initially listened for the variants I could derive from token exploration. However, as it always
68+
ends up adding the `node.trimmedDescription`, and there is always an `ExprListSyntax` element that
69+
contains them, the `ExprListSyntax` visitor override will just map the trimmed description value
70+
into the sequence. Leaving these in as reference for future refactor/clean-up.
71+
*/
72+
73+
// override func visit(_ node: UnresolvedPatternExprSyntax) -> SyntaxVisitorContinueKind {
74+
// return .visitChildren
75+
// }
76+
//
77+
// override func visit(_ node: TypeExprSyntax) -> SyntaxVisitorContinueKind {
78+
// sequenceElements.append(node.trimmedDescription)
79+
// return .skipChildren
80+
// }
81+
//
82+
// override func visit(_ node: UnresolvedAsExprSyntax) -> SyntaxVisitorContinueKind {
83+
// sequenceElements.append(node.trimmedDescription)
84+
// return .skipChildren
85+
// }
86+
//
87+
// override func visit(_ node: UnresolvedIsExprSyntax) -> SyntaxVisitorContinueKind {
88+
// sequenceElements.append(node.trimmedDescription)
89+
// return .skipChildren
90+
// }
91+
//
92+
// override func visit(_ node: IdentifierPatternSyntax) -> SyntaxVisitorContinueKind {
93+
// sequenceElements.append(node.identifier.trimmedDescription)
94+
// return .skipChildren
95+
// }
96+
}

0 commit comments

Comments
 (0)