diff --git a/src/Parameters.jl b/src/Parameters.jl index 15b9f9e..12800b2 100644 --- a/src/Parameters.jl +++ b/src/Parameters.jl @@ -262,7 +262,8 @@ function _pack_new(T, fields) Expr(:call, T, fields...) end -const macro_hidden_nargs = length(:(@m).args) - 1 # ==1 on Julia 0.6, ==2 on Julia 0.7 +"The symbol to use to indicate that `typeof(default_value)` is used." +const TYPEOF=:typeof """ This function is called by the `@with_kw` macro and does the syntax @@ -341,10 +342,10 @@ function with_kw(typedef, mod::Module, withshow=true) l, i = next(lns, start(lns)) if l isa Expr && l.head == :macrocall && l.args[1] == Symbol("@deftype") has_deftyp = true - if length(l.args) != (2 + macro_hidden_nargs) + if length(l.args) != 3 error("Malformed `@deftype` line $l") end - deftyp = l.args[2 + macro_hidden_nargs] + deftyp = l.args[3] if done(lns, i) error("@with_kw only supported for types which have at least one field.") end @@ -385,7 +386,7 @@ function with_kw(typedef, mod::Module, withshow=true) unpack_vars = Any[] # the type def fielddefs = quote end # holds r::R etc - fielddefs.args = Any[] # in julia 0.5 this is [:( # /home/mauro/.julia/v0.5/Parameters/src/Parameters.jl, line 228:)] + fielddefs.args = Any[] kws = OrderedDict{Any, Any}() # assertions in the body asserts = Any[] @@ -395,7 +396,7 @@ function with_kw(typedef, mod::Module, withshow=true) continue end if l isa Symbol # no default value and no type annotation - if has_deftyp + if has_deftyp && deftyp!=TYPEOF # doesn't work to do ::typeof(default_value) here push!(fielddefs.args, :($l::$deftyp)) else push!(fielddefs.args, l) @@ -407,16 +408,22 @@ function with_kw(typedef, mod::Module, withshow=true) push!(unpack_vars, sym) elseif l isa String # doc-string push!(fielddefs.args, l) - elseif l.head==:(=) # default value and with or without type annotation + elseif l.head==:(=) if l.args[1] isa Expr && (l.args[1].head==:call || # inner constructor l.args[1].head==:where && l.args[1].args[1].head==:call) # inner constructor with `where` check_inner_constructor(l) push!(inner_constructors, l) - else + else # default value and with or without type annotation fld = l.args[1] if fld isa Symbol && has_deftyp # no type annotation fld = :($fld::$deftyp) end + # process TYPEOF + if !(fld isa Symbol) && fld.args[2]==TYPEOF + # replace with typeof(default_value) + fld.args[2] = :(typeof($(l.args[2]))) + end + # add field doc-strings docstring = string("Default: ", l.args[2]) if i > 1 && lns[i-1] isa String @@ -426,6 +433,7 @@ function with_kw(typedef, mod::Module, withshow=true) # otherwise add a new line push!(fielddefs.args, docstring) end + # add to output push!(fielddefs.args, fld) kws[decolon2(fld)] = l.args[2] # unwrap-macro @@ -440,6 +448,7 @@ function with_kw(typedef, mod::Module, withshow=true) elseif l.head==:block error("No nested begin-end allowed in type defintion") else # no default value but with type annotation + l.args[2]==TYPEOF && error("Cannot infer type from default if there is no default.") push!(fielddefs.args, l) sym = decolon2(l.args[1]) syms = string(sym) @@ -523,6 +532,20 @@ function with_kw(typedef, mod::Module, withshow=true) # constructors are to allow both calls: # `MT4(r=4, a=5.0)` (outer kwarg-constructor) and # `MT4{Float32, Int}(r=4, a=5.)` (inner kwarg constructor). + # + # NOTE to above NOTE: this is probably not the case (anymore?), + # as Base.@kwdef does not define inner constructors: + # julia> Base.@kwdef struct MT4_{R,I} + # r::R=5 + # a::I + # end + # + # julia> MT4_(r=4, a=5.0) + # MT4_{Int64,Float64}(4, 5.0) + # + # julia> MT4_{Float32, Int}(r=4, a=5.) + # MT4_{Float32,Int64}(4.0f0, 5) + ## outer copy constructor ###