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  }