Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Internal, New Feature, No Breaking] Rewrite resolver for customization and user-selectable schema #172

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 141 additions & 71 deletions src/resolver.jl
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 144 in src/resolver.jl

View check run for this annotation

Codecov / codecov/patch

src/resolver.jl#L143-L144

Added lines #L143 - L144 were not covered by tests
end
end

Check warning on line 146 in src/resolver.jl

View check run for this annotation

Codecov / codecov/patch

src/resolver.jl#L146

Added line #L146 was not covered by tests
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

Check warning on line 155 in src/resolver.jl

View check run for this annotation

Codecov / codecov/patch

src/resolver.jl#L154-L155

Added lines #L154 - L155 were not covered by tests
end
end

Check warning on line 157 in src/resolver.jl

View check run for this annotation

Codecov / codecov/patch

src/resolver.jl#L157

Added line #L157 was not covered by tests
end
resolver.default_mapping_tag
end

Loading