-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathParseStructField.swift
More file actions
125 lines (101 loc) · 3.54 KB
/
ParseStructField.swift
File metadata and controls
125 lines (101 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//
// ParseStructVisitor.swift
// BinaryParseKit
//
// Created by Larry Zeng on 7/15/25.
//
import BinaryParseKitCommons
import Collections
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
class ParseStructField: SyntaxVisitor {
struct VariableInfo {
let type: TypeSyntax
let parseActions: [StructParseAction]
init(type: TypeSyntax, parseActions: [StructParseAction]) {
self.type = type.trimmed
self.parseActions = parseActions
}
}
typealias ParseVariableMapping = OrderedDictionary<TokenSyntax, VariableInfo>
private let context: any MacroExpansionContext
private var hasParse: Bool = false
private var structFieldVisitor: MacroAttributeCollector?
private var existParseRestContent: Bool = false
private(set) var variables: ParseVariableMapping = [:]
private(set) var errors: [Diagnostic] = []
init(context: any MacroExpansionContext) {
self.context = context
super.init(viewMode: .sourceAccurate)
}
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
// Skip static declarations
guard !node.isStaticDecl else {
return .skipChildren
}
let structFieldVisitor = MacroAttributeCollector(context: context)
structFieldVisitor.walk(node.attributes)
hasParse = structFieldVisitor.hasParse
do {
try structFieldVisitor.validate()
self.structFieldVisitor = structFieldVisitor
return .visitChildren
} catch {
errors.append(.init(node: node.attributes, message: error))
return .skipChildren
}
}
override func visitPost(_: VariableDeclSyntax) {
structFieldVisitor = nil
}
override func visit(_ node: PatternBindingSyntax) -> SyntaxVisitorContinueKind {
do {
try parsePatternBinding(node)
} catch {
errors.append(.init(node: node, message: error))
}
return .skipChildren
}
private func parsePatternBinding(_ binding: PatternBindingSyntax) throws(ParseStructMacroError) {
if binding.hasAccessor {
if hasParse {
throw .parseAccessorVariableDecl
} else {
print("skip unparsed accessor")
return
}
}
guard hasParse, let structFieldVisitor else {
throw .noParseAttributeOnVariableDecl
}
if existParseRestContent {
throw .multipleOrNonTrailingParseRest
}
if structFieldVisitor.hasParseRest {
existParseRestContent = true
}
guard binding.hasTypeAnnotation else {
throw .variableDeclNoTypeAnnotation
}
guard let variableName = binding.identifierName else {
throw .notIdentifierDef
}
guard let typeName = binding.typeName else {
throw .invalidTypeAnnotation
}
variables[variableName.trimmed] = .init(type: typeName, parseActions: structFieldVisitor.parseActions)
}
func validate(for node: some SwiftSyntax.SyntaxProtocol) throws(ParseStructMacroError) {
if !errors.isEmpty {
for error in errors {
context.diagnose(error)
}
throw .fatalError(message: "Parsing struct's fields has encountered an error.")
}
if variables.isEmpty {
context.diagnose(.init(node: node, message: ParseStructMacroError.emptyParse))
}
}
}