github.com/aretext/aretext@v1.3.0/syntax/parser/combinators.go (about) 1 package parser 2 3 // MapFn maps a successful parse result to another parse result. 4 type MapFn func(Result) Result 5 6 // Map maps a successful parse result to another parse result using mapFn. 7 func (f Func) Map(mapFn MapFn) Func { 8 return func(iter TrackingRuneIter, state State) Result { 9 result := f(iter, state) 10 if result.IsFailure() { 11 return FailedResult 12 } 13 return mapFn(result) 14 } 15 } 16 17 // MapWithInputFn maps a successful parse result to another parse result, 18 // using the original input (iter + state) that produced the first parse result. 19 type MapWithInputFn func(Result, TrackingRuneIter, State) Result 20 21 // MapWithInput maps a successful parse to another parse result according to mapFn. 22 // The input iterator will output only runes consumed by the result before returning EOF. 23 func (f Func) MapWithInput(mapFn MapWithInputFn) Func { 24 return func(iter TrackingRuneIter, state State) Result { 25 result := f(iter, state) 26 if result.IsFailure() { 27 return FailedResult 28 } 29 iter.Limit(result.NumConsumed) 30 return mapFn(result, iter, state) 31 } 32 } 33 34 // Then produces a parse func that succeeds if both `f` and `nextFn` succeed. 35 func (f Func) Then(nextFn Func) Func { 36 return func(iter TrackingRuneIter, state State) Result { 37 result := f(iter, state) 38 if result.IsFailure() { 39 return FailedResult 40 } 41 42 iter.Skip(result.NumConsumed) 43 nextResult := nextFn(iter, result.NextState) 44 if nextResult.IsFailure() { 45 return FailedResult 46 } 47 48 return combineSeqResults(result, nextResult) 49 } 50 } 51 52 // ThenMaybe produces a parse func that succeeds if `f` succeeds, 53 // optionally followed by a successful result from `nextFn`. 54 func (f Func) ThenMaybe(nextFn Func) Func { 55 return func(iter TrackingRuneIter, state State) Result { 56 result := f(iter, state) 57 if result.IsFailure() { 58 return FailedResult 59 } 60 61 iter.Skip(result.NumConsumed) 62 nextResult := nextFn(iter, result.NextState) 63 if nextResult.IsFailure() { 64 return result 65 } 66 67 return combineSeqResults(result, nextResult) 68 } 69 } 70 71 // ThenNot produces a parse func that succeeds if `f` succeeds, 72 // followed by an unsuccessful parse from `nextFn`. 73 func (f Func) ThenNot(nextFn Func) Func { 74 return func(iter TrackingRuneIter, state State) Result { 75 result := f(iter, state) 76 if result.IsFailure() { 77 return FailedResult 78 } 79 80 iter.Skip(result.NumConsumed) 81 nextResult := nextFn(iter, result.NextState) 82 if nextResult.IsSuccess() { 83 return FailedResult 84 } 85 86 return result 87 } 88 } 89 90 // MaybeBefore produces a parse func that attempts `f` then `nextFn`, 91 // falling back to `nextFn` if `f` fails. 92 func (f Func) MaybeBefore(nextFn Func) Func { 93 return func(iter TrackingRuneIter, state State) Result { 94 result := f(iter, state) 95 if result.IsFailure() { 96 return nextFn(iter, state) 97 } 98 99 iter.Skip(result.NumConsumed) 100 nextResult := nextFn(iter, result.NextState) 101 if nextResult.IsFailure() { 102 return FailedResult 103 } 104 105 return combineSeqResults(result, nextResult) 106 } 107 } 108 109 // combineSeqResults combines two adjacent results into a single result. 110 // The input results may be mutated. 111 func combineSeqResults(r1, r2 Result) Result { 112 tokens := r1.ComputedTokens 113 for _, tok := range r2.ComputedTokens { 114 tokens = append(tokens, ComputedToken{ 115 Offset: r1.NumConsumed + tok.Offset, 116 Length: tok.Length, 117 Role: tok.Role, 118 }) 119 } 120 121 return Result{ 122 NumConsumed: r1.NumConsumed + r2.NumConsumed, 123 ComputedTokens: tokens, 124 NextState: r2.NextState, 125 } 126 } 127 128 // Or produces a parse func that returns the result of `f` if it succeeds, 129 // or the result of `nextFn` if `f` fails. 130 func (f Func) Or(nextFn Func) Func { 131 return func(iter TrackingRuneIter, state State) Result { 132 result := f(iter, state) 133 if result.IsSuccess() { 134 return result 135 } 136 return nextFn(iter, state) 137 } 138 } 139 140 // recoverFromFailure consumes runes until the first successful parse. 141 func (f Func) recoverFromFailure() Func { 142 return func(iter TrackingRuneIter, state State) Result { 143 var numSkipped uint64 144 for { 145 result := f(iter, state) 146 if result.IsSuccess() { 147 return result.ShiftForward(numSkipped) 148 } 149 150 // Recover by skipping one rune ahead. 151 n := iter.Skip(1) 152 numSkipped += n 153 if n == 0 { 154 return Result{ 155 NumConsumed: numSkipped, 156 NextState: state, 157 } 158 } 159 } 160 } 161 }