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  		return
    32  	}
    33  }
    34  
    35  // explodeSeq returns an iterator over the runes in s.
    36  func explodeSeq(s string) iter.Seq[string] {
    37  	return func(yield func(string) bool) {
    38  		for len(s) > 0 {
    39  			_, size := utf8.DecodeRuneInString(s)
    40  			if !yield(s[:size]) {
    41  				return
    42  			}
    43  			s = s[size:]
    44  		}
    45  	}
    46  }
    47  
    48  // splitSeq is SplitSeq or SplitAfterSeq, configured by how many
    49  // bytes of sep to include in the results (none or all).
    50  func splitSeq(s, sep string, sepSave int) iter.Seq[string] {
    51  	if len(sep) == 0 {
    52  		return explodeSeq(s)
    53  	}
    54  	return func(yield func(string) bool) {
    55  		for {
    56  			i := Index(s, sep)
    57  			if i < 0 {
    58  				break
    59  			}
    60  			frag := s[:i+sepSave]
    61  			if !yield(frag) {
    62  				return
    63  			}
    64  			s = s[i+len(sep):]
    65  		}
    66  		yield(s)
    67  	}
    68  }
    69  
    70  // SplitSeq returns an iterator over all substrings of s separated by sep.
    71  // The iterator yields the same strings that would be returned by Split(s, sep),
    72  // but without constructing the slice.
    73  // It returns a single-use iterator.
    74  func SplitSeq(s, sep string) iter.Seq[string] {
    75  	return splitSeq(s, sep, 0)
    76  }
    77  
    78  // SplitAfterSeq returns an iterator over substrings of s split after each instance of sep.
    79  // The iterator yields the same strings that would be returned by SplitAfter(s, sep),
    80  // but without constructing the slice.
    81  // It returns a single-use iterator.
    82  func SplitAfterSeq(s, sep string) iter.Seq[string] {
    83  	return splitSeq(s, sep, len(sep))
    84  }
    85  
    86  // FieldsSeq returns an iterator over substrings of s split around runs of
    87  // whitespace characters, as defined by unicode.IsSpace.
    88  // The iterator yields the same strings that would be returned by Fields(s),
    89  // but without constructing the slice.
    90  func FieldsSeq(s string) iter.Seq[string] {
    91  	return func(yield func(string) bool) {
    92  		start := -1
    93  		for i := 0; i < len(s); {
    94  			size := 1
    95  			r := rune(s[i])
    96  			isSpace := asciiSpace[s[i]] != 0
    97  			if r >= utf8.RuneSelf {
    98  				r, size = utf8.DecodeRuneInString(s[i:])
    99  				isSpace = unicode.IsSpace(r)
   100  			}
   101  			if isSpace {
   102  				if start >= 0 {
   103  					if !yield(s[start:i]) {
   104  						return
   105  					}
   106  					start = -1
   107  				}
   108  			} else if start < 0 {
   109  				start = i
   110  			}
   111  			i += size
   112  		}
   113  		if start >= 0 {
   114  			yield(s[start:])
   115  		}
   116  	}
   117  }
   118  
   119  // FieldsFuncSeq returns an iterator over substrings of s split around runs of
   120  // Unicode code points satisfying f(c).
   121  // The iterator yields the same strings that would be returned by FieldsFunc(s),
   122  // but without constructing the slice.
   123  func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string] {
   124  	return func(yield func(string) bool) {
   125  		start := -1
   126  		for i := 0; i < len(s); {
   127  			size := 1
   128  			r := rune(s[i])
   129  			if r >= utf8.RuneSelf {
   130  				r, size = utf8.DecodeRuneInString(s[i:])
   131  			}
   132  			if f(r) {
   133  				if start >= 0 {
   134  					if !yield(s[start:i]) {
   135  						return
   136  					}
   137  					start = -1
   138  				}
   139  			} else if start < 0 {
   140  				start = i
   141  			}
   142  			i += size
   143  		}
   144  		if start >= 0 {
   145  			yield(s[start:])
   146  		}
   147  	}
   148  }
   149  

View as plain text