From 5018c6801ad734113ae1be87cfd60b7e7da91eb9 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 21 Dec 2019 16:45:33 -0800 Subject: [PATCH 1/4] Use $ to eagerly evaluate expressions in @~ --- src/lazymacro.jl | 4 ++++ test/macrotests.jl | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/lazymacro.jl b/src/lazymacro.jl index 521c0d66..4fa2a94e 100644 --- a/src/lazymacro.jl +++ b/src/lazymacro.jl @@ -64,6 +64,10 @@ app_expr_impl(x) = x function app_expr_impl(ex::Expr) # walk down chain of calls and lazy-ify them if is_call(ex) + if isexpr(ex.args[1], :$) + # eagerly evaluate the call + return Expr(:call, ex.args[1].args[1], ex.args[2:end]...) + end return :($applied($(app_expr_impl.(ex.args)...))) else return lazy_expr(ex) diff --git a/test/macrotests.jl b/test/macrotests.jl index 3c971405..b462087e 100644 --- a/test/macrotests.jl +++ b/test/macrotests.jl @@ -97,4 +97,23 @@ end @test bc.args[1].args isa Tuple{Applied, Int} end +@testset "@~ and \$" begin + A = ones(1, 1) + x = [1] + + # Use `$` to evaluate a sub-expression eagerly + bc = @~ A .+ $Ref(x) + @test bc isa Broadcasted + @test bc.args[1] === A + @test bc.args[2] isa Ref # not Applied + @test bc.args[2][] === x + + # Use `$$` when combined with `@.` + bc = @~ @. A + $$Ref(x) + @test bc isa Broadcasted + @test bc.args[1] === A + @test bc.args[2] isa Ref # not Applied + @test bc.args[2][] === x +end + end # module From d316fc5e992634978241fa1a0cd2fd4d9c0d79bb Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 21 Dec 2019 17:19:41 -0800 Subject: [PATCH 2/4] Fix is_dotcall --- src/lazymacro.jl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lazymacro.jl b/src/lazymacro.jl index 4fa2a94e..3a2b74bb 100644 --- a/src/lazymacro.jl +++ b/src/lazymacro.jl @@ -15,10 +15,17 @@ Broadcast.materialize(x::LazyCast) = x.value is_call(ex) = isexpr(ex, :call) && !is_dotcall(ex) -is_dotcall(ex) = - (isexpr(ex, :.) && isexpr(ex.args[2], :tuple)) || - (isexpr(ex, :call) && ex.args[1] isa Symbol && startswith(String(ex.args[1]), ".")) -# e.g., `f.(x, y, z)` or `x .+ y .+ z` +is_dotcall(ex) = is_dotcall_fn(ex) || is_dotcall_op(ex) + +is_dotcall_fn(ex) = (isexpr(ex, :.) && isexpr(ex.args[2], :tuple)) +# e.g., `f.(x, y, z)` + +function is_dotcall_op(ex) + ex isa Expr && !isempty(ex.args) || return false + op = ex.args[1] + return op isa Symbol && Base.isoperator(op) && startswith(string(op), ".") +end +# e.g., `x .+ y .+ z` lazy_expr(x) = x function lazy_expr(ex::Expr) @@ -40,12 +47,12 @@ end bc_expr_impl(x) = x function bc_expr_impl(ex::Expr) # walk down chain of dot calls - if ex.head == :. && ex.args[2].head === :tuple + if is_dotcall_fn(ex) @assert length(ex.args) == 2 # argument is always expressed as a tuple f = ex.args[1] # function name args = ex.args[2].args return Expr(ex.head, lazy_expr(f), Expr(:tuple, bc_expr_impl.(args)...)) - elseif ex.head == :call && startswith(String(ex.args[1]), ".") + elseif is_dotcall_op(ex) f = ex.args[1] # function name (e.g., `.+`) args = ex.args[2:end] return Expr(ex.head, lazy_expr(f), bc_expr_impl.(args)...) From f3444df21d7688a04c60bf0b8870150e51a17fa7 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 22 Dec 2019 15:14:06 -0800 Subject: [PATCH 3/4] Workaround the test failure in lazymultests.jl --- test/lazymultests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lazymultests.jl b/test/lazymultests.jl index 5a17c460..a19b4df9 100644 --- a/test/lazymultests.jl +++ b/test/lazymultests.jl @@ -162,7 +162,7 @@ LinearAlgebra.factorize(A::MyLazyArray) = factorize(A.data) @test apply(*,A,x) isa ApplyVector @test apply(*,A,Array(x)) isa ApplyVector @test apply(*,Array(A),x) isa ApplyVector - @test apply(*,A,x) == apply(*,Array(A),x) == apply(*,A,Array(x)) == Array(A)*Array(x) + @test apply(*,A,x) ≈ apply(*,Array(A),x) ≈ apply(*,A,Array(x)) ≈ Array(A)*Array(x) @test apply(*,A,B) isa ApplyMatrix @test apply(*,A,Array(B)) isa ApplyMatrix From fa70c534f5affa45fedccce746888786568412e1 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 22 Dec 2019 20:40:22 -0800 Subject: [PATCH 4/4] More strict is_dotcall_op --- src/lazymacro.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lazymacro.jl b/src/lazymacro.jl index 3a2b74bb..c522ecd4 100644 --- a/src/lazymacro.jl +++ b/src/lazymacro.jl @@ -21,7 +21,7 @@ is_dotcall_fn(ex) = (isexpr(ex, :.) && isexpr(ex.args[2], :tuple)) # e.g., `f.(x, y, z)` function is_dotcall_op(ex) - ex isa Expr && !isempty(ex.args) || return false + isexpr(ex, :call) && !isempty(ex.args) || return false op = ex.args[1] return op isa Symbol && Base.isoperator(op) && startswith(string(op), ".") end