You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Copy file name to clipboardExpand all lines: docs/src/reference.md
+10-10Lines changed: 10 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -76,7 +76,7 @@ class of tokenization errors and lets the parser deal with them.
76
76
* 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)
77
77
* 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)
78
78
* 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.
80
80
81
81
## More detail on tree differences
82
82
@@ -90,8 +90,10 @@ mean
90
90
91
91
```
92
92
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
95
97
```
96
98
97
99
so the `xy` prefix is in the *body* of the innermost for loop. Following this,
@@ -112,27 +114,25 @@ source order.
112
114
113
115
However, our green tree is strictly source-ordered, so we must deviate from the
114
116
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
117
119
distinguish flattened iterators. The nested flattens and generators of `Expr`
118
120
forms are reconstructed later. In this form the tree structure resembles the
119
121
source much more closely. For example, `(xy for x in xs for y in ys)` is parsed as
120
122
121
123
```
122
124
(generator
123
125
xy
124
-
(= x xs)
125
-
(= y ys))
126
+
(iteration x xs)
127
+
(iteration y ys))
126
128
```
127
129
128
130
And the cartesian iteration `(xy for x in xs, y in ys)` is parsed as
Copy file name to clipboardExpand all lines: test/parser.jl
+31-31Lines changed: 31 additions & 31 deletions
Original file line number
Diff line number
Diff line change
@@ -300,7 +300,7 @@ tests = [
300
300
"x where \n {T}"=>"(where x (braces T))"
301
301
"x where {T,S}"=>"(where x (braces T S))"
302
302
"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)))))"
304
304
"x where T"=>"(where x T)"
305
305
"x where \n T"=>"(where x T)"
306
306
"x where T<:S"=>"(where x (<: T S))"
@@ -389,7 +389,7 @@ tests = [
389
389
"T[x y]"=>"(typed_hcat T x y)"
390
390
"T[x ; y]"=>"(typed_vcat T x y)"
391
391
"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))))"
393
393
((v=v"1.8",), "T[a ; b ;; c ; d]") =>"(typed_ncat-2 T (nrow-1 a b) (nrow-1 c d))"
394
394
395
395
# Dotted forms
@@ -461,8 +461,8 @@ tests = [
461
461
"while cond body end"=>"(while cond (block body))"
462
462
"while x < y \n a \n b \n end"=>"(while (call-i x < y) (block a b))"
463
463
# 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))"
466
466
# let
467
467
"let x=1\n end"=>"(let (block (= x 1)) (block))"
468
468
"let x=1 ; end"=>"(let (block (= x 1)) (block))"
@@ -670,16 +670,16 @@ tests = [
670
670
"import A..."=>"(import (importpath A ..))"
671
671
"import A; B"=>"(import (importpath A))"
672
672
],
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))"
683
683
],
684
684
JuliaSyntax.parse_paren => [
685
685
# Tuple syntax with commas
@@ -707,8 +707,8 @@ tests = [
707
707
"(x)"=>"(parens x)"
708
708
"(a...)"=>"(parens (... a))"
709
709
# 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))))"
712
712
# Range parsing in parens
713
713
"(1:\n2)"=>"(parens (call-i 1 : 2))"
714
714
"(1:2)"=>"(parens (call-i 1 : 2))"
@@ -776,19 +776,19 @@ tests = [
776
776
"[x \n, ]"=>"(vect x)"
777
777
"[x"=>"(vect x (error-t))"
778
778
"[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))))"
781
781
# 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)))"
0 commit comments