Skip to content

Commit 3efa7e3

Browse files
authored
Merge pull request #375 from tayloraswift/ucf-signature-patterns
add a quick-and-dirty implementation of DocC signature pattern syntax
2 parents a67cbfb + e709a10 commit 3efa7e3

8 files changed

+530
-76
lines changed

Sources/LinkResolution/UCF.ResolvableOverload.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension UCF.ResolvableOverload
1919
{
2020
if case nil = predicate.seal
2121
{
22-
// Macros are currently the only kind of declaration that *must* be spelled with
22+
// Macros are currently the only kind of declaration that *must* be spelled with
2323
// trailing parentheses.
2424
switch self.phylum
2525
{
@@ -40,7 +40,7 @@ extension UCF.ResolvableOverload
4040
case .var: break
4141
}
4242
}
43-
else
43+
else
4444
{
4545
switch self.phylum
4646
{
@@ -62,7 +62,7 @@ extension UCF.ResolvableOverload
6262
}
6363
}
6464

65-
guard
65+
guard
6666
let suffix:UCF.Selector.Suffix = predicate.suffix
6767
else
6868
{
@@ -75,6 +75,8 @@ extension UCF.ResolvableOverload
7575
case .legacy(_, let hash?): return hash == self.hash
7676
case .hash(let hash): return hash == self.hash
7777
case .filter(let filter): return filter ~= self.phylum
78+
// TODO: unimplemented
79+
case .pattern: return true
7880
}
7981
}
8082
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
extension UCF.PatternFilter
2+
{
3+
@frozen public
4+
struct Identifier:Equatable, Hashable, Sendable
5+
{
6+
@usableFromInline
7+
let suffix:String
8+
9+
@inlinable
10+
init(suffix:String)
11+
{
12+
self.suffix = suffix
13+
}
14+
}
15+
}
16+
extension UCF.PatternFilter.Identifier:CustomStringConvertible
17+
{
18+
@inlinable public
19+
var description:String
20+
{
21+
self.suffix.isEmpty ? "_" : self.suffix
22+
}
23+
}
24+
extension UCF.PatternFilter.Identifier
25+
{
26+
@inlinable public
27+
init?(_ pattern:some StringProtocol)
28+
{
29+
if pattern.isEmpty
30+
{
31+
return nil
32+
}
33+
if pattern == "_"
34+
{
35+
self.init(suffix: "")
36+
}
37+
else
38+
{
39+
self.init(suffix: String.init(pattern))
40+
}
41+
}
42+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
extension UCF.PatternFilter
2+
{
3+
@frozen public
4+
enum Output:Equatable, Hashable, Sendable
5+
{
6+
case single(Identifier)
7+
case tuple([Identifier])
8+
}
9+
}
10+
extension UCF.PatternFilter.Output
11+
{
12+
static func parse(_ string:Substring) -> Self?
13+
{
14+
guard
15+
let i:String.Index = string.firstIndex(of: "("),
16+
let k:String.Index = string.lastIndex(of: ")")
17+
else
18+
{
19+
guard
20+
let identifier:UCF.PatternFilter.Identifier = .init(string)
21+
else
22+
{
23+
return nil
24+
}
25+
26+
return .single(identifier)
27+
}
28+
29+
guard i < k
30+
else
31+
{
32+
return nil
33+
}
34+
35+
let j:String.Index = string.index(after: i)
36+
37+
// If we do not check for this, ``BidirectionalCollection.split`` will emit a single
38+
// empty substring due to `omitEmptySubsequences`.
39+
if j == k
40+
{
41+
return .tuple([])
42+
}
43+
44+
var outputs:[UCF.PatternFilter.Identifier] = []
45+
for output:Substring in string[j ..< k].split(separator: ",",
46+
omittingEmptySubsequences: false)
47+
{
48+
guard
49+
let output:UCF.PatternFilter.Identifier = .init(output)
50+
else
51+
{
52+
return nil
53+
}
54+
55+
outputs.append(output)
56+
}
57+
58+
return outputs.count == 1 ? .single(outputs[0]) : .tuple(outputs)
59+
}
60+
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
extension UCF
2+
{
3+
@frozen public
4+
enum PatternFilter:Equatable, Hashable, Sendable
5+
{
6+
case fullSignature([Identifier], Output)
7+
case inputs([Identifier])
8+
case output(Output)
9+
}
10+
}
11+
extension UCF.PatternFilter
12+
{
13+
@inlinable public
14+
var inputs:[Identifier]?
15+
{
16+
switch self
17+
{
18+
case .fullSignature(let inputs, _): inputs
19+
case .inputs(let inputs): inputs
20+
case .output: nil
21+
}
22+
}
23+
24+
@inlinable public
25+
var output:Output?
26+
{
27+
switch self
28+
{
29+
case .fullSignature(_, let output): output
30+
case .inputs: nil
31+
case .output(let output): output
32+
}
33+
}
34+
}
35+
extension UCF.PatternFilter:CustomStringConvertible
36+
{
37+
public
38+
var description:String
39+
{
40+
var string:String = ""
41+
42+
if let inputs:[UCF.PatternFilter.Identifier] = self.inputs
43+
{
44+
string.append("(")
45+
46+
var first:Bool = true
47+
for input:UCF.PatternFilter.Identifier in inputs
48+
{
49+
if first
50+
{
51+
first = false
52+
}
53+
else
54+
{
55+
string.append(",")
56+
}
57+
58+
string.append("\(input)")
59+
}
60+
61+
string.append(")")
62+
}
63+
64+
guard
65+
let output:UCF.PatternFilter.Output = self.output
66+
else
67+
{
68+
return string
69+
}
70+
71+
if !string.isEmpty
72+
{
73+
string.append("-")
74+
}
75+
76+
string.append(">")
77+
78+
switch output
79+
{
80+
case .single(let output):
81+
string.append("\(output)")
82+
83+
case .tuple(let outputs):
84+
string.append("(")
85+
86+
var first:Bool = true
87+
for output:UCF.PatternFilter.Identifier in outputs
88+
{
89+
if first
90+
{
91+
first = false
92+
}
93+
else
94+
{
95+
string.append(",")
96+
}
97+
98+
string.append("\(output)")
99+
}
100+
101+
string.append(")")
102+
}
103+
104+
return string
105+
}
106+
}
107+
extension UCF.PatternFilter
108+
{
109+
static func parse(_ string:Substring) -> Self?
110+
{
111+
guard
112+
let first:Character = string.first
113+
else
114+
{
115+
return nil
116+
}
117+
118+
switch first
119+
{
120+
case "(":
121+
let i:String.Index = string.index(after: string.startIndex)
122+
123+
guard
124+
let j:String.Index = string[i...].firstIndex(of: ")")
125+
else
126+
{
127+
return nil
128+
}
129+
130+
var inputs:[UCF.PatternFilter.Identifier] = []
131+
for input:Substring in string[i ..< j].split(separator: ",",
132+
omittingEmptySubsequences: false)
133+
{
134+
guard
135+
let input:UCF.PatternFilter.Identifier = .init(input)
136+
else
137+
{
138+
return nil
139+
}
140+
141+
inputs.append(input)
142+
}
143+
144+
let k:String.Index = string.index(after: j)
145+
146+
if let l:String.Index = string.index(k, offsetBy: 2, limitedBy: string.endIndex),
147+
string[k ..< l] == "->"
148+
{
149+
guard
150+
let output:UCF.PatternFilter.Output = .parse(string[l...])
151+
else
152+
{
153+
return nil
154+
}
155+
156+
return .fullSignature(inputs, output)
157+
}
158+
159+
return .inputs(inputs)
160+
161+
case ">":
162+
// This pattern contains an output only.
163+
let i:String.Index = string.index(after: string.startIndex)
164+
guard
165+
let output:UCF.PatternFilter.Output = .parse(string[i...])
166+
else
167+
{
168+
return nil
169+
}
170+
171+
return .output(output)
172+
173+
default:
174+
return nil
175+
}
176+
}
177+
}

0 commit comments

Comments
 (0)