|
11 | 11 |
|
12 | 12 | import _MatchingEngine |
13 | 13 |
|
14 | | -// TODO: what here should be in the compile-time module? |
| 14 | +/// A structured capture |
| 15 | +struct StructuredCapture { |
| 16 | + /// The `.optional` height of the result |
| 17 | + var optionalCount = 0 |
15 | 18 |
|
16 | | -enum Capture { |
17 | | - case atom(Any) |
18 | | - indirect case tuple([Capture]) |
19 | | - indirect case some(Capture) |
20 | | - case none(childType: AnyType) |
21 | | - indirect case array([Capture], childType: AnyType) |
22 | | -} |
23 | | - |
24 | | -extension Capture { |
25 | | - static func none(childType: Any.Type) -> Capture { |
26 | | - .none(childType: AnyType(childType)) |
27 | | - } |
| 19 | + var storedCapture: StoredCapture? |
28 | 20 |
|
29 | | - static func array(_ children: [Capture], childType: Any.Type) -> Capture { |
30 | | - .array(children, childType: AnyType(childType)) |
| 21 | + var someCount: Int { |
| 22 | + storedCapture == nil ? optionalCount - 1 : optionalCount |
31 | 23 | } |
32 | 24 | } |
33 | 25 |
|
34 | | -extension Capture { |
35 | | - static func tupleOrAtom(_ elements: [Capture]) -> Self { |
36 | | - elements.count == 1 ? elements[0] : .tuple(elements) |
37 | | - } |
38 | | - |
39 | | - static var void: Capture { |
40 | | - .tuple([]) |
41 | | - } |
| 26 | +/// A storage form for a successful capture |
| 27 | +struct StoredCapture { |
| 28 | + // TODO: drop optional when engine tracks all ranges |
| 29 | + var range: Range<String.Index>? |
42 | 30 |
|
43 | | - var value: Any { |
44 | | - switch self { |
45 | | - case .atom(let atom): |
46 | | - return atom |
47 | | - case .tuple(let elements): |
48 | | - return TypeConstruction.tuple( |
49 | | - of: elements.map(\.value)) |
50 | | - case .array(let elements, let childType): |
51 | | - func helper<T>(_: T.Type) -> Any { |
52 | | - elements.map { $0.value as! T } |
53 | | - } |
54 | | - return _openExistential(childType.base, do: helper) |
55 | | - case .some(let subcapture): |
56 | | - func helper<T>(_ value: T) -> Any { |
57 | | - Optional(value) as Any |
58 | | - } |
59 | | - return _openExistential(subcapture.value, do: helper) |
60 | | - case .none(let childType): |
61 | | - func helper<T>(_: T.Type) -> Any { |
62 | | - nil as T? as Any |
63 | | - } |
64 | | - return _openExistential(childType.base, do: helper) |
65 | | - } |
66 | | - } |
| 31 | + // If strongly typed, value is set |
| 32 | + var value: Any? = nil |
| 33 | +} |
67 | 34 |
|
68 | | - private func prepending(_ newElement: Any) -> Self { |
69 | | - switch self { |
70 | | - case .atom, .some, .none, .array: |
71 | | - return .tuple([.atom(newElement), self]) |
72 | | - case .tuple(let elements): |
73 | | - return .tuple([.atom(newElement)] + elements) |
74 | | - } |
| 35 | +// TODO: Where should this live? Inside TypeConstruction? |
| 36 | +func constructExistentialMatchComponent( |
| 37 | + from input: Substring, |
| 38 | + in range: Range<String.Index>?, |
| 39 | + value: Any?, |
| 40 | + optionalCount: Int |
| 41 | +) -> Any { |
| 42 | + let someCount: Int |
| 43 | + var underlying: Any |
| 44 | + if let v = value { |
| 45 | + underlying = v |
| 46 | + someCount = optionalCount |
| 47 | + } else if let r = range { |
| 48 | + underlying = input[r] |
| 49 | + someCount = optionalCount |
| 50 | + } else { |
| 51 | + // Ok since we Any-box every step up the ladder |
| 52 | + underlying = Optional<Any>(nil) as Any |
| 53 | + someCount = optionalCount - 1 |
75 | 54 | } |
76 | 55 |
|
77 | | - func matchValue(withWholeMatch wholeMatch: Substring) -> Any { |
78 | | - prepending(wholeMatch).value |
| 56 | + for _ in 0..<someCount { |
| 57 | + underlying = Optional(underlying) as Any |
79 | 58 | } |
| 59 | + return underlying |
80 | 60 | } |
81 | 61 |
|
82 | | -extension Capture: CustomStringConvertible { |
83 | | - public var description: String { |
84 | | - var printer = PrettyPrinter() |
85 | | - _print(&printer) |
86 | | - return printer.finish() |
| 62 | +extension StructuredCapture { |
| 63 | + func existentialMatchComponent( |
| 64 | + from input: Substring |
| 65 | + ) -> Any { |
| 66 | + constructExistentialMatchComponent( |
| 67 | + from: input, |
| 68 | + in: storedCapture?.range, |
| 69 | + value: storedCapture?.value, |
| 70 | + optionalCount: optionalCount) |
87 | 71 | } |
| 72 | +} |
88 | 73 |
|
89 | | - private func _print(_ printer: inout PrettyPrinter) { |
90 | | - switch self { |
91 | | - case let .atom(n): |
92 | | - printer.print("Atom(\(n))") |
93 | | - case let .tuple(ns): |
94 | | - if ns.isEmpty { |
95 | | - printer.print("Tuple()") |
96 | | - return |
97 | | - } |
98 | | - |
99 | | - printer.printBlock("Tuple") { printer in |
100 | | - for n in ns { |
101 | | - n._print(&printer) |
102 | | - } |
103 | | - } |
104 | | - |
105 | | - case let .some(n): |
106 | | - printer.printBlock("Some") { printer in |
107 | | - n._print(&printer) |
108 | | - } |
109 | | - |
110 | | - case let .none(childType): |
111 | | - printer.print("None(\(childType))") |
112 | | - |
113 | | - case let .array(ns, childType): |
114 | | - printer.printBlock("Array(\(childType))") { printer in |
115 | | - for n in ns { |
116 | | - n._print(&printer) |
117 | | - } |
118 | | - } |
119 | | - |
120 | | - } |
| 74 | +extension Sequence where Element == StructuredCapture { |
| 75 | + // FIXME: This is a stop gap where we still slice the input |
| 76 | + // and traffic through existentials |
| 77 | + func existentialMatch( |
| 78 | + from input: Substring |
| 79 | + ) -> Any { |
| 80 | + var caps = Array<Any>() |
| 81 | + caps.append(input) |
| 82 | + caps.append(contentsOf: self.map { |
| 83 | + $0.existentialMatchComponent(from: input) |
| 84 | + }) |
| 85 | + return TypeConstruction.tuple(of: caps) |
121 | 86 | } |
122 | 87 | } |
| 88 | + |
0 commit comments