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
69 changes: 52 additions & 17 deletions src/Pipe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,62 @@ export @pipe

const PLACEHOLDER = :_

function rewrite(ff::Expr,target)
function replace(arg::Any)
arg #Normally do nothing
end
function replace(arg::Symbol)
if arg==PLACEHOLDER
target
else
arg
end
end
function replace(arg::Expr)
rep = copy(arg)
rep.args = map(replace,rep.args)
rep
function replace(arg::Any, target)
arg #Normally do nothing
end

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

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

function rewrite(ff::Expr, target)
rep_args = map(x->replace(x, target), ff.args)
if ff.args != rep_args
#_ subsitution
ff.args=rep_args
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)
end

function rewrite_broadcasted(ff::Expr, target)
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

#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_broadcasted(ff,target)
end

function rewrite_apply(ff, target)
:($ff($target)) #function application
end

function rewrite_apply_broadcasted(ff, target)
temp_var = gensym()
:($temp_var->$ff($temp_var))
end

function rewrite(ff::Symbol, target)
if ff==PLACEHOLDER
target
Expand All @@ -47,6 +69,14 @@ function rewrite(ff::Symbol, target)
end
end

function rewrite_broadcasted(ff::Symbol, target)
if ff==PLACEHOLDER
target
else
rewrite_apply_broadcasted(ff,target)
end
end

function funnel(ee::Any) #Could be a Symbol could be a literal
ee #first (left most) input
end
Expand All @@ -55,6 +85,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_broadcasted(ee.args[3],target)
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 changes how Symbols are stringified so we compute the representation here
@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))