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
AST: Rearrange do to sit inside call/macrocall (#322)
`do` syntax is represented in `Expr` with the `do` outside the call.
This makes some sense syntactically (do appears as "an operator" after
the function call).
However semantically this nesting is awkward because the lambda
represented by the do block is passed to the call. This same problem
occurs for the macro form `@f(x) do \n body end` where the macro
expander needs a special rule to expand nestings of the form
`Expr(:do, Expr(:macrocall ...), ...)`, rearranging the expression which
are passed to this macro call rather than passing the expressions up the
tree.
In this PR, we change the parsing of
@f(x, y) do a, b\n body\n end
f(x, y) do a, b\n body\n end
to tack the `do` onto the end of the call argument list:
(macrocall @f x y (do (tuple a b) body))
(call f x y (do (tuple a b) body))
This achieves the following desirable properties
1. Content of `do` is nested inside the call which improves the match
between AST and semantics
2. Macro can be passed the syntax as-is rather than the macro expander
rearranging syntax before passing it to the macro
3. In the future, a macro can detect when it's being passed do syntax
rather than lambda syntax
4. `do` head is used uniformly for both call and macrocall
5. We preserve the source ordering properties we need for the green tree.
Copy file name to clipboardExpand all lines: docs/src/reference.md
+28-14Lines changed: 28 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,7 +43,7 @@ the source text more closely.
43
43
* The right hand side of `x where {T}` retains the `K"braces"` node around the `T` to distinguish it from `x where T`.
44
44
* Ternary syntax is not immediately lowered to an `if` node: `a ? b : c` parses as `(? a b c)` rather than `Expr(:if, :a, :b, :c)` (#85)
45
45
*`global const` and `const global` are not normalized by the parser. This is done in `Expr` conversion (#130)
46
-
*The AST for `do` is flatter and not lowered to a lambda by the parser: `f(x) do y ; body end` is parsed as `(do (call f x) (tuple y) (block body))` (#98)
46
+
*[`do`syntax](#Do-blocks)is nested as the last child of the call which the `do` lambda will be passed to (#98, #322)
47
47
*`@.` is not lowered to `@__dot__` inside the parser (#146)
48
48
* Docstrings use the `K"doc"` kind, and are not lowered to `Core.@doc` until later (#217)
49
49
* Juxtaposition uses the `K"juxtapose"` kind rather than lowering immediately to `*` (#220)
@@ -78,7 +78,6 @@ class of tokenization errors and lets the parser deal with them.
78
78
* We use flags rather than child nodes to represent the difference between `struct` and `mutable struct`, `module` and `baremodule` (#220)
79
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).
80
80
81
-
82
81
## More detail on tree differences
83
82
84
83
### Generators
@@ -196,23 +195,38 @@ The same goes for command strings which are always wrapped in `K"cmdstring"`
196
195
regardless of whether they have multiple pieces (due to triple-quoted
197
196
dedenting) or otherwise.
198
197
199
-
### No desugaring of the closure in do blocks
198
+
### Do blocks
200
199
201
-
The reference parser represents `do` syntax with a closure for the second
202
-
argument. That is,
200
+
`do` syntax is represented in the `Expr` AST with the `do` outside the call.
201
+
This makes some sense syntactically (do appears as "an operator" after the
202
+
function call).
203
203
204
-
```julia
205
-
f(x) do y
206
-
body
207
-
end
208
-
```
204
+
However semantically this nesting is awkward because the lambda represented by
205
+
the do block is passed to the call. This same problem occurs for the macro form
206
+
`@f(x) do \n body end` where the macro expander needs a special rule to expand
207
+
nestings of the form `Expr(:do, Expr(:macrocall ...), ...)`, rearranging the
208
+
expression which are passed to this macro call rather than passing the
209
+
expressions up the tree.
210
+
211
+
The implied closure is also lowered to a nested `Expr(:->)` expression, though
212
+
it this somewhat premature to do this during parsing.
213
+
214
+
To resolve these problems we parse
215
+
216
+
@f(x, y) do a, b\n body\n end
217
+
f(x, y) do a, b\n body\n end
209
218
210
-
becomes `(do (call f x) (-> (tuple y) (block body)))` in the reference parser.
219
+
by tacking the `do` onto the end of the call argument list:
211
220
212
-
However, the nested closure with `->` head is implied here rather than present
213
-
in the surface syntax, which suggests this is a premature desugaring step.
214
-
Instead we emit the flatter structure `(do (call f x) (tuple y) (block body))`.
221
+
(macrocall @f x y (do (tuple a b) body))
222
+
(call f x y (do (tuple a b) body))
215
223
224
+
This achieves the following desirable properties
225
+
1. Content of `do` is nested inside the call which improves the match between AST and semantics
226
+
2. Macro can be passed the syntax as-is rather than the macro expander rearranging syntax before passing it to the macro
227
+
3. In the future, a macro can detect when it's being passed do syntax rather than lambda syntax
228
+
4.`do` head is used uniformly for both call and macrocall
229
+
5. We preserve the source ordering properties we need for the green tree.
0 commit comments