Skip to content

Commit

Permalink
TOML: Make Dates a type parameter (#55017)
Browse files Browse the repository at this point in the history
This will allow us to resolve the `Dates` at compile-time eventually. It
also fixes `TOML.Parser()` to return Dates types again.
  • Loading branch information
topolarity authored Jul 25, 2024
1 parent 1ece299 commit 0ffbae8
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 36 deletions.
10 changes: 7 additions & 3 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,15 @@ const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing)
LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict())


struct TOMLCache
p::TOML.Parser
struct TOMLCache{Dates}
p::TOML.Parser{Dates}
d::Dict{String, CachedTOMLDict}
end
const TOML_CACHE = TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}())
TOMLCache(p::TOML.Parser) = TOMLCache(p, Dict{String, CachedTOMLDict}())
# TODO: Delete this converting constructor once Pkg stops using it
TOMLCache(p::TOML.Parser, d::Dict{String, Dict{String, Any}}) = TOMLCache(p, convert(Dict{String, CachedTOMLDict}, d))

const TOML_CACHE = TOMLCache(TOML.Parser{nothing}())

parsed_toml(project_file::AbstractString) = parsed_toml(project_file, TOML_CACHE, require_lock)
function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_lock::ReentrantLock)
Expand Down
39 changes: 21 additions & 18 deletions base/toml_parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const TOMLDict = Dict{String, Any}
# Parser #
##########

mutable struct Parser
mutable struct Parser{Dates}
str::String
# 1 character look ahead
current_char::Char
Expand Down Expand Up @@ -86,12 +86,12 @@ mutable struct Parser
filepath::Union{String, Nothing}

# Optionally populate with the Dates stdlib to change the type of Date types returned
Dates::Union{Module, Nothing}
Dates::Union{Module, Nothing} # TODO: remove once Pkg is updated
end

function Parser(str::String; filepath=nothing)
function Parser{Dates}(str::String; filepath=nothing) where {Dates}
root = TOMLDict()
l = Parser(
l = Parser{Dates}(
str, # str
EOF_CHAR, # current_char
firstindex(str), # pos
Expand All @@ -112,6 +112,7 @@ function Parser(str::String; filepath=nothing)
startup(l)
return l
end

function startup(l::Parser)
# Populate our one character look-ahead
c = eat_char(l)
Expand All @@ -122,8 +123,10 @@ function startup(l::Parser)
end
end

Parser() = Parser("")
Parser(io::IO) = Parser(read(io, String))
Parser{Dates}() where {Dates} = Parser{Dates}("")
Parser{Dates}(io::IO) where {Dates} = Parser{Dates}(read(io, String))

# Parser(...) will be defined by TOML stdlib

function reinit!(p::Parser, str::String; filepath::Union{Nothing, String}=nothing)
p.str = str
Expand Down Expand Up @@ -1021,11 +1024,11 @@ function parse_datetime(l)
return try_return_datetime(l, year, month, day, h, m, s, ms)
end

function try_return_datetime(p, year, month, day, h, m, s, ms)
Dates = p.Dates
if Dates !== nothing
function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) where Dates
if Dates !== nothing || p.Dates !== nothing
mod = Dates !== nothing ? Dates : p.Dates
try
return Dates.DateTime(year, month, day, h, m, s, ms)
return mod.DateTime(year, month, day, h, m, s, ms)
catch ex
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
rethrow()
Expand All @@ -1035,11 +1038,11 @@ function try_return_datetime(p, year, month, day, h, m, s, ms)
end
end

function try_return_date(p, year, month, day)
Dates = p.Dates
if Dates !== nothing
function try_return_date(p::Parser{Dates}, year, month, day) where Dates
if Dates !== nothing || p.Dates !== nothing
mod = Dates !== nothing ? Dates : p.Dates
try
return Dates.Date(year, month, day)
return mod.Date(year, month, day)
catch ex
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
rethrow()
Expand All @@ -1058,11 +1061,11 @@ function parse_local_time(l::Parser)
return try_return_time(l, h, m, s, ms)
end

function try_return_time(p, h, m, s, ms)
Dates = p.Dates
if Dates !== nothing
function try_return_time(p::Parser{Dates}, h, m, s, ms) where Dates
if Dates !== nothing || p.Dates !== nothing
mod = Dates !== nothing ? Dates : p.Dates
try
return Dates.Time(h, m, s, ms)
return mod.Time(h, m, s, ms)
catch ex
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
rethrow()
Expand Down
4 changes: 3 additions & 1 deletion stdlib/REPL/src/Pkg_beforeload.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ end
function projname(project_file::String)
if isfile(project_file)
name = try
p = Base.TOML.Parser()
# The `nothing` here means that this TOML parser does not return proper Dates.jl
# objects - but that's OK since we're just checking the name here.
p = Base.TOML.Parser{nothing}()
Base.TOML.reinit!(p, read(project_file, String); filepath=project_file)
proj = Base.TOML.parse(p)
get(proj, "name", nothing)
Expand Down
22 changes: 8 additions & 14 deletions stdlib/TOML/src/TOML.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,10 @@ performance if a larger number of small files are parsed.
"""
const Parser = Internals.Parser

"""
DTParser()
Constructor for a TOML `Parser` which returns date and time objects from Dates.
"""
function DTParser(args...; kwargs...)
parser = Parser(args...; kwargs...)
parser.Dates = Dates
return parser
end
# Dates-enabled constructors
Parser() = Parser{Dates}()
Parser(io::IO) = Parser{Dates}(io)
Parser(str::String; filepath=nothing) = Parser{Dates}(str; filepath)

"""
parsefile(f::AbstractString)
Expand All @@ -59,7 +53,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a
See also [`TOML.tryparsefile`](@ref).
"""
parsefile(f::AbstractString) =
Internals.parse(DTParser(readstring(f); filepath=abspath(f)))
Internals.parse(Parser(readstring(f); filepath=abspath(f)))
parsefile(p::Parser, f::AbstractString) =
Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f)))

Expand All @@ -73,7 +67,7 @@ Parse file `f` and return the resulting table (dictionary). Return a
See also [`TOML.parsefile`](@ref).
"""
tryparsefile(f::AbstractString) =
Internals.tryparse(DTParser(readstring(f); filepath=abspath(f)))
Internals.tryparse(Parser(readstring(f); filepath=abspath(f)))
tryparsefile(p::Parser, f::AbstractString) =
Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f)))

