diff --git a/src/resolver.jl b/src/resolver.jl index ff42f05..c5c9a9c 100644 --- a/src/resolver.jl +++ b/src/resolver.jl @@ -1,90 +1,160 @@ +# A resolution: a pair consisting of a tag to be resolved and a corresponding regular expression. +const Resolution = Pair{String, Regex} - -# TODO: -# This is a punt for now. It does not handle any sort of custom resolving tags, -# only matching default implicits. - - -const DEFAULT_SCALAR_TAG = "tag:yaml.org,2002:str" -const DEFAULT_SEQUENCE_TAG = "tag:yaml.org,2002:seq" -const DEFAULT_MAPPING_TAG = "tag:yaml.org,2002:map" - - -const default_implicit_resolvers = - [ - ("tag:yaml.org,2002:bool", - r"^(?:true|True|TRUE|false|False|FALSE)$"x), - - ("tag:yaml.org,2002:int", - r"^(?:[-+]?0b[0-1_]+ - |[-+]? [0-9]+ - |0o [0-7]+ - |0x [0-9a-fA-F]+)$"x), - - ("tag:yaml.org,2002:float", - r"^(?:[-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )? - |[-+]? (?: \.inf | \.Inf | \.INF ) - |\.nan | \.NaN | \.NAN)$"x), - - ("tag:yaml.org,2002:merge", - r"^(?:<<)$"), - - ("tag:yaml.org,2002:null", - r"^(?:~|null|Null|NULL|)$"x), - - ("tag:yaml.org,2002:timestamp", - r"^(\d{4})- (?# year) - (\d\d?)- (?# month) - (\d\d?) (?# day) - (?: - (?:[Tt]|[ \t]+) - (\d\d?): (?# hour) - (\d\d): (?# minute) - (\d\d) (?# second) - (?:\.(\d*))? (?# fraction) - (?: - [ \t]*(Z|([+\-])(\d\d?) - (?: - :(\d\d) - )?) - )? - )?$"x), - - ("tag:yaml.org,2002:value", - r"^(?:=)$"), - - ("tag:yaml.org,2002:yaml", - r"^(?:!|&|\*)$") - ] - - +# A resolver: a complete set of resolutions of a schema. struct Resolver - implicit_resolvers::Vector{Tuple{String,Regex}} - - function Resolver() - new(copy(default_implicit_resolvers)) - end + default_scalar_tag::String + default_sequence_tag::String + default_mapping_tag::String + # `Dict{String, Regex}` might be better for resolutions. + # However, dicts are unordered so it changes the current behavior of `resolve`. + scalar_resolutions::Vector{Resolution} + sequence_resolutions::Vector{Resolution} + mapping_resolutions::Vector{Resolution} end +# ----------------------------- +# essential resolver instances +# ----------------------------- + +# The resolver for the failsafe schema. +const FAILSAFE_SCHEMA_RESOLVER = Resolver( + "tag:yaml.org,2002:str", + "tag:yaml.org,2002:seq", + "tag:yaml.org,2002:map", + [], + [], + [], +) + +# The resolver for the JSON schema. +const JSON_SCHEMA_RESOLVER = Resolver( + "tag:yaml.org,2002:str", + "tag:yaml.org,2002:seq", + "tag:yaml.org,2002:map", + [ + "tag:yaml.org,2002:null" => r"^(?: null )$"x, + "tag:yaml.org,2002:bool" => r"^(?: true | false )$"x, + "tag:yaml.org,2002:int" => r"^(?: -? ( 0 | [1-9] [0-9]* ) )$"x, + "tag:yaml.org,2002:float" => r"^(?: + -? ( 0 | [1-9] [0-9]* ) + ( \. [0-9]* )? + ( [eE] [-+]? [0-9]+ )? + )$"x, + ], + [], + [], +) + +# The resolver for the Core schema. +const CORE_SCHEMA_RESOLVER = Resolver( + "tag:yaml.org,2002:str", + "tag:yaml.org,2002:seq", + "tag:yaml.org,2002:map", + [ + "tag:yaml.org,2002:null" => r"^(?: null | Null | NULL | ~ | )$"x, + "tag:yaml.org,2002:bool" => r"^(?: true | True | TRUE | false | False | FALSE )$"x, + "tag:yaml.org,2002:int" => r"^(?: + [-+]? [0-9]+ | + 0o [0-7]+ | + 0x [0-9a-fA-F]+ + )$"x, + "tag:yaml.org,2002:float" => r"^(?: + [-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )? | + [-+]? ( \.inf | \.Inf | \.INF ) | + \.nan | \.NaN | \.NAN + )$"x, + ], + [], + [], +) + +# The resolver for the YAML.jl v0.4.10 schema. +const YAML_JL_0_4_10_RESOLVER = Resolver( + "tag:yaml.org,2002:str", + "tag:yaml.org,2002:seq", + "tag:yaml.org,2002:map", + [ + "tag:yaml.org,2002:bool" => r"^(?:true|True|TRUE|false|False|FALSE)$"x, + "tag:yaml.org,2002:int" => r"^(?: + [-+]?0b[0-1_]+ | + [-+]? [0-9]+ | + 0o [0-7]+ | + 0x [0-9a-fA-F]+ + )$"x, + "tag:yaml.org,2002:float" => r"^(?: + [-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )? | + [-+]? (?: \.inf | \.Inf | \.INF ) | + \.nan | \.NaN | \.NAN + )$"x, + "tag:yaml.org,2002:merge" => r"^(?:<<)$", + "tag:yaml.org,2002:null" => r"^(?:~|null|Null|NULL|)$"x, + "tag:yaml.org,2002:timestamp" => r"^ + (\d{4})- (?# year) + (\d\d?)- (?# month) + (\d\d?) (?# day) + (?: + (?:[Tt]|[ \t]+) + (\d\d?): (?# hour) + (\d\d): (?# minute) + (\d\d) (?# second) + (?:\.(\d*))? (?# fraction) + (?: + [ \t]*(Z|([+\-])(\d\d?) + (?: + :(\d\d) + )? + ) + )? + )? + $"x, + "tag:yaml.org,2002:value" => r"^(?:=)$", + "tag:yaml.org,2002:yaml" => r"^(?:!|&|\*)$", + ], + [], + [], +) + +# The default resolver. +# Currently, point to YAML.jl 0.4.10 resolver for non-breaking. +Resolver() = YAML_JL_0_4_10_RESOLVER + +""" + resolve(resolver::Resolver, N::Type{T}, value, implicit) where {T <: Node} + +Get the appropriate tag of the given `value` in the schema. +""" +function resolve end function resolve(resolver::Resolver, ::Type{ScalarNode}, value, implicit) if implicit[1] - for (tag, pat) in resolver.implicit_resolvers + for (tag, pat) in resolver.scalar_resolutions if occursin(pat, value) return tag end end end - - DEFAULT_SCALAR_TAG + resolver.default_scalar_tag end - function resolve(resolver::Resolver, ::Type{SequenceNode}, value, implicit) - DEFAULT_SEQUENCE_TAG + if implicit[1] + for (tag, pat) in resolver.sequence_resolutions + if occursin(pat, value) + return tag + end + end + end + resolver.default_sequence_tag end function resolve(resolver::Resolver, ::Type{MappingNode}, value, implicit) - DEFAULT_MAPPING_TAG + if implicit[1] + for (tag, pat) in resolver.mapping_resolutions + if occursin(pat, value) + return tag + end + end + end + resolver.default_mapping_tag end -