Skip to content

Commit 74f7137

Browse files
authored
Parse do blocks without desugaring the closure (#98)
The reference parser represents `do` syntax with a closure for the second argument. However, the nested closure with `->` head is implied rather than present in the surface syntax, which suggests this is a premature desugaring step. Instead we emit a flatter three-argument form for `do`.
1 parent 4818362 commit 74f7137

File tree

4 files changed

+37
-21
lines changed

4 files changed

+37
-21
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,23 @@ The same goes for command strings which are always wrapped in `K"cmdstring"`
462462
regardless of whether they have multiple pieces (due to triple-quoted
463463
dedenting) or otherwise.
464464

465+
### No desugaring of the closure in do blocks
466+
467+
The reference parser represents `do` syntax with a closure for the second
468+
argument. That is,
469+
470+
```julia
471+
f(x) do y
472+
body
473+
end
474+
```
475+
476+
becomes `(do (call f x) (-> (tuple y) (block body)))` in the reference parser.
477+
478+
However, the nested closure with `->` head is implied here rather than present
479+
in the surface syntax, which suggests this is a premature desugaring step.
480+
Instead we emit the flatter structure `(do (call f x) (tuple y) (block body))`.
481+
465482
## More about syntax kinds
466483

467484
We generally track the type of syntax nodes with a syntax "kind", stored

src/expr.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,16 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
218218
end
219219
elseif headsym == :module
220220
pushfirst!(args[3].args, loc)
221-
end
222-
if headsym == :inert || (headsym == :quote && length(args) == 1 &&
221+
elseif headsym == :inert || (headsym == :quote && length(args) == 1 &&
223222
!(a1 = only(args); a1 isa Expr || a1 isa QuoteNode ||
224223
a1 isa Bool # <- compat hack, Julia 1.4+
225224
))
226225
return QuoteNode(only(args))
227-
else
228-
return Expr(headsym, args...)
226+
elseif headsym == :do
227+
@check length(args) == 3
228+
return Expr(:do, args[1], Expr(:->, args[2], args[3]))
229229
end
230+
return Expr(headsym, args...)
230231
end
231232

232233
Base.Expr(node::SyntaxNode) = _to_expr(node)

src/parser.jl

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,7 @@ end
423423
# a \n b ==> (block a b)
424424
#
425425
# flisp: parse-block
426-
function parse_block(ps::ParseState, down=parse_eq, mark=position(ps),
427-
consume_end=false)
426+
function parse_block(ps::ParseState, down=parse_eq, mark=position(ps))
428427
parse_block_inner(ps::ParseState, down)
429428
emit(ps, mark, K"block")
430429
end
@@ -1452,10 +1451,8 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
14521451
parse_call_arglist(ps, K")", is_macrocall)
14531452
emit(ps, mark, is_macrocall ? K"macrocall" : K"call")
14541453
if peek(ps) == K"do"
1455-
# f(x) do y body end ==> (do (call :f :x) (-> (tuple :y) (block :body)))
1456-
bump(ps, TRIVIA_FLAG)
1457-
parse_do(ps)
1458-
emit(ps, mark, K"do")
1454+
# f(x) do y body end ==> (do (call :f :x) (tuple :y) (block :body))
1455+
parse_do(ps, mark)
14591456
end
14601457
if is_macrocall
14611458
break
@@ -2179,23 +2176,24 @@ function parse_catch(ps::ParseState)
21792176
end
21802177

21812178
# flisp: parse-do
2182-
function parse_do(ps::ParseState)
2179+
function parse_do(ps::ParseState, mark)
2180+
bump(ps, TRIVIA_FLAG) # do
21832181
ps = normal_context(ps)
2184-
mark = position(ps)
2182+
m = position(ps)
21852183
if peek(ps) in KSet"NewlineWs ;"
2186-
# f() do\nend ==> (do (call f) (-> (tuple) (block)))
2187-
# f() do ; body end ==> (do (call f) (-> (tuple) (block body)))
2184+
# f() do\nend ==> (do (call f) (tuple) (block))
2185+
# f() do ; body end ==> (do (call f) (tuple) (block body))
21882186
# this trivia needs to go into the tuple due to the way position()
21892187
# works.
21902188
bump(ps, TRIVIA_FLAG)
21912189
else
2192-
# f() do x, y\n body end ==> (do (call f) (-> (tuple x y) (block body)))
2190+
# f() do x, y\n body end ==> (do (call f) (tuple x y) (block body))
21932191
parse_comma_separated(ps, parse_range)
21942192
end
2195-
emit(ps, mark, K"tuple")
2193+
emit(ps, m, K"tuple")
21962194
parse_block(ps)
21972195
bump_closing_token(ps, K"end")
2198-
emit(ps, mark, K"->")
2196+
emit(ps, mark, K"do")
21992197
end
22002198

22012199
function macro_name_kind(k)

test/parser.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,10 @@ tests = [
242242
"f(a).g(b)" => "(call (. (call f a) (quote g)) b)"
243243
"\$A.@x" => "(macrocall (. (\$ A) (quote @x)))"
244244
# do
245-
"f() do\nend" => "(do (call f) (-> (tuple) (block)))"
246-
"f() do ; body end" => "(do (call f) (-> (tuple) (block body)))"
247-
"f() do x, y\n body end" => "(do (call f) (-> (tuple x y) (block body)))"
248-
"f(x) do y body end" => "(do (call f x) (-> (tuple y) (block body)))"
245+
"f() do\nend" => "(do (call f) (tuple) (block))"
246+
"f() do ; body end" => "(do (call f) (tuple) (block body))"
247+
"f() do x, y\n body end" => "(do (call f) (tuple x y) (block body))"
248+
"f(x) do y body end" => "(do (call f x) (tuple y) (block body))"
249249
# Keyword arguments depend on call vs macrocall
250250
"foo(a=1)" => "(call foo (kw a 1))"
251251
"@foo(a=1)" => "(macrocall @foo (= a 1))"

0 commit comments

Comments
 (0)