Skip to content

Commit da801cc

Browse files
authored
Change AST for iterations to use iteration kind (#433)
The `=` node which has traditionally been used for iteration specifications in `for` loops and generators doesn't have normal assignment semantics. Let's consider for x in xs body end which has been parsed as `(for (= x xs) (block body))`. Problems: * The iteration does create a binding for `x`, but not to the expression on the right hand side of the `=`. * The user may use `in` or `∈` in the source code rather than `=`. The parser still uses a `=` node for consistency but this only emphasizes that there's something a bit weird going on. So this use of `=` is not assignment; merely assignment-like. In this change, we use `in` instead of `=` and wrap this in an `iteration` node so that all iteration (including over multiple iterators) has the same structure. Thus the `for` loop above parses as `(for (iteration (in x xs)) (block body))` instead. The `cartesian_iteration` head naturally becomes `iteration` instead - being less specific here with the naming seems appropriate in trying to represent the surface syntax; cartesian semantics come later in lowering and a macro may decide to do something else with the iteration spec. These changes are also used for generators. After the changes we have tree structures such as julia> parsestmt(SyntaxNode, "for i in is body end") line:col│ tree │ file_name 1:1 │[for] 1:4 │ [iteration] 1:4 │ [in] 1:5 │ i 1:10 │ is 1:12 │ [block] 1:13 │ body julia> parsestmt(SyntaxNode, "for i in is, j in js body end") line:col│ tree │ file_name 1:1 │[for] 1:4 │ [iteration] 1:4 │ [in] 1:5 │ i 1:10 │ is 1:13 │ [in] 1:14 │ j 1:19 │ js 1:21 │ [block] 1:22 │ body julia> parsestmt(SyntaxNode, "[a for i = is, j = js if z]") line:col│ tree │ file_name 1:1 │[comprehension] 1:2 │ [generator] 1:2 │ a 1:7 │ [filter] 1:7 │ [iteration] 1:7 │ [in] 1:8 │ i 1:12 │ is 1:15 │ [in] 1:16 │ j 1:20 │ js 1:26 │ z julia> parsestmt(SyntaxNode, "[a for i = is for j = js if z]") line:col│ tree │ file_name 1:1 │[comprehension] 1:2 │ [generator] 1:2 │ a 1:7 │ [iteration] 1:7 │ [in] 1:8 │ i 1:12 │ is 1:18 │ [filter] 1:18 │ [iteration] 1:18 │ [in] 1:19 │ j 1:23 │ js 1:29 │ z
1 parent fd55088 commit da801cc

File tree

5 files changed

+78
-83
lines changed

5 files changed

+78
-83
lines changed

docs/src/reference.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class of tokenization errors and lets the parser deal with them.
7676
* Using `try catch else finally end` is parsed with `K"catch"` `K"else"` and `K"finally"` children to avoid the awkwardness of the optional child nodes in the `Expr` representation (#234)
7777
* The dotted import path syntax as in `import A.b.c` is parsed with a `K"importpath"` kind rather than `K"."`, because a bare `A.b.c` has a very different nested/quoted expression representation (#244)
7878
* We use flags rather than child nodes to represent the difference between `struct` and `mutable struct`, `module` and `baremodule` (#220)
79-
* Multiple iterations within the header of a `for`, as in `for a=as, b=bs body end` are represented with a `cartesian_iterator` head rather than a `block`, as these lists of iterators are neither semantically nor syntactically a sequence of statements. Unlike other uses of `block` (see also generators).
79+
* Iterations are represented with the `iteration` head rather than `=` within the header of a `for`. Thus `for i=is ; body end` parses to `(for (iteration i is) (block body))`. Cartesian iteration as in `for a=as, b=bs body end` are represented with a longer `iteration` block rather than a `block` containing `=` because these lists of iterators are neither semantically nor syntactically a sequence of statements, unlike other uses of `block`. Generators also use the `iteration` head - see information on that below.
8080

8181
## More detail on tree differences
8282

@@ -90,8 +90,10 @@ mean
9090

9191
```
9292
for x in xs
93-
for y in ys
94-
push!(xy, collection)
93+
for y in ys
94+
push!(xy, collection)
95+
end
96+
end
9597
```
9698

9799
so the `xy` prefix is in the *body* of the innermost for loop. Following this,
@@ -112,27 +114,25 @@ source order.
112114

113115
However, our green tree is strictly source-ordered, so we must deviate from the
114116
Julia AST. We deal with this by grouping cartesian products of iterators
115-
(separated by commas) within `cartesian_iterator` blocks as in `for` loops, and
116-
use the presence of multiple iterator blocks rather than the `flatten` head to
117+
(separated by commas) within `iteration` blocks as in `for` loops, and
118+
use the length of the `iteration` block rather than the `flatten` head to
117119
distinguish flattened iterators. The nested flattens and generators of `Expr`
118120
forms are reconstructed later. In this form the tree structure resembles the
119121
source much more closely. For example, `(xy for x in xs for y in ys)` is parsed as
120122

121123
```
122124
(generator
123125
xy
124-
(= x xs)
125-
(= y ys))
126+
(iteration x xs)
127+
(iteration y ys))
126128
```
127129

128130
And the cartesian iteration `(xy for x in xs, y in ys)` is parsed as
129131

130132
```
131133
(generator
132134
xy
133-
(cartesian_iterator
134-
(= x xs)
135-
(= y ys)))
135+
(iteration x xs y ys))
136136
```
137137

138138
### Whitespace trivia inside strings

src/expr.jl

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,17 @@ function _extract_do_lambda!(args)
198198
end
199199
end
200200

201+
function _append_iterspec!(args, ex)
202+
if @isexpr(ex, :iteration)
203+
for iter in ex.args::Vector{Any}
204+
push!(args, Expr(:(=), iter.args...))
205+
end
206+
else
207+
push!(args, ex)
208+
end
209+
return args
210+
end
211+
201212
# Convert internal node of the JuliaSyntax parse tree to an Expr
202213
function _internal_node_to_Expr(source, srcrange, head, childranges, childheads, args)
203214
k = kind(head)
@@ -301,10 +312,8 @@ function _internal_node_to_Expr(source, srcrange, head, childranges, childheads,
301312
# Move parameters blocks to args[2]
302313
_reorder_parameters!(args, 2)
303314
elseif k == K"for"
304-
a1 = args[1]
305-
if @isexpr(a1, :cartesian_iterator)
306-
args[1] = Expr(:block, a1.args...)
307-
end
315+
iters = _append_iterspec!([], args[1])
316+
args[1] = length(iters) == 1 ? only(iters) : Expr(:block, iters...)
308317
# Add extra line number node for the `end` of the block. This may seem
309318
# useless but it affects code coverage.
310319
push!(args[2].args, endloc)
@@ -360,12 +369,8 @@ function _internal_node_to_Expr(source, srcrange, head, childranges, childheads,
360369
# source-ordered `generator` format.
361370
gen = args[1]
362371
for j = length(args):-1:2
363-
aj = args[j]
364-
if @isexpr(aj, :cartesian_iterator)
365-
gen = Expr(:generator, gen, aj.args...)
366-
else
367-
gen = Expr(:generator, gen, aj)
368-
end
372+
gen = Expr(:generator, gen)
373+
_append_iterspec!(gen.args, args[j])
369374
if j < length(args)
370375
# Additional `for`s flatten the inner generator
371376
gen = Expr(:flatten, gen)
@@ -374,14 +379,7 @@ function _internal_node_to_Expr(source, srcrange, head, childranges, childheads,
374379
return gen
375380
elseif k == K"filter"
376381
@assert length(args) == 2
377-
iterspec = args[1]
378-
outargs = Any[args[2]]
379-
if @isexpr(iterspec, :cartesian_iterator)
380-
append!(outargs, iterspec.args)
381-
else
382-
push!(outargs, iterspec)
383-
end
384-
args = outargs
382+
args = _append_iterspec!(Any[args[2]], args[1])
385383
elseif k == K"nrow" || k == K"ncat"
386384
# For lack of a better place, the dimension argument to nrow/ncat
387385
# is stored in the flags

src/kinds.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,7 @@ register_kinds!(JuliaSyntax, 0, [
10991099
# Comprehensions
11001100
"generator"
11011101
"filter"
1102-
"cartesian_iterator"
1102+
"iteration"
11031103
"comprehension"
11041104
"typed_comprehension"
11051105
# Container for a single statement/atom plus any trivia and errors

src/parser.jl

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ function parse_where_chain(ps0::ParseState, mark)
10751075
# x where {T,S} ==> (where x (braces T S))
10761076
# Also various nonsensical forms permitted
10771077
# x where {T S} ==> (where x (bracescat (row T S)))
1078-
# x where {y for y in ys} ==> (where x (braces (generator y (= y ys))))
1078+
# x where {y for y in ys} ==> (where x (braces (generator y (iteration (in y ys)))))
10791079
m = position(ps)
10801080
bump(ps, TRIVIA_FLAG)
10811081
ckind, cflags = parse_cat(ps, K"}", ps.end_symbol)
@@ -1578,7 +1578,7 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15781578
# T[x y] ==> (typed_hcat T x y)
15791579
# T[x ; y] ==> (typed_vcat T x y)
15801580
# T[a b; c d] ==> (typed_vcat T (row a b) (row c d))
1581-
# T[x for x in xs] ==> (typed_comprehension T (generator x (= x xs)))
1581+
# T[x for x in xs] ==> (typed_comprehension T (generator x (iteration (in x xs))))
15821582
#v1.8: T[a ; b ;; c ; d] ==> (typed_ncat-2 T (nrow-1 a b) (nrow-1 c d))
15831583
outk = ckind == K"vect" ? K"ref" :
15841584
ckind == K"hcat" ? K"typed_hcat" :
@@ -1798,8 +1798,8 @@ function parse_resword(ps::ParseState)
17981798
bump_closing_token(ps, K"end")
17991799
emit(ps, mark, K"while")
18001800
elseif word == K"for"
1801-
# for x in xs end ==> (for (= x xs) (block))
1802-
# for x in xs, y in ys \n a \n end ==> (for (cartesian_iterator (= x xs) (= y ys)) (block a))
1801+
# for x in xs end ==> (for (iteration (in x xs)) (block))
1802+
# for x in xs, y in ys \n a \n end ==> (for (iteration (in x xs) (in y ys)) (block a))
18031803
bump(ps, TRIVIA_FLAG)
18041804
parse_iteration_specs(ps)
18051805
parse_block(ps)
@@ -2621,11 +2621,11 @@ function parse_iteration_spec(ps::ParseState)
26212621
if peek_behind(ps).orig_kind == K"outer"
26222622
if peek_skip_newline_in_gen(ps) in KSet"= in ∈"
26232623
# Not outer keyword
2624-
# outer = rhs ==> (= outer rhs)
2625-
# outer <| x = rhs ==> (= (call-i outer <| x) rhs)
2624+
# outer = rhs ==> (iteration (in outer rhs))
2625+
# outer <| x = rhs ==> (iteration (in (call-i outer <| x) rhs))
26262626
else
2627-
# outer i = rhs ==> (= (outer i) rhs)
2628-
# outer (x,y) = rhs ==> (= (outer (tuple-p x y)) rhs)
2627+
# outer i = rhs ==> (iteration (in (outer i) rhs))
2628+
# outer (x,y) = rhs ==> (iteration (in (outer (tuple-p x y)) rhs))
26292629
reset_node!(ps, position(ps), kind=K"outer", flags=TRIVIA_FLAG)
26302630
parse_pipe_lt(ps)
26312631
emit(ps, mark, K"outer")
@@ -2641,17 +2641,15 @@ function parse_iteration_spec(ps::ParseState)
26412641
end
26422642
# Or try parse_pipe_lt ???
26432643
end
2644-
emit(ps, mark, K"=")
2644+
emit(ps, mark, K"in")
26452645
end
26462646

26472647
# Parse an iteration spec, or a comma separate list of such for for loops and
26482648
# generators
26492649
function parse_iteration_specs(ps::ParseState)
26502650
mark = position(ps)
26512651
n_iters = parse_comma_separated(ps, parse_iteration_spec)
2652-
if n_iters > 1
2653-
emit(ps, mark, K"cartesian_iterator")
2654-
end
2652+
emit(ps, mark, K"iteration")
26552653
end
26562654

26572655
# flisp: parse-space-separated-exprs
@@ -2701,27 +2699,27 @@ end
27012699
# Parse generators
27022700
#
27032701
# We represent generators quite differently from `Expr`:
2704-
# * Cartesian products of iterators are grouped within cartesian_iterator
2702+
# * Iteration variables and their iterators are grouped within K"iteration"
27052703
# nodes, as in the short form of `for` loops.
27062704
# * The `generator` kind is used for both cartesian and flattened generators
27072705
#
2708-
# (x for a in as for b in bs) ==> (parens (generator x (= a as) (= b bs)))
2709-
# (x for a in as, b in bs) ==> (parens (generator x (cartesian_iterator (= a as) (= b bs))))
2710-
# (x for a in as, b in bs if z) ==> (parens (generator x (filter (cartesian_iterator (= a as) (= b bs)) z)))
2706+
# (x for a in as for b in bs) ==> (parens (generator x (iteration (in a as)) (iteration (in b bs))))
2707+
# (x for a in as, b in bs) ==> (parens (generator x (iteration (in a as) (in b bs))))
2708+
# (x for a in as, b in bs if z) ==> (parens (generator x (filter (iteration (in a as) (in b bs)) z)))
27112709
#
27122710
# flisp: parse-generator
27132711
function parse_generator(ps::ParseState, mark)
27142712
while (t = peek_token(ps); kind(t) == K"for")
27152713
if !preceding_whitespace(t)
2716-
# ((x)for x in xs) ==> (parens (generator (parens x) (error) (= x xs)))
2714+
# ((x)for x in xs) ==> (parens (generator (parens x) (error) (iteration (in x xs))))
27172715
bump_invisible(ps, K"error", TRIVIA_FLAG,
27182716
error="Expected space before `for` in generator")
27192717
end
27202718
bump(ps, TRIVIA_FLAG)
27212719
iter_mark = position(ps)
27222720
parse_iteration_specs(ps)
27232721
if peek(ps) == K"if"
2724-
# (x for a in as if z) ==> (parens (generator x (filter (= a as) z)))
2722+
# (x for a in as if z) ==> (parens (generator x (filter (iteration (in a as)) z)))
27252723
bump(ps, TRIVIA_FLAG)
27262724
parse_cond(ps)
27272725
emit(ps, iter_mark, K"filter")
@@ -2732,7 +2730,7 @@ end
27322730

27332731
# flisp: parse-comprehension
27342732
function parse_comprehension(ps::ParseState, mark, closer)
2735-
# [x for a in as] ==> (comprehension (generator x a in as))
2733+
# [x for a in as] ==> (comprehension (generator x (iteration (in a as))))
27362734
ps = ParseState(ps, whitespace_newline=true,
27372735
space_sensitive=false,
27382736
end_symbol=false)
@@ -2982,8 +2980,8 @@ function parse_cat(ps::ParseState, closer, end_is_symbol)
29822980
# [x ==> (vect x (error-t))
29832981
parse_vect(ps, closer)
29842982
elseif k == K"for"
2985-
# [x for a in as] ==> (comprehension (generator x (= a as)))
2986-
# [x \n\n for a in as] ==> (comprehension (generator x (= a as)))
2983+
# [x for a in as] ==> (comprehension (generator x (iteration (in a as))))
2984+
# [x \n\n for a in as] ==> (comprehension (generator x (iteration (in a as))))
29872985
parse_comprehension(ps, mark, closer)
29882986
else
29892987
# [x y] ==> (hcat x y)
@@ -3139,8 +3137,7 @@ function parse_brackets(after_parse::Function,
31393137
continue
31403138
elseif k == K"for"
31413139
# Generator syntax
3142-
# (x for a in as) ==> (parens (generator x (= a as)))
3143-
# (x \n\n for a in as) ==> (parens (generator x (= a as)))
3140+
# (x for a in as) ==> (parens (generator x (iteration (in a as))))
31443141
parse_generator(ps, mark)
31453142
else
31463143
# Error - recovery done when consuming closing_kind

test/parser.jl

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ tests = [
300300
"x where \n {T}" => "(where x (braces T))"
301301
"x where {T,S}" => "(where x (braces T S))"
302302
"x where {T S}" => "(where x (bracescat (row T S)))"
303-
"x where {y for y in ys}" => "(where x (braces (generator y (= y ys))))"
303+
"x where {y for y in ys}" => "(where x (braces (generator y (iteration (in y ys)))))"
304304
"x where T" => "(where x T)"
305305
"x where \n T" => "(where x T)"
306306
"x where T<:S" => "(where x (<: T S))"
@@ -389,7 +389,7 @@ tests = [
389389
"T[x y]" => "(typed_hcat T x y)"
390390
"T[x ; y]" => "(typed_vcat T x y)"
391391
"T[a b; c d]" => "(typed_vcat T (row a b) (row c d))"
392-
"T[x for x in xs]" => "(typed_comprehension T (generator x (= x xs)))"
392+
"T[x for x in xs]" => "(typed_comprehension T (generator x (iteration (in x xs))))"
393393
((v=v"1.8",), "T[a ; b ;; c ; d]") => "(typed_ncat-2 T (nrow-1 a b) (nrow-1 c d))"
394394

395395
# Dotted forms
@@ -461,8 +461,8 @@ tests = [
461461
"while cond body end" => "(while cond (block body))"
462462
"while x < y \n a \n b \n end" => "(while (call-i x < y) (block a b))"
463463
# for
464-
"for x in xs end" => "(for (= x xs) (block))"
465-
"for x in xs, y in ys \n a \n end" => "(for (cartesian_iterator (= x xs) (= y ys)) (block a))"
464+
"for x in xs end" => "(for (iteration (in x xs)) (block))"
465+
"for x in xs, y in ys \n a \n end" => "(for (iteration (in x xs) (in y ys)) (block a))"
466466
# let
467467
"let x=1\n end" => "(let (block (= x 1)) (block))"
468468
"let x=1 ; end" => "(let (block (= x 1)) (block))"
@@ -670,16 +670,16 @@ tests = [
670670
"import A..." => "(import (importpath A ..))"
671671
"import A; B" => "(import (importpath A))"
672672
],
673-
JuliaSyntax.parse_iteration_spec => [
674-
"i = rhs" => "(= i rhs)"
675-
"i in rhs" => "(= i rhs)"
676-
"i ∈ rhs" => "(= i rhs)"
677-
"i = 1:10" => "(= i (call-i 1 : 10))"
678-
"(i,j) in iter" => "(= (tuple-p i j) iter)"
679-
"outer = rhs" => "(= outer rhs)"
680-
"outer <| x = rhs" => "(= (call-i outer <| x) rhs)"
681-
"outer i = rhs" => "(= (outer i) rhs)"
682-
"outer (x,y) = rhs" => "(= (outer (tuple-p x y)) rhs)"
673+
JuliaSyntax.parse_iteration_specs => [
674+
"i = rhs" => "(iteration (in i rhs))"
675+
"i in rhs" => "(iteration (in i rhs))"
676+
"i ∈ rhs" => "(iteration (in i rhs))"
677+
"i = 1:10" => "(iteration (in i (call-i 1 : 10)))"
678+
"(i,j) in iter" => "(iteration (in (tuple-p i j) iter))"
679+
"outer = rhs" => "(iteration (in outer rhs))"
680+
"outer <| x = rhs" => "(iteration (in (call-i outer <| x) rhs))"
681+
"outer i = rhs" => "(iteration (in (outer i) rhs))"
682+
"outer (x,y) = rhs" => "(iteration (in (outer (tuple-p x y)) rhs))"
683683
],
684684
JuliaSyntax.parse_paren => [
685685
# Tuple syntax with commas
@@ -707,8 +707,8 @@ tests = [
707707
"(x)" => "(parens x)"
708708
"(a...)" => "(parens (... a))"
709709
# Generators
710-
"(x for a in as)" => "(parens (generator x (= a as)))"
711-
"(x \n\n for a in as)" => "(parens (generator x (= a as)))"
710+
"(x for a in as)" => "(parens (generator x (iteration (in a as))))"
711+
"(x \n\n for a in as)" => "(parens (generator x (iteration (in a as))))"
712712
# Range parsing in parens
713713
"(1:\n2)" => "(parens (call-i 1 : 2))"
714714
"(1:2)" => "(parens (call-i 1 : 2))"
@@ -776,19 +776,19 @@ tests = [
776776
"[x \n, ]" => "(vect x)"
777777
"[x" => "(vect x (error-t))"
778778
"[x \n\n ]" => "(vect x)"
779-
"[x for a in as]" => "(comprehension (generator x (= a as)))"
780-
"[x \n\n for a in as]" => "(comprehension (generator x (= a as)))"
779+
"[x for a in as]" => "(comprehension (generator x (iteration (in a as))))"
780+
"[x \n\n for a in as]" => "(comprehension (generator x (iteration (in a as))))"
781781
# parse_generator
782-
"(x for a in as for b in bs)" => "(parens (generator x (= a as) (= b bs)))"
783-
"(x for a in as, b in bs)" => "(parens (generator x (cartesian_iterator (= a as) (= b bs))))"
784-
"(x for a in as, b in bs if z)" => "(parens (generator x (filter (cartesian_iterator (= a as) (= b bs)) z)))"
785-
"(x for a in as, b in bs for c in cs, d in ds)" => "(parens (generator x (cartesian_iterator (= a as) (= b bs)) (cartesian_iterator (= c cs) (= d ds))))"
786-
"(x for a in as for b in bs if z)" => "(parens (generator x (= a as) (filter (= b bs) z)))"
787-
"(x for a in as if z for b in bs)" => "(parens (generator x (filter (= a as) z) (= b bs)))"
788-
"[x for a = as for b = bs if cond1 for c = cs if cond2]" => "(comprehension (generator x (= a as) (filter (= b bs) cond1) (filter (= c cs) cond2)))"
789-
"[x for a = as if begin cond2 end]" => "(comprehension (generator x (filter (= a as) (block cond2))))"
790-
"[(x)for x in xs]" => "(comprehension (generator (parens x) (error-t) (= x xs)))"
791-
"(x for a in as if z)" => "(parens (generator x (filter (= a as) z)))"
782+
"(x for a in as for b in bs)" => "(parens (generator x (iteration (in a as)) (iteration (in b bs))))"
783+
"(x for a in as, b in bs)" => "(parens (generator x (iteration (in a as) (in b bs))))"
784+
"(x for a in as, b in bs if z)" => "(parens (generator x (filter (iteration (in a as) (in b bs)) z)))"
785+
"(x for a in as, b in bs for c in cs, d in ds)" => "(parens (generator x (iteration (in a as) (in b bs)) (iteration (in c cs) (in d ds))))"
786+
"(x for a in as for b in bs if z)" => "(parens (generator x (iteration (in a as)) (filter (iteration (in b bs)) z)))"
787+
"(x for a in as if z for b in bs)" => "(parens (generator x (filter (iteration (in a as)) z) (iteration (in b bs))))"
788+
"[x for a = as for b = bs if cond1 for c = cs if cond2]" => "(comprehension (generator x (iteration (in a as)) (filter (iteration (in b bs)) cond1) (filter (iteration (in c cs)) cond2)))"
789+
"[x for a = as if begin cond2 end]" => "(comprehension (generator x (filter (iteration (in a as)) (block cond2))))"
790+
"[(x)for x in xs]" => "(comprehension (generator (parens x) (error-t) (iteration (in x xs))))"
791+
"(x for a in as if z)" => "(parens (generator x (filter (iteration (in a as)) z)))"
792792
# parse_vect
793793
"[x, y]" => "(vect x y)"
794794
"[x, y]" => "(vect x y)"
@@ -876,8 +876,8 @@ tests = [
876876
"\"hi\$(\"ho\")\"" => "(string \"hi\" (parens (string \"ho\")))"
877877
"\"\$(x,y)\"" => "(string (parens (error x y)))"
878878
"\"\$(x;y)\"" => "(string (parens (error x y)))"
879-
"\"\$(x for y in z)\"" => "(string (parens (error (generator x (= y z)))))"
880-
"\"\$((x for y in z))\"" => "(string (parens (parens (generator x (= y z)))))"
879+
"\"\$(x for y in z)\"" => "(string (parens (error (generator x (iteration (in y z))))))"
880+
"\"\$((x for y in z))\"" => "(string (parens (parens (generator x (iteration (in y z))))))"
881881
"\"\$(xs...)\"" => "(string (parens (... xs)))"
882882
"\"a \$foo b\"" => "(string \"a \" foo \" b\")"
883883
"\"\$var\"" => "(string var)"
@@ -996,7 +996,7 @@ parsestmt_test_specs = [
996996
":+'y'" => "(juxtapose (call-post (quote-: +) ') (call-post y '))"
997997
# unary subtype ops and newlines
998998
"a +\n\n<:" => "(call-i a + <:)"
999-
"for\n\n<:" => "(for (= <: (error (error-t))) (block (error)) (error-t))"
999+
"for\n\n<:" => "(for (iteration (in <: (error (error-t)))) (block (error)) (error-t))"
10001000
# Empty character consumes trailing ' delimiter (ideally this could be
10011001
# tested above but we don't require the input stream to be consumed in the
10021002
# unit tests there.

0 commit comments

Comments
 (0)