5
5
// Created by Kåre Morstøl on 25/05/2020.
6
6
//
7
7
8
- /// Skips 0 or more elements until a match for the next patterns are found.
8
+ /// Skips 0 or more elements until a match for the next patterns is found.
9
9
///
10
- /// If this is at the end of a pattern, it skips to the end of input.
10
+ /// ```swift
11
+ /// let s = Skip() • a
12
+ /// ```
13
+ /// is the same as `|S <- A / . <S>|` in standard PEG.
14
+ ///
15
+ /// - note:
16
+ /// If `Skip` is at the end of a pattern, it just succeeds without consuming input. So it will be pointless.
17
+ ///
18
+ /// But this works:
19
+ /// ```swift
20
+ /// let s = Skip()
21
+ /// let p = s • " "
22
+ /// ```
23
+ /// because here the `s` pattern is "inlined".
24
+ ///
25
+ /// This, however, does not work:
26
+ /// ```swift
27
+ /// let g = Grammar { g in
28
+ /// g.nextSpace <- g.skip • " "
29
+ /// g.skip <- Skip()
30
+ /// }
31
+ /// ```
32
+ /// because in grammars the subexpressions are called, like functions, not "inlined", like Swift variables.
33
+ /// So the `Skip()` in `g.skip` can't tell what will come after it.
11
34
public struct Skip < Input: BidirectionalCollection > : Pattern where Input. Element: Hashable {
12
35
public var description : String { " Skip() " }
13
36
37
+ @inlinable
14
38
public init ( ) { }
15
39
40
+ @inlinable
16
41
public init ( ) where Input == String { }
17
42
18
43
@inlinable
@@ -24,6 +49,7 @@ public struct Skip<Input: BidirectionalCollection>: Pattern where Input.Element:
24
49
import SE0270_RangeSet
25
50
26
51
extension MutableCollection where Self: RandomAccessCollection , Self: RangeReplaceableCollection , Index == Int {
52
+ /// Replaces all placeholder `.skip` instructions.
27
53
@usableFromInline
28
54
mutating func replaceSkips< Input> ( ) where Element == Instruction < Input > {
29
55
// `setupSkip(at: i)` adds 1 new instruction somewhere after `ì`, so we cant loop over self.indices directly
@@ -38,26 +64,30 @@ extension MutableCollection where Self: RandomAccessCollection, Self: RangeRepla
38
64
} while i < self . endIndex
39
65
}
40
66
67
+ /// Replaces the dummy `.skip` instruction at `skipIndex` with one that will search using the instructions
68
+ /// right after `skipIndex`.
41
69
@usableFromInline
42
70
mutating func setupSkip< Input> ( at skipIndex: Index ) where Element == Instruction < Input > {
43
- let searchablesStartAt = skipIndex + 1
44
- switch self [ searchablesStartAt ] {
71
+ let afterSkip = skipIndex + 1
72
+ switch self [ afterSkip ] {
45
73
case let . checkIndex( function, atIndexOffset: 0 ) :
46
74
self [ skipIndex] = . search { input, index in
47
75
input [ index... ] . indices. first ( where: { function ( input, $0) } )
48
76
?? ( function ( input, input. endIndex) ? input. endIndex : nil )
49
77
}
50
- self [ searchablesStartAt ] = . choice( offset: - 1 , atIndexOffset: 1 )
78
+ self [ afterSkip ] = . choice( offset: - 1 , atIndexOffset: 1 )
51
79
case . checkIndex( _, atIndexOffset: _) :
52
- fatalError ( " Cannot see a valid reason for a `.checkIndex` with a non-zero offset to be located right after a `.skip` instruction. " ) // Correct me if I'm wrong.
80
+ // A `.checkIndex` will only have a non-zero offset if it has been moved by `moveMovablesForward`,
81
+ // and that will never move anything beyond a `.skip`.
82
+ fatalError ( " A `.checkIndex` with a non-zero offset can't be located right after a `.skip` instruction. " )
53
83
case let . checkElement( test) :
54
84
self [ skipIndex] = . search { input, index in
55
85
input [ index... ] . firstIndex ( where: test)
56
86
. map ( input. index ( after: ) )
57
87
}
58
- self [ searchablesStartAt ] = . choice( offset: - 1 , atIndexOffset: 0 )
88
+ self [ afterSkip ] = . choice( offset: - 1 , atIndexOffset: 0 )
59
89
case . elementEquals:
60
- let elements : [ Input . Element ] = self [ searchablesStartAt ... ]
90
+ let elements : [ Input . Element ] = self [ afterSkip ... ]
61
91
. mapPrefix {
62
92
switch $0 {
63
93
case let . elementEquals( element) :
@@ -71,14 +101,14 @@ extension MutableCollection where Self: RandomAccessCollection, Self: RangeRepla
71
101
input [ index... ] . firstIndex ( of: elements [ 0 ] )
72
102
. map ( input. index ( after: ) )
73
103
}
74
- self [ searchablesStartAt ] = . choice( offset: - 1 , atIndexOffset: 0 )
104
+ self [ afterSkip ] = . choice( offset: - 1 , atIndexOffset: 0 )
75
105
} else {
76
106
let cache = SearchCache ( elements)
77
107
self [ skipIndex] = . search { input, index in
78
108
input. range ( of: cache, from: index) ? . upperBound
79
109
}
80
- self [ searchablesStartAt ] = . choice( offset: - 1 , atIndexOffset: ( - elements. count) + 1 )
81
- self [ searchablesStartAt + 1 ] = . jump( offset: elements. count - 1 )
110
+ self [ afterSkip ] = . choice( offset: - 1 , atIndexOffset: ( - elements. count) + 1 )
111
+ self [ afterSkip + 1 ] = . jump( offset: elements. count - 1 )
82
112
}
83
113
default :
84
114
self [ skipIndex] = . choice( offset: 0 , atIndexOffset: + 1 )
@@ -89,16 +119,15 @@ extension MutableCollection where Self: RandomAccessCollection, Self: RangeRepla
89
119
}
90
120
91
121
@usableFromInline
92
- mutating func placeSkipCommit< Input> ( startSearchFrom: Index )
93
- where Element == Instruction < Input > {
122
+ mutating func placeSkipCommit< Input> ( startSearchFrom: Index ) where Element == Instruction < Input > {
94
123
var i = startSearchFrom
95
124
loop: while true {
96
125
switch self [ i] {
97
126
case let . choice( _, indexOffset) where indexOffset < 0 :
98
127
fatalError ( " Not implemented. " )
99
128
case let . choice( offset, _) :
100
129
// Follow every choice offset.
101
- // If one step back there is a jump forwards, then it's a '/' operation . So follow it too.
130
+ // If one step back there is a jump forwards, then it's a '/' pattern . So follow that jump too.
102
131
if case let . jump( jumpOffset) = self [ i + offset - 1 ] , jumpOffset > 0 {
103
132
i += offset - 1 + jumpOffset
104
133
} else {
@@ -112,17 +141,22 @@ extension MutableCollection where Self: RandomAccessCollection, Self: RangeRepla
112
141
insertInstructions ( . commit, at: i)
113
142
return
114
143
case . openCall:
115
- fatalError ( )
144
+ fatalError ( " `.openCall` instruction should have been replaced. " )
116
145
}
117
146
}
118
147
}
119
148
120
- /// Inserts new instructions at `location`. Adjusts the offsets of the other instructions accordingly.
149
+ /// Inserts `newInstructions` at `location`. Adjusts the offsets of the other instructions accordingly.
150
+ ///
151
+ /// Since all offsets are relative to the positions of their instructions,
152
+ /// if `location` lies between an instruction with an offset and where that offset leads to,
153
+ /// the offset needs to be increased by the length of `newInstructions`.
121
154
@usableFromInline
122
155
mutating func insertInstructions< Input> ( _ newInstructions: Element ... , at location: Index )
123
156
where Element == Instruction < Input > {
124
157
insert ( contentsOf: newInstructions, at: location)
125
158
let insertedRange = location ..< ( location + newInstructions. count + 1 )
159
+ /// instruction ... location ... offsetTarget
126
160
for i in startIndex ..< insertedRange. lowerBound {
127
161
switch self [ i] {
128
162
case let . call( offset) where offset > ( location - i) :
@@ -135,6 +169,7 @@ extension MutableCollection where Self: RandomAccessCollection, Self: RangeRepla
135
169
break
136
170
}
137
171
}
172
+ /// offsetTarget ... location ... instruction
138
173
for i in insertedRange. upperBound ..< endIndex {
139
174
switch self [ i] {
140
175
case let . call( offset) where offset < ( location - i) :
0 commit comments