@@ -754,32 +754,87 @@ function vsym(expr::Expr)
754
754
end
755
755
end
756
756
757
- index_to_dict (i:: Integer ) = Dict (" type" => " integer" , " value" => i)
758
- index_to_dict (v:: AbstractVector{Int} ) = Dict (" type" => " vector" , " values" => v)
759
- index_to_dict (r:: UnitRange ) = Dict (" type" => " unitrange" , " start" => r. start, " stop" => r. stop)
760
- index_to_dict (r:: StepRange ) = Dict (" type" => " steprange" , " start" => r. start, " stop" => r. stop, " step" => r. step)
761
- index_to_dict (:: Colon ) = Dict (" type" => " colon" )
762
- index_to_dict (s:: ConcretizedSlice{T,Base.OneTo{I}} ) where {T,I} = Dict (" type" => " concretized_slice" , " oneto" => s. range. stop)
763
- index_to_dict (:: ConcretizedSlice{T,R} ) where {T,R} = error (" ConcretizedSlice with range type $(R) not supported" )
764
- index_to_dict (t:: Tuple ) = Dict (" type" => " tuple" , " values" => map (index_to_dict, t))
765
-
757
+ # String constants for each index type that we support serialisation /
758
+ # deserialisation of
759
+ const _BASE_INTEGER_TYPE = " Base.Integer"
760
+ const _BASE_VECTOR_TYPE = " Base.Vector"
761
+ const _BASE_UNITRANGE_TYPE = " Base.UnitRange"
762
+ const _BASE_STEPRANGE_TYPE = " Base.StepRange"
763
+ const _BASE_ONETO_TYPE = " Base.OneTo"
764
+ const _BASE_COLON_TYPE = " Base.Colon"
765
+ const _CONCRETIZED_SLICE_TYPE = " AbstractPPL.ConcretizedSlice"
766
+ const _BASE_TUPLE_TYPE = " Base.Tuple"
767
+
768
+ """
769
+ index_to_dict(::Integer)
770
+ index_to_dict(::AbstractVector{Int})
771
+ index_to_dict(::UnitRange)
772
+ index_to_dict(::StepRange)
773
+ index_to_dict(::Colon)
774
+ index_to_dict(::ConcretizedSlice{T, Base.OneTo{I}}) where {T, I}
775
+ index_to_dict(::Tuple)
776
+
777
+ Convert an index `i` to a dictionary representation.
778
+ """
779
+ index_to_dict (i:: Integer ) = Dict (" type" => _BASE_INTEGER_TYPE, " value" => i)
780
+ index_to_dict (v:: Vector{Int} ) = Dict (" type" => _BASE_VECTOR_TYPE, " values" => v)
781
+ index_to_dict (r:: UnitRange ) = Dict (" type" => _BASE_UNITRANGE_TYPE, " start" => r. start, " stop" => r. stop)
782
+ index_to_dict (r:: StepRange ) = Dict (" type" => _BASE_STEPRANGE_TYPE, " start" => r. start, " stop" => r. stop, " step" => r. step)
783
+ index_to_dict (r:: Base.OneTo{I} ) where {I} = Dict (" type" => _BASE_ONETO_TYPE, " stop" => r. stop)
784
+ index_to_dict (:: Colon ) = Dict (" type" => _BASE_COLON_TYPE)
785
+ index_to_dict (s:: ConcretizedSlice{T,R} ) where {T,R} = Dict (" type" => _CONCRETIZED_SLICE_TYPE, " range" => index_to_dict (s. range))
786
+ index_to_dict (t:: Tuple ) = Dict (" type" => _BASE_TUPLE_TYPE, " values" => map (index_to_dict, t))
787
+
788
+ """
789
+ dict_to_index(dict)
790
+ dict_to_index(symbol_val, dict)
791
+
792
+ Convert a dictionary representation of an index `dict` to an index.
793
+
794
+ Users can extend the functionality of `dict_to_index` (and hence `VarName`
795
+ de/serialisation) by extending this method along with [`index_to_dict`](@ref).
796
+ Specifically, suppose you have a custom index type `MyIndexType` and you want
797
+ to be able to de/serialise a `VarName` containing this index type. You should
798
+ then implement the following two methods:
799
+
800
+ 1. `AbstractPPL.index_to_dict(i::MyIndexType)` should return a dictionary
801
+ representation of the index `i`. This dictionary must contain the key
802
+ `"type"`, and the corresponding value must be a string that uniquely
803
+ identifies the index type. Generally, it makes sense to use the name of the
804
+ type (perhaps prefixed with module qualifiers) as this value to avoid
805
+ clashes. The remainder of the dictionary can have any structure you like.
806
+
807
+ 2. Suppose the value of `index_to_dict(i)["type"]` is "MyModule.MyIndexType".
808
+ You should then implement the corresponding method
809
+ `AbstractPPL.dict_to_index(::Val{Symbol("MyModule.MyIndexType")}, dict)`,
810
+ which should take the dictionary representation as the second argument and
811
+ return the original `MyIndexType` object.
812
+
813
+ To see an example of this in action, you can look in the the AbstractPPL test
814
+ suite, which contains a test for serialising OffsetArrays.
815
+ """
766
816
function dict_to_index (dict)
767
- if dict[" type" ] == " integer"
817
+ t = dict[" type" ]
818
+ if t == _BASE_INTEGER_TYPE
768
819
return dict[" value" ]
769
- elseif dict[ " type " ] == " vector "
820
+ elseif t == _BASE_VECTOR_TYPE
770
821
return collect (Int, dict[" values" ])
771
- elseif dict[ " type " ] == " unitrange "
822
+ elseif t == _BASE_UNITRANGE_TYPE
772
823
return dict[" start" ]: dict[" stop" ]
773
- elseif dict[ " type " ] == " steprange "
824
+ elseif t == _BASE_STEPRANGE_TYPE
774
825
return dict[" start" ]: dict[" step" ]: dict[" stop" ]
775
- elseif dict[" type" ] == " colon"
826
+ elseif t == _BASE_ONETO_TYPE
827
+ return Base. OneTo (dict[" stop" ])
828
+ elseif t == _BASE_COLON_TYPE
776
829
return Colon ()
777
- elseif dict[ " type " ] == " concretized_slice "
778
- return ConcretizedSlice (Base. Slice (Base . OneTo (dict[" oneto " ])))
779
- elseif dict[ " type " ] == " tuple "
830
+ elseif t == _CONCRETIZED_SLICE_TYPE
831
+ return ConcretizedSlice (Base. Slice (dict_to_index (dict[" range " ])))
832
+ elseif t == _BASE_TUPLE_TYPE
780
833
return tuple (map (dict_to_index, dict[" values" ])... )
781
834
else
782
- error (" Unknown index type: $(dict[" type" ]) " )
835
+ # Will error if the method is not defined, but this hook allows users
836
+ # to extend this function
837
+ return dict_to_index (Val (Symbol (t)), dict)
783
838
end
784
839
end
785
840
@@ -813,6 +868,12 @@ Convert a `VarName` as a string, via an intermediate dictionary. This differs
813
868
from `string(vn)` in that concretised slices are faithfully represented (rather
814
869
than being pretty-printed as colons).
815
870
871
+ For `VarName`s which index into an array, this function will only work if the
872
+ indices can be serialised. This is true for all standard Julia index types, but
873
+ if you are using custom index types, you will need to implement the
874
+ `index_to_dict` and `dict_to_index` methods for those types. See the
875
+ documentation of [`dict_to_index`](@ref) for instructions on how to do this.
876
+
816
877
```jldoctest
817
878
julia> vn_to_string(@varname(x))
818
879
"{\\ "optic\\ ":{\\ "type\\ ":\\ "identity\\ "},\\ "sym\\ ":\\ "x\\ "}"
@@ -821,18 +882,18 @@ julia> vn_to_string(@varname(x.a))
821
882
"{\\ "optic\\ ":{\\ "field\\ ":\\ "a\\ ",\\ "type\\ ":\\ "property\\ "},\\ "sym\\ ":\\ "x\\ "}"
822
883
823
884
julia> y = ones(2); vn_to_string(@varname(y[:]))
824
- "{\\ "optic\\ ":{\\ "indices\\ ":{\\ "values\\ ":[{\\ "type\\ ":\\ "colon \\ "}],\\ "type\\ ":\\ "tuple \\ "},\\ "type\\ ":\\ "index\\ "},\\ "sym\\ ":\\ "y\\ "}"
885
+ "{\\ "optic\\ ":{\\ "indices\\ ":{\\ "values\\ ":[{\\ "type\\ ":\\ "Base.Colon \\ "}],\\ "type\\ ":\\ "Base.Tuple \\ "},\\ "type\\ ":\\ "index\\ "},\\ "sym\\ ":\\ "y\\ "}"
825
886
826
887
julia> y = ones(2); vn_to_string(@varname(y[:], true))
827
- "{\\ "optic\\ ":{\\ "indices\\ ":{\\ "values\\ ":[{\\ "oneto \\ ":2,\\ "type\\ ":\\ "concretized_slice \\ "}],\\ "type\\ ":\\ "tuple \\ "},\\ "type\\ ":\\ "index\\ "},\\ "sym\\ ":\\ "y\\ "}"
888
+ "{\\ "optic\\ ":{\\ "indices\\ ":{\\ "values\\ ":[{\\ "range \\ ":{ \\ "stop \\ ": 2,\\ "type\\ ":\\ "Base.OneTo \\ "}, \\ "type \\ ": \\ "AbstractPPL.ConcretizedSlice \\ "} ],\\ "type\\ ":\\ "Base.Tuple \\ "},\\ "type\\ ":\\ "index\\ "},\\ "sym\\ ":\\ "y\\ "}"
828
889
```
829
890
"""
830
891
vn_to_string (vn:: VarName ) = JSON. json (vn_to_dict (vn))
831
892
832
893
"""
833
- vn_from_string(str)
894
+ vn_from_string(str::AbstractString )
834
895
835
896
Convert a string representation of a `VarName` back to a `VarName`. The string
836
897
should have been generated by `vn_to_string`.
837
898
"""
838
- vn_from_string (str) = dict_to_vn (JSON. parse (str))
899
+ vn_from_string (str:: AbstractString ) = dict_to_vn (JSON. parse (str))
0 commit comments