diff --git a/src/varname.jl b/src/varname.jl index a154d0f..a0a56fa 100644 --- a/src/varname.jl +++ b/src/varname.jl @@ -754,118 +754,6 @@ function vsym(expr::Expr) end end -""" - index_to_str(i) - -Generates a string representation of the index `i`, or a tuple thereof. - -## Examples - -```jldoctest -julia> AbstractPPL.index_to_str(2) -"2" - -julia> AbstractPPL.index_to_str((1, 2:5)) -"(1, 2:5,)" - -julia> AbstractPPL.index_to_str(:) -":" - -julia> AbstractPPL.index_to_str(AbstractPPL.ConcretizedSlice(Base.Slice(Base.OneTo(10)))) -"ConcretizedSlice(Base.OneTo(10))" -``` -""" -index_to_str(i::Integer) = string(i) -index_to_str(r::UnitRange) = "$(first(r)):$(last(r))" -index_to_str(::Colon) = ":" -index_to_str(s::ConcretizedSlice{T,R}) where {T,R} = "ConcretizedSlice(" * repr(s.range) * ")" -index_to_str(t::Tuple) = "(" * join(map(index_to_str, t), ", ") * ",)" - -""" - optic_to_nt(optic) - -Convert an optic to a named tuple representation. - -## Examples -```jldoctest; setup=:(using Accessors) -julia> AbstractPPL.optic_to_nt(identity) -(type = "identity",) - -julia> AbstractPPL.optic_to_nt(@optic _.a) -(type = "property", field = "a") - -julia> AbstractPPL.optic_to_nt(@optic _.a.b) -(type = "composed", outer = (type = "property", field = "b"), inner = (type = "property", field = "a")) - -julia> AbstractPPL.optic_to_nt(@optic _[1]) # uses index_to_str() -(type = "index", indices = "(1,)") -``` -""" -optic_to_nt(::typeof(identity)) = (type = "identity",) -optic_to_nt(::PropertyLens{sym}) where {sym} = (type = "property", field = String(sym)) -optic_to_nt(i::IndexLens) = (type = "index", indices = index_to_str(i.indices)) -optic_to_nt(c::ComposedOptic) = (type = "composed", outer = optic_to_nt(c.outer), inner = optic_to_nt(c.inner)) - - -""" - nt_to_optic(nt) - -Convert a named tuple representation back to an optic. -""" -function nt_to_optic(nt) - if nt.type == "identity" - return identity - elseif nt.type == "index" - return IndexLens(eval(Meta.parse(nt.indices))) - elseif nt.type == "property" - return PropertyLens{Symbol(nt.field)}() - elseif nt.type == "composed" - return nt_to_optic(nt.outer) ∘ nt_to_optic(nt.inner) - end -end - -""" - vn_to_string(vn::VarName) - -Convert a `VarName` as a string, via an intermediate named tuple. This differs -from `string(vn)` in that concretised slices are faithfully represented (rather -than being pretty-printed as colons). - -```jldoctest -julia> vn_to_string(@varname(x)) -"(sym = \\"x\\", optic = (type = \\"identity\\",))" - -julia> vn_to_string(@varname(x.a)) -"(sym = \\"x\\", optic = (type = \\"property\\", field = \\"a\\"))" - -julia> y = ones(2); vn_to_string(@varname(y[:])) -"(sym = \\"y\\", optic = (type = \\"index\\", indices = \\"(:,)\\"))" - -julia> y = ones(2); vn_to_string(@varname(y[:], true)) -"(sym = \\"y\\", optic = (type = \\"index\\", indices = \\"(ConcretizedSlice(Base.OneTo(2)),)\\"))" -``` -""" -vn_to_string(vn::VarName) = repr((sym = String(getsym(vn)), optic = optic_to_nt(getoptic(vn)))) - -""" - vn_from_string(str) - -Convert a string representation of a `VarName` back to a `VarName`. The string -should have been generated by `vn_to_string`. - -!!! warning - This function should only be used with trusted input, as it uses `eval` -and `Meta.parse` to parse the string. -""" -function vn_from_string(str) - new_fields = eval(Meta.parse(str)) - return VarName{Symbol(new_fields.sym)}(nt_to_optic(new_fields.optic)) -end - -# ----------------------------------------- -# Alternate implementation with StructTypes -# ----------------------------------------- - index_to_dict(i::Integer) = Dict("type" => "integer", "value" => i) index_to_dict(v::AbstractVector{Int}) = Dict("type" => "vector", "values" => v) index_to_dict(r::UnitRange) = Dict("type" => "unitrange", "start" => r.start, "stop" => r.stop) @@ -918,6 +806,33 @@ vn_to_dict(vn::VarName) = Dict("sym" => getsym(vn), "optic" => optic_to_dict(get dict_to_vn(dict::Dict{<:AbstractString, Any}) = VarName{Symbol(dict["sym"])}(dict_to_optic(dict["optic"])) -vn_to_string2(vn::VarName) = JSON.json(vn_to_dict(vn)) +""" + vn_to_string(vn::VarName) + +Convert a `VarName` as a string, via an intermediate dictionary. This differs +from `string(vn)` in that concretised slices are faithfully represented (rather +than being pretty-printed as colons). + +```jldoctest +julia> vn_to_string(@varname(x)) +"{\\"optic\\":{\\"type\\":\\"identity\\"},\\"sym\\":\\"x\\"}" + +julia> vn_to_string(@varname(x.a)) +"{\\"optic\\":{\\"field\\":\\"a\\",\\"type\\":\\"property\\"},\\"sym\\":\\"x\\"}" + +julia> y = ones(2); vn_to_string(@varname(y[:])) +"{\\"optic\\":{\\"indices\\":{\\"values\\":[{\\"type\\":\\"colon\\"}],\\"type\\":\\"tuple\\"},\\"type\\":\\"index\\"},\\"sym\\":\\"y\\"}" + +julia> y = ones(2); vn_to_string(@varname(y[:], true)) +"{\\"optic\\":{\\"indices\\":{\\"values\\":[{\\"oneto\\":2,\\"type\\":\\"concretized_slice\\"}],\\"type\\":\\"tuple\\"},\\"type\\":\\"index\\"},\\"sym\\":\\"y\\"}" +``` +""" +vn_to_string(vn::VarName) = JSON.json(vn_to_dict(vn)) + +""" + vn_from_string(str) -vn_from_string2(str) = dict_to_vn(JSON.parse(str)) +Convert a string representation of a `VarName` back to a `VarName`. The string +should have been generated by `vn_to_string`. +""" +vn_from_string(str) = dict_to_vn(JSON.parse(str)) diff --git a/test/varname.jl b/test/varname.jl index ffa2a6d..94a1d5b 100644 --- a/test/varname.jl +++ b/test/varname.jl @@ -172,7 +172,7 @@ end @varname(z[2:5,:], true), ] for vn in vns - @test vn_from_string2(vn_to_string2(vn)) == vn + @test vn_from_string(vn_to_string(vn)) == vn end # For this VarName, the {de,}serialisation works correctly but we must @@ -181,7 +181,7 @@ end # addresses rather than the contents (thus vn_vec == vn_vec2 returns # false). vn_vec = @varname(x[[1, 2, 5, 6]]) - vn_vec2 = vn_from_string2(vn_to_string2(vn_vec)) + vn_vec2 = vn_from_string(vn_to_string(vn_vec)) @test hash(vn_vec) == hash(vn_vec2) end end