diff --git a/crates/typst/src/eval/flow.rs b/crates/typst/src/eval/flow.rs index d25d5472c..93d6da859 100644 --- a/crates/typst/src/eval/flow.rs +++ b/crates/typst/src/eval/flow.rs @@ -152,7 +152,11 @@ impl Eval for ast::ForLoop<'_> { // Iterate over graphemes of string. iter!(for pattern in str.as_str().graphemes(true)); } - (Pattern::Destructuring(_), Value::Str(_)) => { + (Pattern::Normal(_) | Pattern::Placeholder(_), Value::Bytes(bytes)) => { + // Iterate over the integers of bytes. + iter!(for pattern in bytes.as_slice()); + } + (Pattern::Destructuring(_), Value::Str(_) | Value::Bytes(_)) => { bail!(pattern.span(), "cannot destructure values of {}", iterable_type); } _ => { diff --git a/crates/typst/src/foundations/bytes.rs b/crates/typst/src/foundations/bytes.rs index b2fd0e3bd..3c6fa1fa8 100644 --- a/crates/typst/src/foundations/bytes.rs +++ b/crates/typst/src/foundations/bytes.rs @@ -13,12 +13,13 @@ use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value /// A sequence of bytes. /// /// This is conceptually similar to an array of [integers]($int) between `{0}` -/// and `{255}`, but represented much more efficiently. +/// and `{255}`, but represented much more efficiently. You can iterate over it +/// using a [for loop]($scripting/#loops). /// /// You can convert /// - a [string]($str) or an [array]($array) of integers to bytes with the /// [`bytes`]($bytes) constructor -/// - bytes to a string with the [`str`]($str) constructor +/// - bytes to a string with the [`str`]($str) constructor, with UTF-8 encoding /// - bytes to an array of integers with the [`array`]($array) constructor /// /// When [reading]($read) data from a file, you can decide whether to load it diff --git a/docs/reference/scripting.md b/docs/reference/scripting.md index ee9478bb1..6e64cebbb 100644 --- a/docs/reference/scripting.md +++ b/docs/reference/scripting.md @@ -188,13 +188,6 @@ together into one larger array. For loops can iterate over a variety of collections: -- `{for letter in "abc" {..}}` \ - Iterates over the characters of the [string]($str). - (Technically, iterates over the grapheme clusters of the string. Most of the - time, a grapheme cluster is just a single character/codepoint. However, some - constructs like flag emojis that consist of multiple codepoints are still only - one cluster.) - - `{for value in array {..}}` \ Iterates over the items in the [array]($array). The destructuring syntax described in [Let binding]($scripting/#bindings) can also be used here. @@ -203,6 +196,17 @@ For loops can iterate over a variety of collections: Iterates over the key-value pairs of the [dictionary]($dictionary). The pairs can also be destructured by using `{for (key, value) in dict {..}}`. +- `{for letter in "abc" {..}}` \ + Iterates over the characters of the [string]($str). Technically, it iterates + over the grapheme clusters of the string. Most of the time, a grapheme cluster + is just a single codepoint. However, a grapheme cluster could contain multiple + codepoints, like a flag emoji. + +- `{for byte in bytes("😀") {..}}` \ + Iterates over the [bytes]($bytes), which can be converted from a [string]($str) + or [read]($read) from a file without encoding. Each byte value is an + [integer]($int) between `{0}` and `{255}`. + To control the execution of the loop, Typst provides the `{break}` and `{continue}` statements. The former performs an early exit from the loop while the latter skips ahead to the next iteration of the loop. diff --git a/tests/typ/bugs/3275-loop-errors.typ b/tests/typ/bugs/3275-loop-errors.typ index 0f05d6afe..9fdd2961b 100644 --- a/tests/typ/bugs/3275-loop-errors.typ +++ b/tests/typ/bugs/3275-loop-errors.typ @@ -6,17 +6,19 @@ #for x in (1, 2) {} #for x in (a: 1, b: 2) {} #for x in "foo" {} +#for x in bytes("😊") {} --- // Placeholder. #for _ in (1, 2) {} #for _ in (a: 1, b: 2) {} #for _ in "foo" {} +#for _ in bytes("😊") {} --- // Destructuring. -#for (k, v) in (("a", 1), ("b", 2), ("c", 3)) {} -#for (k, ..) in (("a", 1), ("b", 2), ("c", 3)) {} +#for (a,b,c) in (("a", 1, bytes(())), ("b", 2, bytes(""))) {} +#for (a, ..) in (("a", 1, bytes(())), ("b", 2, bytes(""))) {} #for (k, v) in (a: 1, b: 2, c: 3) {} #for (.., v) in (a: 1, b: 2, c: 3) {} @@ -25,8 +27,8 @@ #for x in [1, 2] {} --- -// Error: 11-25 cannot loop over bytes -#for _ in bytes((22, 0)) {} +// Error: 11-25 cannot loop over arguments +#for _ in arguments("a") {} --- // Error: 16-21 cannot loop over integer @@ -44,6 +46,22 @@ // Error: 6-12 cannot destructure string #for (x, y) in ("foo", "bar") {} +--- +// Error: 6-12 cannot destructure values of bytes +#for (x, y) in bytes("😊") {} + +--- +// Error: 6-12 cannot destructure bytes +#for (x, y) in (bytes((1,2)), bytes((1,2))) {} + --- // Error: 6-12 cannot destructure integer #for (x, y) in (1, 2) {} + +--- +// Error: 10-11 not enough elements to destructure +#for (x, y) in ((1,), (2,)) {} + +--- +// Error: 6-12 too many elements to destructure +#for (x, y) in ((1,2,3), (4,5,6)) {}