Skip to content
This repository has been archived by the owner on Apr 18, 2023. It is now read-only.

Commit

Permalink
Merge pull request #108 from invenia/ed/remove-evals
Browse files Browse the repository at this point in the history
Remove uses of `eval` from macros
  • Loading branch information
ararslan committed Dec 13, 2018
2 parents 0a5195a + 98c8473 commit ecca2eb
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 4 deletions.
48 changes: 48 additions & 0 deletions src/code_transformation/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,51 @@ replace_vararg(typ::SymOrExpr, vararg_info::Tuple) =
vararg_info[2] == :no_N || vararg_info[2] == :Vararg ?
replace_body(typ, :(Vararg{$(get_body(typ))})) :
replace_body(typ, :(Vararg{$(get_body(typ)), $(vararg_info[2])}))

"""
parse_kwargs(nt_expr) -> NamedTuple
Accepts an expression containing a `NamedTuple` literal and parses it into a `NamedTuple`
with expressions as values.
"""
function parse_kwargs(nt_expr)
if isempty(nt_expr.args) || nt_expr == Expr(:call, :NamedTuple)
return NamedTuple()
end

first_arg = first(nt_expr.args)
if first_arg isa Expr && first_arg.head == :parameters
return parse_kwargs_parameters(nt_expr)
elseif first_arg isa Expr && first_arg.head == :(=)
return parse_kwargs_tuple(nt_expr)
else
throw(ArgumentError("Unsupported expression $nt_expr for kwargs;"
* " they must be passed as a NamedTuple literal"))
end
end

function parse_kwargs_tuple(tup_expr)
nt_names = Tuple(first(ex.args) for ex in tup_expr.args)
return NamedTuple{nt_names}(last(ex.args) for ex in tup_expr.args)
end

function parse_kwargs_parameters(param_tuple_expr)
# code is the same even though the inner args are also different expression types
# (:kw vs :())
return parse_kwargs_tuple(param_tuple_expr.args[1])
end

"""
parse_is_node(bool_array_expr) -> Vector{Bool}
Accepts an expression containing a `Vector{Bool}` literal and parses it into a
`Vector{Bool}`.
"""
function parse_is_node(bool_array_expr)
if bool_array_expr.head != :vect
throw(ArgumentError("Unsupported expression $bool_array_expr for is_node; "
* "it must be passed as a `Vector{Bool}` literal (e.g., `[true, false]`)"))
end

return collect(Bool, bool_array_expr.args)
end
3 changes: 2 additions & 1 deletion src/sensitivities/indexing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import Base.getindex
for i = 1:7
T = Expr(:curly, :Tuple, fill(:Any, i)...)
@eval @explicit_intercepts getindex $T [[true]; fill(false, $i - 1)]
is_node = Expr(:vect, true, fill(false, i - 1)...)
@eval @explicit_intercepts getindex $T $is_node
end

function (Ā, ::typeof(getindex), ::Type{Arg{1}}, p, y, ȳ, A, inds...)
Expand Down
8 changes: 5 additions & 3 deletions src/sensitivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ should be provided as a `NamedTuple` and be added to the generated function's si
"""
macro union_intercepts(f::Symbol, type_tuple::Expr, invoke_type_tuple::Expr, kwargs::Expr=:(()))
kwargs.head === :tuple || throw(ArgumentError("malformed keyword argument specification"))
return esc(union_intercepts(f, type_tuple, invoke_type_tuple; eval(kwargs)...))
return esc(union_intercepts(f, type_tuple, invoke_type_tuple; parse_kwargs(kwargs)...))
end

"""
Expand Down Expand Up @@ -39,10 +39,12 @@ to add to the function signature can be specified in `kwargs`, which must be a `
macro explicit_intercepts(
f::SymOrExpr,
type_tuple::Expr,
is_node::Expr=:([true for _ in $(get_types(get_body(type_tuple)))]),
is_node::Expr=Expr(:vect, (true for _ in get_types(get_body(type_tuple)))...),
kwargs::Expr=:(()),
)
return esc(explicit_intercepts(f, type_tuple, eval(is_node); eval(kwargs)...))
return esc(
explicit_intercepts(f, type_tuple, parse_is_node(is_node); parse_kwargs(kwargs)...)
)
end

"""
Expand Down
19 changes: 19 additions & 0 deletions test/code_transformation/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,23 @@
@test Nabla.replace_vararg(:(T where T), (:T, :2)) == :(Vararg{T, 2} where T)
@test Nabla.replace_vararg(:(U{T} where T), (:T, :N)) == :(Vararg{U{T}, N} where T)
@test Nabla.replace_vararg(:(U{T} where N), (:T, :N)) == :(Vararg{U{T}, N} where N)

@testset "parse_kwargs" begin
@test Nabla.parse_kwargs(:(())) == NamedTuple()
@test Nabla.parse_kwargs(:(NamedTuple())) == NamedTuple()
@test Nabla.parse_kwargs(:((a = 1, b = 2))) == NamedTuple{(:a, :b)}((1, 2))
@test Nabla.parse_kwargs(:((; a = 1, b = 2))) == NamedTuple{(:a, :b)}((1, 2))
@test Nabla.parse_kwargs(:((a = sum(1:10), b = :c))) ==
NamedTuple{(:a, :b)}((:(sum(1:10)), :(:c)))
@test Nabla.parse_kwargs(:((; a = sum(1:10), b = :c))) ==
NamedTuple{(:a, :b)}((:(sum(1:10)), :(:c)))
@test_throws ArgumentError Nabla.parse_kwargs(:([a => 2]))
end

@testset "parse_is_node" begin
@test Nabla.parse_is_node(:([])) == Bool[]
@test Nabla.parse_is_node(:([true])) == [true]
@test Nabla.parse_is_node(:([true, false])) == [true, false]
@test_throws ArgumentError Nabla.parse_is_node(:((true, false)))
end
end

0 comments on commit ecca2eb

Please sign in to comment.