Source file src/strings/iter.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package strings
     6  
     7  import (
     8  	"iter"
     9  	"unicode"
    10  	"unicode/utf8"
    11  )
    12  
    13  // Lines returns an iterator over the newline-terminated lines in the string s.
    14  // The lines yielded by the iterator include their terminating newlines.
    15  // If s is empty, the iterator yields no lines at all.
    16  // If s does not end in a newline, the final yielded line will not end in a newline.
    17  // It returns a single-use iterator.
    18  func Lines(s string) iter.Seq[string] {
    19  	return func(yield func(string) bool) {
    20  		for len(s) > 0 {
    21  			var line string
    22  			if i := IndexByte(s, '\n'); i >= 0 {
    23  				line, s = s[:i+1], s[i+1:]
    24  			} else {
    25  				line, s = s, ""
    26  			}
    27  			if !yield(line) {
    28  				return
    29  			}
    30  		}
    31  	}
    32  }
    33  
    34  // explodeSeq returns an iterator over the runes in s.
    35  func explodeSeq(s string, yield func(string) bool) {
    36  	for len(s) > 0 {
    37  		_, size := utf8.DecodeRuneInString(s)
    38  		if !yield(s[:size]) {
    39  			return
    40  		}
    41  		s = s[size:]
    42  	}
    43  }
    44  
    45  // splitSeq is SplitSeq or SplitAfterSeq, configured by how many
    46  // bytes of sep to include in the results (none or all).
    47  func splitSeq(s, sep string, sepSave int) iter.Seq[string] {
    48  	return func(yield func(string) bool) {
    49  		if len(sep) == 0 {
    50  			explodeSeq(s, yield)
    51  			return
    52  		}
    53  		for {
    54  			i := Index(s, sep)
    55  			if i < 0 {
    56  				break
    57  			}
    58  			frag := s[:i+sepSave]
    59  			if !yield(frag) {
    60  				return
    61  			}
    62  			s = s[i+len(sep):]
    63  		}
    64  		yield(s)
    65  	}
    66  }
    67  
    68  // SplitSeq returns an iterator over all substrings of s separated by sep.
    69  // The iterator yields the same strings that would be returned by [Split](s, sep),
    70  // but without constructing the slice.
    71  // It returns a single-use iterator.
    72  func SplitSeq(s, sep string) iter.Seq[string] {
    73  	return splitSeq(s, sep, 0)
    74  }
    75  
    76  // SplitAfterSeq returns an iterator over substrings of s split after each instance of sep.
    77  // The iterator yields the same strings that would be returned by [SplitAfter](s, sep),
    78  // but without constructing the slice.
    79  // It returns a single-use iterator.
    80  func SplitAfterSeq(s, sep string) iter.Seq[string] {
    81  	return splitSeq(s, sep, len(sep))
    82  }
    83  
    84  // FieldsSeq returns an iterator over substrings of s split around runs of
    85  // whitespace characters, as defined by [unicode.IsSpace].
    86  // The iterator yields the same strings that would be returned by [Fields](s),
    87  // but without constructing the slice.
    88  func FieldsSeq(s string) iter.Seq[string] {
    89  	return func(yield func(string) bool) {
    90  		start := -1
    91  		for i := 0; i < len(s); {
    92  			size := 1
    93  			r := rune(s[i])
    94  			isSpace := asciiSpace[s[i]] != 0
    95  			if r >= utf8.RuneSelf {
    96  				r, size = utf8.DecodeRuneInString(s[i:])
    97  				isSpace = unicode.IsSpace(r)
    98  			}
    99  			if isSpace {
   100  				if start >= 0 {
   101  					if !yield(s[start:i]) {
   102  						return
   103  					}
   104  					start = -1
   105  				}
   106  			} else if start < 0 {
   107  				start = i
   108  			}
   109  			i += size
   110  		}
   111  		if start >= 0 {
   112  			yield(s[start:])
   113  		}
   114  	}
   115  }
   116  
   117  // FieldsFuncSeq returns an iterator over substrings of s split around runs of
   118  // Unicode code points satisfying f(c).
   119  // The iterator yields the same strings that would be returned by [FieldsFunc](s),
   120  // but without constructing the slice.
   121  func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string] {
   122  	return func(yield func(string) bool) {
   123  		start := -1
   124  		for i := 0; i < len(s); {
   125  			size := 1
   126  			r := rune(s[i])
   127  			if r >= utf8.RuneSelf {
   128  				r, size = utf8.DecodeRuneInString(s[i:])
   129  			}
   130  			if f(r) {
   131  				if start >= 0 {
   132  					if !yield(s[start:i]) {
   133  						return
   134  					}
   135  					start = -1
   136  				}
   137  			} else if start < 0 {
   138  				start = i
   139  			}
   140  			i += size
   141  		}
   142  		if start >= 0 {
   143  			yield(s[start:])
   144  		}
   145  	}
   146  }
   147  

View as plain text