[
  {
    "path": ".github/workflows/TagBot.yml",
    "content": "name: TagBot\non:\n  schedule:\n    - cron: 0 * * * *\njobs:\n  TagBot:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: JuliaRegistries/TagBot@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.jl.cov\n*.jl.mem\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: julia\nos:\n  - linux\n  - osx\njulia:\n  - 1.0\n  - 1.2\n  - 1.3\n  - 1.4\n  - nightly\nnotifications:\n  email: false\n# uncomment the following lines to override the default test script\n#script:\n#  - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi\n#  - julia --check-bounds=yes -e 'Pkg.clone(pwd()); Pkg.build(\"Pipe\"); Pkg.test(\"Pipe\"; coverage=true)'\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The Pipe.jl package is licensed under the MIT \"Expat\" License:\n\n> Copyright (c) 2015: Lyndon White.\n>\n> Permission is hereby granted, free of charge, to any person obtaining\n> a copy of this software and associated documentation files (the\n> \"Software\"), to deal in the Software without restriction, including\n> without limitation the rights to use, copy, modify, merge, publish,\n> distribute, sublicense, and/or sell copies of the Software, and to\n> permit persons to whom the Software is furnished to do so, subject to\n> the following conditions:\n>\n> The above copyright notice and this permission notice shall be\n> included in all copies or substantial portions of the Software.\n>\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Project.toml",
    "content": "name = \"Pipe\"\nuuid = \"b98c9c47-44ae-5843-9183-064241ee97a0\"\nversion = \"1.3.0\"\n\n[compat]\njulia = \"1\"\n\n[extras]\nTest = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n\n[targets]\ntest = [\"Test\"]\n"
  },
  {
    "path": "README.md",
    "content": "# Pipe\n\n - 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)\n - 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)\n - 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)\n - 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)\n - 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)\n\n## Usage\nLoad the package via\n```julia\nusing Pipe: @pipe\n```\n(Just doing `using Pipe` will give an error about a name conflict with the `Base.Pipe` type.)\n\nPlace `@pipe` at the start of the line for which you want \"advanced piping functionality\" to work.\n\nThis works the same as Julia piping,\nexcept if you place a underscore in the right-hand expression, \nit will be replaced with the result of the left-hand expression.\n\nSo:\n```julia\n@pipe a |> b(x,_)       # b(x,a)    NOT: (b(x,_))(a) \n```\n\nFuther the `_` can be unpacked, called, deindexed, etc.\n```julia\n@pipe a |> b(_...)      # b(a...)\n@pipe a |> b(_(1, 2))   # b(a(1,2))\n@pipe a |> b(_[3])      # b(a[3])\n```\n\nThis last can be used for interacting with multiple returned values. In general, however, this is frowned upon.\nGenerally, a pipeline is good for expressing a logical flow data through Single Input Single Output functions. \nWhen you deindex multiple times, that is case of working with Multiple Input Multiple Output functions.\n\nIn that case it is likely more clear to create named variables, and call the functions normally in sequence.\nThere is also a performace cost for doing multiple deindexes (see below).\n\nFor example:\n\n```julia\nfunction get_angle(rise, run)\n    atan(rise / run)\nend\n\n@pipe (2,4) |> get_angle(_[1],_[2]) # 0.4636476090008061\nget_angle(2,4)                      # 0.4636476090008061 (Note: the ordinary way is much clearer)\n```\n\nHowever, for each `_` in the right hand side of the `|>`, the left hand side will be called.\nThis can incur a performance cost.\n\n```julia\nfunction ratio(value, lr, rr)\n    println(\"slitting on ratio $lr:$rr\")\n    value * lr / (lr + rr), value * rr / (lr + rr)\nend\n\nfunction percent(left, right)\n    left / right * 100\nend\n\n@pipe 10 |> ratio(_,4,1) |> percent(_[1],_[2]) # 400.0, outputs splitting on ratio 4:1 Twice\n@pipe 10 |> ratio(_,4,1) |> percent(_...)      # 400.0, outputs splitting on ratio 4:1 Once\n```\n\n---------------------\n\n## See Also:\n\n - [List of similar/related works](https://github.com/JuliaLang/julia/issues/5571#issuecomment-205754539)\n"
  },
  {
    "path": "src/Pipe.jl",
    "content": "module Pipe\n#Reflow piped things replacing the _ in the next section\n\nexport @pipe\n\nconst PLACEHOLDER = :_\n\nfunction replace(arg::Any, target)\n    arg #Normally do nothing\nend\n\nfunction replace(arg::Symbol, target)\n    if arg==PLACEHOLDER\n        target\n    else\n        arg\n    end\nend\n\nfunction replace(arg::Expr, target)\n    rep = copy(arg)\n    rep.args = map(x->replace(x, target), rep.args)\n    rep\nend\n\nfunction rewrite(ff::Expr, target)\n    rep_args = map(x->replace(x, target), ff.args)\n    if ff.args != rep_args\n        #_ subsitution\n        ff.args = rep_args\n        return ff\n    end\n\n    #No subsitution was done (no _ found)\n    #Apply to a function that is being returned by ff,\n    #(ff could be a function call or something more complex)\n    rewrite_apply(ff,target)\nend\n\nfunction rewrite_broadcasted(ff::Expr, target)\n    temp_var = gensym()\n    rep_args = map(x->replace(x, temp_var), ff.args)\n    if ff.args != rep_args\n        #_ subsitution\n        ff.args = rep_args\n        return :($temp_var->$ff)\n    end\n\n    #No subsitution was done (no _ found)\n    #Apply to a function that is being returned by ff,\n    #(ff could be a function call or something more complex)\n    rewrite_apply_broadcasted(ff,target)\nend\n\nfunction rewrite_apply(ff, target)\n    :($ff($target)) #function application\nend\n\nfunction rewrite_apply_broadcasted(ff, target)\n    temp_var = gensym()\n    :($temp_var->$ff($temp_var))\nend\n\nfunction rewrite(ff::Symbol, target)\n    if ff==PLACEHOLDER\n        target\n    else\n        rewrite_apply(ff,target)\n    end\nend\n\nfunction rewrite_broadcasted(ff::Symbol, target)\n    if ff==PLACEHOLDER\n        target\n    else\n        rewrite_apply_broadcasted(ff,target)\n    end\nend\n\nfunction funnel(ee::Any) #Could be a Symbol could be a literal\n    ee #first (left most) input\nend\n\nfunction funnel(ee::Expr)\n    if (ee.args[1]==:|>)\n        target = funnel(ee.args[2]) #Recurse\n        rewrite(ee.args[3],target)\n    elseif (ee.args[1]==:.|>)\n        target = funnel(ee.args[2]) #Recurse\n        rewritten = rewrite_broadcasted(ee.args[3],target)\n        ee.args[3] = rewritten\n        ee\n    else\n        #Not in a piping situtation\n        ee #make no change\n    end\nend\n\nmacro pipe(ee)\n    esc(funnel(ee))\nend\n\nend\n"
  },
  {
    "path": "test/runtests.jl",
    "content": "using Pipe\nusing Test\n\n_macroexpand(q) = macroexpand(Main, q)\n\nrml! = Base.remove_linenums!\n# performs linenum removal and temp variable replacing to avoid different names of temp variables in different julia versions\nstringify_expr(e::Expr) = replace(string(rml!(e)), r\"##\\d{3}\"=>\"##000\")\npipe_equals(e1::Expr, e2::Expr) = stringify_expr(_macroexpand(e1)) == stringify_expr(e2)\n\n#No change to nonpipes functionality\n@test _macroexpand( :(@pipe a) ) == :a #doesn't change single inputs\n@test _macroexpand( :(@pipe b(a)) ) == :(b(a)) #doesn't change inputs that a function applications\n\n#Compatable with Julia 1.3 piping functionality\n@test _macroexpand( :(@pipe a|>b) ) == :(b(a)) #basic\n@test _macroexpand( :(@pipe a|>b|>c) ) == :(c(b(a)))  #Keeps chaining 3\n@test _macroexpand( :(@pipe a|>b|>c|>d) ) == :(d(c(b(a)))) #Keeps chaining 4\n\n@test _macroexpand( :(@pipe a|>b(x)) ) == :((b(x))(a))  #applying to function calls returning functions\n@test _macroexpand( :(@pipe a(x)|>b ) ) == :(b(a(x)))   #feeding functioncall results on wards\n\n@test _macroexpand(:(@pipe 1|>a)) ==:(a(1)) #Works with literals (int)\n@test _macroexpand(:(@pipe \"foo\"|>a)) == :(a(\"foo\")) #Works with literal (string)\n@test _macroexpand( :(@pipe a|>bb[2])) == :((bb[2])(a)) #Should work with RHS that is a array reference\n\n\n\n#Marked locations\n@test _macroexpand( :(@pipe a |> _)) == :(a) #Identity works\n@test _macroexpand( :(@pipe a |> _[b])) == :(a[b]) #Indexing works\n\n@test _macroexpand( :(@pipe a|>b(_) ) ) == :(b(a)) #Marked location only\n@test _macroexpand( :(@pipe a|>b(x,_) ) ) == :(b(x,a)) # marked 2nd (and last)\n@test _macroexpand( :(@pipe a|>b(_,x) ) ) == :(b(a,x)) # marked first\n@test _macroexpand( :(@pipe a|>b(_,_) ) ) == :(b(a,a)) # marked double (Not certain if this is a good idea)\n@test _macroexpand( :(@pipe a|>bb[2](x,_))) == :((bb[2])(x,a)) #Should work with RHS that is a array reference\n\n#Macros and blocks\nmacro testmacro(arg, n)\n    esc(:($arg + $n))\nend\n@test _macroexpand( :(@pipe a |> @testmacro _ 3 ) ) == :(a + 3) # Can pipe into macros\n@test _macroexpand( :(@pipe a |> begin b = _; c + b + _ end )) == :(\n                                 begin b = a; c + b + a end)\n\n#marked Unpacking\n@test _macroexpand( :(@pipe a|>b(_...) ) ) == :(b(a...)) # Unpacking\n@test _macroexpand( :(@pipe a|>bb[2](_...))) == :((bb[2])(a...)) #Should work with RHS of arry ref and do unpacking\n\n#Mixing modes\n@test _macroexpand( :(@pipe a|>b|>c(_) ) ) == :(c(b(a)))\n@test _macroexpand( :(@pipe a|>b(x,_)|>c|>d(_,y) ) ) == :(d(c(b(x,a)),y))\n@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\n\n# broadcasting\nvars = 1:10 .|> y->gensym() # Julia < 1.3 changes how Symbols are stringified so we compute the representation here\n@test pipe_equals(:(@pipe 1:10 .|> _*2 ), :(1:10 .|> $(vars[1])->$(vars[1]) * 2))\n@test pipe_equals(:(@pipe 1:10 .|> fn ), :(1:10 .|> $(vars[2])->fn($(vars[2]))))\n@test pipe_equals(:(@pipe a .|> fn .|> _*2 ), :(a .|> ($(vars[3])->fn($(vars[3]))) .|> ($(vars[4])->$(vars[4])*2)))\n@test pipe_equals(:(@pipe a .|> fn |> _*2 ), :((a .|> $(vars[5])->fn($(vars[5]))) * 2))\n@test pipe_equals(:(@pipe [1,2,2] |> atan.([10,20,30], _) ), :(atan.([10,20,30], [1,2,2])))\n@test pipe_equals(:(@pipe [1,2,2] .|> atan.([10,20,30], _) ), :([1,2,2] .|> $(vars[6])->atan.([10,20,30], $(vars[6]))))\n@test pipe_equals(:(@pipe fn |> _.(1:2) ), :(fn.(1:2)))\n@test pipe_equals(:(@pipe fn .|> _.(1:2) ), :(fn .|> $(vars[7])->$(vars[7]).(1:2)))\n\n@test pipe_equals(:(@pipe [true,false] .|> ! ), :([true, false] .|> $(vars[8])->!$(vars[8])))\n@test pipe_equals(:(@pipe [1, 2] |> .+(_, x) ), :([1, 2] .+ x))\n@test pipe_equals(:(@pipe [1, 2] |>  _ .+ x ), :([1, 2] .+ x))\n@test pipe_equals(:(@pipe [1, 2] .|> .+(_, x) ), :([1, 2] .|> $(vars[9])->$(vars[9]).+x))\n@test pipe_equals(:(@pipe [1, 2] .|>  _ .+ x ), :([1, 2] .|> $(vars[10])->$(vars[10]).+x))\n"
  }
]