summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/eval/flow.rs6
-rw-r--r--crates/typst/src/foundations/bytes.rs5
-rw-r--r--docs/reference/scripting.md18
-rw-r--r--tests/typ/bugs/3275-loop-errors.typ26
4 files changed, 41 insertions, 14 deletions
diff --git a/crates/typst/src/eval/flow.rs b/crates/typst/src/eval/flow.rs
index d25d5472..93d6da85 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 b2fd0e3b..3c6fa1fa 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 ee9478bb..6e64cebb 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 0f05d6af..9fdd2961 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
@@ -45,5 +47,21 @@
#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)) {}