Expand All @@ -87,7 +81,7 @@ Throw a [`ParserError`](@ref) upon failure.
See also [`TOML.tryparse`](@ref).
"""
parse(str::AbstractString) =
Internals.parse(DTParser(String(str)))
Internals.parse(Parser(String(str)))
parse(p::Parser, str::AbstractString) =
Internals.parse(Internals.reinit!(p, String(str)))
parse(io::IO) = parse(read(io, String))
Expand All @@ -103,7 +97,7 @@ Return a [`ParserError`](@ref) upon failure.
See also [`TOML.parse`](@ref).
"""
tryparse(str::AbstractString) =
Internals.tryparse(DTParser(String(str)))
Internals.tryparse(Parser(String(str)))
tryparse(p::Parser, str::AbstractString) =
Internals.tryparse(Internals.reinit!(p, String(str)))
tryparse(io::IO) = tryparse(read(io, String))
Expand Down
15 changes: 15 additions & 0 deletions stdlib/TOML/test/values.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,31 @@ using Test
using TOML
using TOML: Internals

# Construct an explicit Parser to test the "cached" version of parsing
const test_parser = TOML.Parser()

function testval(s, v)
f = "foo = $s"
# First, test with the standard entrypoint
parsed = TOML.parse(f)["foo"]
return isequal(v, parsed) && typeof(v) == typeof(parsed)
(!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false
# Next, test with the "cached" (explicit Parser) entrypoint
parsed = TOML.parse(test_parser, f)["foo"]
(!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false
return true
end

function failval(s, v)
f = "foo = $s"
# First, test with the standard entrypoint
err = TOML.tryparse(f);
return err isa TOML.Internals.ParserError && err.type == v
(!isa(err, TOML.Internals.ParserError) || err.type != v) && return false
# Next, test with the "cached" (explicit Parser) entrypoint
err = TOML.tryparse(test_parser, f);
(!isa(err, TOML.Internals.ParserError) || err.type != v) && return false
return true
end

@testset "Numbers" begin
Expand Down

0 comments on commit 0ffbae8

Please sign in to comment.