Skip to content

Commit 73e7293

Browse files
committed
Require iteratable to be passed to foreach
1 parent 15a9959 commit 73e7293

3 files changed

Lines changed: 17 additions & 6 deletions

File tree

src/problem/messages.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,8 +1126,15 @@ of the document doesn't take ant parameters.
11261126
"#.trim_ascii().to_string(),
11271127
),
11281128
RunnerError::NotIterable => (
1129-
"Value is not a list".to_string(),
1130-
"The foreach keyword requires a list to iterate over, but the value suppliedisn't one.".to_string(),
1129+
"Iteration requires a list".to_string(),
1130+
r#"
1131+
The foreach loop control structure requires a list to iterate over, but the
1132+
value supplied isn't one. A tablet is a dictonary, not a sequence. If you want
1133+
to use the values from a tablet convert them into a list first with the
1134+
values() function. There is also a labels() function to get each of the
1135+
tablet's labels, and pairs() to get a sequence of tuples of labels and values
1136+
you can iterate over.
1137+
"#.trim_ascii().to_string(),
11311138
),
11321139
RunnerError::UserQuit => (
11331140
"Interrupted".to_string(),

src/runner/checks/runner.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -909,9 +909,10 @@ fn foreach_widens_primitive_to_singleton() {
909909
#[test]
910910
fn foreach_over_non_list_or_unbound_errors() {
911911
// foreach item in source, where `source` is supplied by the caller's
912-
// environment. A tuple or tablet source is `NotIterable` (only lists
913-
// iterate and only scalars widen); an unbound source propagates
914-
// `UnboundVariable` rather than being swallowed.
912+
// environment. A tuple or tablet source is `NotIterable` (lists iterate
913+
// and scalars widen, but a tablet is a record that must be projected via
914+
// values()/labels()/pairs() first, and a tuple does neither); an unbound
915+
// source propagates `UnboundVariable` rather than being swallowed.
915916
let names = [Identifier::new("item")];
916917
let loop_op = Operation::Loop {
917918
names: &names,
@@ -948,7 +949,8 @@ fn foreach_over_non_list_or_unbound_errors() {
948949
other => panic!("expected NotIterable, got {:?}", other),
949950
}
950951

951-
// A tablet bound to `source` is not a list either.
952+
// A tablet bound to `source` is a record, not a sequence: it must be
953+
// projected with values()/labels()/pairs() rather than iterated directly.
952954
let mut tablet_fixture = StoreFixture::new("foreach-tablet");
953955
let mut env = Environment::new();
954956
env.extend(

src/runner/runner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ impl<'i, P: Prompt> Runner<'i, P> {
263263
Value::Arraeum(items) => items,
264264
// A scalar in list context is a singleton list.
265265
value @ (Value::Literali(_) | Value::Quanticle(_)) => vec![value],
266+
// A tablet is a record, not a sequence, so it does not
267+
// iterate directly.
266268
_ => return Err(RunnerError::NotIterable),
267269
};
268270
for (i, item) in items

0 commit comments

Comments
 (0)