Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recurse _ to bypass square brackets, and usually-infix operators #10

Merged
merged 9 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Underscores.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,27 @@ function lower_underscores(ex)
if ex isa Expr
if isquoted(ex)
return ex

elseif ex.head == :call && length(ex.args) > 1 &&
ex.args[1] in _pipeline_ops
# Special case for pipelining and composition operators
return Expr(ex.head, ex.args[1],
map(lower_underscores, ex.args[2:end])...)

elseif ex.head == :. && length(ex.args) == 2 &&
ex.args[2] isa Expr && ex.args[2].head == :tuple
# Broadcast calls treated as normal calls for underscore lowering
return replace__(Expr(ex.head, replace_(ex.args[1]),
Expr(:tuple, map(replace_, ex.args[2].args)...)))

elseif ex.head == :ref && ex.args[1] isa Expr
# Indexing is not counted as outermost function
return replace__(Expr(ex.head,
Expr(ex.args[1].head, map(replace_, ex.args[1].args)...), ex.args[2]))

elseif ex.head == :do
error("@_ expansion for `do` syntax is reserved")

mcabbott marked this conversation as resolved.
Show resolved Hide resolved
else
# For other syntax, replace _ in args individually and __ over the
# entire expression.
Expand All @@ -109,6 +118,8 @@ The detailed rules are:
expands the closure scope to the whole expression.
4. Piping and composition chains with `|>,<|,∘` are treated as a special case
where the replacement recurses into sub-expressions.
5. Indexing `[...]` is a similar special case as regards `_` (and `_1,_2,...`),
but does not affect `__`.
mcabbott marked this conversation as resolved.
Show resolved Hide resolved

These rules imply the following equivalences

Expand All @@ -120,6 +131,7 @@ These rules imply the following equivalences
| `@_ func(a,__,b)` | (3) | `x->func(a,x,b)` |
| `@_ func(a,__2,b)` | (3) | `(x,y)->func(a,y,b)` |
| `@_ data \\|> map(_.f,__)` | (1,3,4) | `data \\|> (d->map(x->x.f,d))` |
| `@_ dt \\|> map(_[2],__)[3]`| (1,3,4,5) | `dt \\|> (d->map(x->x[2],d)[3])` |
mcabbott marked this conversation as resolved.
Show resolved Hide resolved

# Extended help

Expand Down
8 changes: 8 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ using Test
@test data[1:1] == @_ filter(startswith(_.x, "a"), data)
@test data[2:3] == @_ filter(_.y >= 2, data)

# Use with indexing
@test data[1] == @_ filter(startswith(_.x, "a"), data)[end]
@test data[2:3] == @_ filter(_.y >= 2, data)[1:2]

# Multiple args
@test [0,0] == @_ map(_-_, [1,2])

Expand All @@ -31,6 +35,10 @@ using Test

@test [0,0,0] == @_ data |> map(_1.y + _2, __, [-1,-2,-3])

@test 3 == @_ [[1], [1,2], [1,2,3]] |>
map(_[_[end]], __) |>
__[end]
mcabbott marked this conversation as resolved.
Show resolved Hide resolved

# Use with piping and lazy versions of map and filter
Filter(f) = x->filter(f,x)
Map(f) = x->map(f,x)
Expand Down