stealthrocket / coroutine

Durable coroutines for Go

Home Page:http://stealthrocket.tech

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

coroc: support for range loops over strings

chriso opened this issue · comments

The desugaring pass should be updated to desugar for range loops over strings:

var s string
for range string { ... }
for index := range string { ... }
for index, rune := range string { ... }

At first glance it should simply be a matter of adding *types.Basic (with .Kind() == types.String) to the case where we handle *types.Array and *types.Slice here, however range loops are a bit different when strings are involved. When indexing a string you get back a byte. When using a for range loop with a string you get a rune along with an index representing its offset in the string. More info in https://go.dev/blog/strings.

The desugaring pass should use utf.DecodeRuneInString to parse runes from strings iteratively.

Test case:

diff --git i/compiler/coroutine_test.go w/compiler/coroutine_test.go
index 85fa53b..ca9be36 100644
--- i/compiler/coroutine_test.go
+++ w/compiler/coroutine_test.go
@@ -243,6 +243,16 @@ func TestCoroutineYield(t *testing.T) {
                        coro:   func() { IdentityGenericStructClosureInt(11) },
                        yields: []int{11, 100, 23, 12, 101, 45},
                },
+
+               {
+                       name: "range over string",
+                       coro: func() { RangeOverString("foo") },
+                       yields: []int{
+                               0, 0, 0,
+                               0, 1, 2,
+                               0, 'f', 1, 'o', 2, 'o',
+                       },
+               },
        }
 
        // This emulates the installation of function type information by the
diff --git i/compiler/testdata/coroutine.go w/compiler/testdata/coroutine.go
index 36b42ff..1a11938 100644
--- i/compiler/testdata/coroutine.go
+++ w/compiler/testdata/coroutine.go
@@ -636,3 +636,16 @@ func IdentityGenericStructClosureInt(n int) {
        fn(23)
        fn(45)
 }
+
+func RangeOverString(s string) {
+       for range s {
+               coroutine.Yield[int, any](0)
+       }
+       for i := range s {
+               coroutine.Yield[int, any](i)
+       }
+       for i, c := range s {
+               coroutine.Yield[int, any](i)
+               coroutine.Yield[int, any](int(c))
+       }
+}