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

Added support for broadcasted piping .|>. #16

Merged
merged 14 commits into from
Jun 3, 2020
Merged
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ julia:
- 1.0
- 1.2
- 1.3
- 1.4
- nightly
notifications:
email: false
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Pipe"
uuid = "b98c9c47-44ae-5843-9183-064241ee97a0"
version = "1.2.0"
version = "1.3.0"

[compat]
julia = "1"
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Pipe


- Julia 0.7: [![Build Status 0.6](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/1)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia 1.0: [![Build Status 0.6](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/2)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia Nightly: [![Build Status Nightly](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/7)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia 1.0: [![Build Status 1.0](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/1)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia 1.2: [![Build Status 1.2](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/2)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia 1.3: [![Build Status 1.3](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/3)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia 1.4: [![Build Status 1.4](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/4)](https://travis-ci.org/oxinabox/Pipe.jl)
- Julia Nightly: [![Build Status Nightly](https://travis-matrix-badges.herokuapp.com/repos/oxinabox/Pipe.jl/branches/master/5)](https://travis-ci.org/oxinabox/Pipe.jl)

## Usage

Expand Down
72 changes: 47 additions & 25 deletions src/Pipe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,62 @@ export @pipe

const PLACEHOLDER = :_

function rewrite(ff::Expr,target)
function replace(arg::Any)
arg #Normally do nothing
function replace(arg::Any, target)
arg #Normally do nothing
end

function replace(arg::Symbol, target)
if arg==PLACEHOLDER
target
else
arg
end
function replace(arg::Symbol)
if arg==PLACEHOLDER
target
else
arg
end

function replace(arg::Expr, target)
rep = copy(arg)
rep.args = map(x->replace(x, target), rep.args)
rep
end

function rewrite(ff::Expr, target, broadcast=false)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we break this into two functions?
rewrite and rewrite_broadcasted

if broadcast
temp_var = gensym()
rep_args = map(x->replace(x, temp_var), ff.args)
if ff.args != rep_args
#_ subsitution
ff.args = rep_args
return :($temp_var->$ff)
end
else
rep_args = map(x->replace(x, target), ff.args)
if ff.args != rep_args
#_ subsitution
ff.args = rep_args
return ff
end
end
function replace(arg::Expr)
rep = copy(arg)
rep.args = map(replace,rep.args)
rep
end

rep_args = map(replace,ff.args)
if ff.args != rep_args
#_ subsitution
ff.args=rep_args
return ff
end
#No subsitution was done (no _ found)
#Apply to a function that is being returned by ff,
#(ff could be a function call or something more complex)
rewrite_apply(ff,target)
rewrite_apply(ff,target,broadcast)
end


function rewrite_apply(ff, target)
:($ff($target)) #function application
function rewrite_apply(ff, target, broadcast=false)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we break this into two functions?
rewrite_apply and rewrite_apply_broadcasted

if broadcast
temp_var = gensym()
:($temp_var->$ff($temp_var))
else
:($ff($target)) #function application
end
end

function rewrite(ff::Symbol, target)
function rewrite(ff::Symbol, target, broadcast=false)
if ff==PLACEHOLDER
target
else
rewrite_apply(ff,target)
rewrite_apply(ff,target,broadcast)
end
end

Expand All @@ -55,6 +72,11 @@ function funnel(ee::Expr)
if (ee.args[1]==:|>)
target = funnel(ee.args[2]) #Recurse
rewrite(ee.args[3],target)
elseif (ee.args[1]==:.|>)
target = funnel(ee.args[2]) #Recurse
rewritten = rewrite(ee.args[3],target,true)
ee.args[3] = rewritten
ee
else
#Not in a piping situtation
ee #make no change
Expand Down
22 changes: 22 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ using Test

_macroexpand(q) = macroexpand(Main, q)

rml! = Base.remove_linenums!
# performs linenum removal and temp variable replacing to avoid different names of temp variables in different julia versions
stringify_expr(e::Expr) = replace(string(rml!(e)), r"##\d{3}"=>"##000")
pipe_equals(e1::Expr, e2::Expr) = stringify_expr(_macroexpand(e1)) == stringify_expr(e2)

#No change to nonpipes functionality
@test _macroexpand( :(@pipe a) ) == :a #doesn't change single inputs
@test _macroexpand( :(@pipe b(a)) ) == :(b(a)) #doesn't change inputs that a function applications
Expand Down Expand Up @@ -47,3 +52,20 @@ end
@test _macroexpand( :(@pipe a|>b|>c(_) ) ) == :(c(b(a)))
@test _macroexpand( :(@pipe a|>b(x,_)|>c|>d(_,y) ) ) == :(d(c(b(x,a)),y))
@test _macroexpand( :(@pipe a|>b(xb,_)|>c|>d(_,xd)|>e(xe) |>f(xf,_,yf)|>_[i] ) ) == :(f(xf,(e(xe))(d(c(b(xb,a)),xd)),yf)[i]) #Very Complex

# broadcasting
vars = 1:10 .|> y->gensym() # Julia < 1.3 does uses Symbol for variables, but Julia >= 1.3 uses var, so I use output of gensym
racinmat marked this conversation as resolved.
Show resolved Hide resolved
@test pipe_equals(:(@pipe 1:10 .|> _*2 ), :(1:10 .|> $(vars[1])->$(vars[1]) * 2))
@test pipe_equals(:(@pipe 1:10 .|> fn ), :(1:10 .|> $(vars[2])->fn($(vars[2]))))
@test pipe_equals(:(@pipe a .|> fn .|> _*2 ), :(a .|> ($(vars[3])->fn($(vars[3]))) .|> ($(vars[4])->$(vars[4])*2)))
@test pipe_equals(:(@pipe a .|> fn |> _*2 ), :((a .|> $(vars[5])->fn($(vars[5]))) * 2))
@test pipe_equals(:(@pipe [1,2,2] |> atan.([10,20,30], _) ), :(atan.([10,20,30], [1,2,2])))
@test pipe_equals(:(@pipe [1,2,2] .|> atan.([10,20,30], _) ), :([1,2,2] .|> $(vars[6])->atan.([10,20,30], $(vars[6]))))
@test pipe_equals(:(@pipe fn |> _.(1:2) ), :(fn.(1:2)))
@test pipe_equals(:(@pipe fn .|> _.(1:2) ), :(fn .|> $(vars[7])->$(vars[7]).(1:2)))

@test pipe_equals(:(@pipe [true,false] .|> ! ), :([true, false] .|> $(vars[8])->!$(vars[8])))
@test pipe_equals(:(@pipe [1, 2] |> .+(_, x) ), :([1, 2] .+ x))
@test pipe_equals(:(@pipe [1, 2] |> _ .+ x ), :([1, 2] .+ x))
@test pipe_equals(:(@pipe [1, 2] .|> .+(_, x) ), :([1, 2] .|> $(vars[9])->$(vars[9]).+x))
@test pipe_equals(:(@pipe [1, 2] .|> _ .+ x ), :([1, 2] .|> $(vars[10])->$(vars[10]).+x))