Skip to content

Commit 18cbe7f

Browse files
committed
implement a far more powerful signature pattern parser, that can handle complex type expressions
1 parent a476eb0 commit 18cbe7f

31 files changed

+890
-316
lines changed

Sources/LinkResolution/UCF.Autograph.swift

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ extension UCF
2020
}
2121
extension UCF.Autograph
2222
{
23-
static func ~= (predicate:UCF.PatternFilter, self:Self) -> Bool
23+
static func ~= (filter:UCF.SignatureFilter, self:Self) -> Bool
2424
{
25-
if let inputs:[String?] = predicate.inputs
25+
if let inputs:[String?] = filter.inputs
2626
{
2727
guard self.inputs.count == inputs.count
2828
else
@@ -38,30 +38,14 @@ extension UCF.Autograph
3838
}
3939
}
4040

41-
switch predicate.output
41+
if let output:[String?] = filter.output
4242
{
43-
case nil:
44-
break
45-
46-
case .single(let output):
47-
guard self.output.count == 1
48-
else
49-
{
50-
return false
51-
}
52-
53-
if let output:String, output != self.output[0]
54-
{
55-
return false
56-
}
57-
58-
case .tuple(let outputs):
59-
guard self.output.count == outputs.count
43+
guard self.output.count == output.count
6044
else
6145
{
6246
return false
6347
}
64-
for case (let required, let provided?) in zip(self.output, outputs)
48+
for case (let required, let provided?) in zip(self.output, output)
6549
{
6650
if required != provided
6751
{

Sources/LinkResolution/UCF.ResolvableOverload.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ extension UCF.ResolvableOverload
8181
case .hash(let hash):
8282
return hash == self.hash
8383

84-
case .filter(let filter):
84+
case .keywords(let filter):
8585
return filter ~= self.phylum
8686

87-
case .pattern(let pattern):
87+
case .signature(let filter):
8888
if let autograph:UCF.Autograph = self.autograph
8989
{
90-
return pattern ~= autograph
90+
return filter ~= autograph
9191
}
9292
else
9393
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Grammar
2+
3+
extension UCF
4+
{
5+
enum ArrowRule:ParsingRule
6+
{
7+
typealias Location = String.Index
8+
typealias Terminal = Unicode.Scalar
9+
10+
static func parse<Source>(
11+
_ input:inout ParsingInput<some ParsingDiagnostics<Source>>) throws
12+
where Source:Collection<Terminal>, Source.Index == Location
13+
{
14+
try input.parse(as: UnicodeEncoding<Location, Terminal>.Hyphen.self)
15+
try input.parse(as: UnicodeEncoding<Location, Terminal>.AngleRight.self)
16+
}
17+
}
18+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Grammar
2+
3+
extension UCF
4+
{
5+
/// BracketPattern ::= '[' TypePattern ( ':' TypePattern ) ? ']'
6+
enum BracketPatternRule:ParsingRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
11+
typealias Construction = (TypePattern, TypePattern?)
12+
13+
static func parse<Diagnostics>(
14+
_ input:inout ParsingInput<Diagnostics>) throws -> Construction where
15+
Diagnostics:ParsingDiagnostics,
16+
Diagnostics.Source.Element == Terminal,
17+
Diagnostics.Source.Index == Location
18+
{
19+
try input.parse(as: UnicodeEncoding<Location, Terminal>.BracketLeft.self)
20+
21+
let first:TypePattern = try input.parse(as: TypePatternRule.self)
22+
let value:TypePattern?
23+
24+
if case ()? = input.parse(as: UnicodeEncoding<Location, Terminal>.Colon?.self)
25+
{
26+
value = try input.parse(as: TypePatternRule.self)
27+
}
28+
else
29+
{
30+
value = nil
31+
}
32+
33+
try input.parse(as: UnicodeEncoding<Location, Terminal>.BracketRight.self)
34+
35+
return (first, value)
36+
}
37+
}
38+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Grammar
2+
3+
extension UCF
4+
{
5+
/// FunctionPattern ::= TuplePattern ( '->' TypePattern ) ?
6+
enum FunctionPatternRule:ParsingRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
11+
typealias Construction = ([TypePattern], TypePattern?)
12+
13+
static func parse<Diagnostics>(
14+
_ input:inout ParsingInput<Diagnostics>) throws -> Construction where
15+
Diagnostics:ParsingDiagnostics,
16+
Diagnostics.Source.Element == Terminal,
17+
Diagnostics.Source.Index == Location
18+
{
19+
let tuple:[TypePattern] = try input.parse(as: TuplePatternRule.self)
20+
21+
if case ()? = input.parse(as: ArrowRule?.self)
22+
{
23+
return (tuple, try input.parse(as: TypePatternRule.self))
24+
}
25+
else
26+
{
27+
return (tuple, nil)
28+
}
29+
}
30+
}
31+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extension UCF
2+
{
3+
enum IdentifierError:Error
4+
{
5+
case reserved
6+
}
7+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import Grammar
2+
3+
extension UCF.IdentifierRule
4+
{
5+
/// A parsing rule that matches a codepoint that can begin a Swift identifier.
6+
enum FirstCodepoint:TerminalRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
typealias Construction = Void
11+
12+
static func parse(terminal:Terminal) -> Void?
13+
{
14+
switch terminal
15+
{
16+
case "a" ... "z",
17+
"A" ... "Z",
18+
"_",
19+
"\u{00A8}",
20+
"\u{00AA}",
21+
"\u{00AD}",
22+
"\u{00AF}",
23+
"\u{00B2}" ... "\u{00B5}",
24+
"\u{00B7}" ... "\u{00BA}",
25+
"\u{00BC}" ... "\u{00BE}",
26+
"\u{00C0}" ... "\u{00D6}",
27+
"\u{00D8}" ... "\u{00F6}",
28+
"\u{00F8}" ... "\u{00FF}",
29+
"\u{0100}" ... "\u{02FF}",
30+
"\u{0370}" ... "\u{167F}",
31+
"\u{1681}" ... "\u{180D}",
32+
"\u{180F}" ... "\u{1DBF}",
33+
"\u{1E00}" ... "\u{1FFF}",
34+
"\u{200B}" ... "\u{200D}",
35+
"\u{202A}" ... "\u{202E}",
36+
"\u{203F}" ... "\u{2040}",
37+
"\u{2054}",
38+
"\u{2060}" ... "\u{206F}",
39+
"\u{2070}" ... "\u{20CF}",
40+
"\u{2100}" ... "\u{218F}",
41+
"\u{2460}" ... "\u{24FF}",
42+
"\u{2776}" ... "\u{2793}",
43+
"\u{2C00}" ... "\u{2DFF}",
44+
"\u{2E80}" ... "\u{2FFF}",
45+
"\u{3004}" ... "\u{3007}",
46+
"\u{3021}" ... "\u{302F}",
47+
"\u{3031}" ... "\u{303F}",
48+
"\u{3040}" ... "\u{D7FF}",
49+
"\u{F900}" ... "\u{FD3D}",
50+
"\u{FD40}" ... "\u{FDCF}",
51+
"\u{FDF0}" ... "\u{FE1F}",
52+
"\u{FE30}" ... "\u{FE44}",
53+
"\u{FE47}" ... "\u{FFFD}",
54+
"\u{10000}" ... "\u{1FFFD}",
55+
"\u{20000}" ... "\u{2FFFD}",
56+
"\u{30000}" ... "\u{3FFFD}",
57+
"\u{40000}" ... "\u{4FFFD}",
58+
"\u{50000}" ... "\u{5FFFD}",
59+
"\u{60000}" ... "\u{6FFFD}",
60+
"\u{70000}" ... "\u{7FFFD}",
61+
"\u{80000}" ... "\u{8FFFD}",
62+
"\u{90000}" ... "\u{9FFFD}",
63+
"\u{A0000}" ... "\u{AFFFD}",
64+
"\u{B0000}" ... "\u{BFFFD}",
65+
"\u{C0000}" ... "\u{CFFFD}",
66+
"\u{D0000}" ... "\u{DFFFD}",
67+
"\u{E0000}" ... "\u{EFFFD}":
68+
return ()
69+
70+
default:
71+
return nil
72+
}
73+
}
74+
}
75+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Grammar
2+
3+
extension UCF.IdentifierRule
4+
{
5+
/// A parsing rule that matches a codepoint that can occur in a Swift identifier, as long
6+
/// as it is not the first codepoint.
7+
enum NextCodepoint:TerminalRule
8+
{
9+
typealias Location = String.Index
10+
typealias Terminal = Unicode.Scalar
11+
typealias Construction = Void
12+
13+
static func parse(terminal:Terminal) -> Void?
14+
{
15+
switch terminal
16+
{
17+
case "0" ... "9",
18+
"\u{0300}" ... "\u{036F}",
19+
"\u{1DC0}" ... "\u{1DFF}",
20+
"\u{20D0}" ... "\u{20FF}",
21+
"\u{FE20}" ... "\u{FE2F}":
22+
return ()
23+
24+
default:
25+
return UCF.IdentifierRule.FirstCodepoint.parse(terminal: terminal)
26+
}
27+
}
28+
}
29+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Grammar
2+
3+
extension UCF.IdentifierRule
4+
{
5+
/// Matches any terminal that is not a backtick (`` ` ``).
6+
enum RawCodepoint:TerminalRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
typealias Construction = Void
11+
12+
static func parse(terminal:Terminal) -> Void?
13+
{
14+
terminal != "`" ? () : nil
15+
}
16+
}
17+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Grammar
2+
3+
extension UCF
4+
{
5+
/// Identifier ::= ( ``FirstCodepoint`` ``NextCodepoint`` * ) - 'Type' | '`' '`' ^ '`'
6+
enum IdentifierRule:ParsingRule
7+
{
8+
typealias Location = String.Index
9+
typealias Terminal = Unicode.Scalar
10+
11+
static func parse<Diagnostics>(
12+
_ input:inout ParsingInput<Diagnostics>) throws -> Range<Location> where
13+
Diagnostics:ParsingDiagnostics,
14+
Diagnostics.Source.Element == Terminal,
15+
Diagnostics.Source.Index == Location
16+
{
17+
let start:Location = input.index
18+
19+
if case ()? = input.parse(as: FirstCodepoint?.self)
20+
{
21+
input.parse(as: NextCodepoint.self, in: Void.self)
22+
}
23+
else
24+
{
25+
try input.parse(as: UnicodeEncoding<Location, Terminal>.Backtick.self)
26+
input.parse(as: RawCodepoint.self, in: Void.self)
27+
try input.parse(as: UnicodeEncoding<Location, Terminal>.Backtick.self)
28+
}
29+
30+
let end:Location = input.index
31+
32+
if input[start ..< end].elementsEqual(["T", "y", "p", "e"])
33+
{
34+
throw IdentifierError.reserved
35+
}
36+
else
37+
{
38+
return start ..< end
39+
}
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)