Skip to content

Commit 0ffbae8

Browse files
authored
TOML: Make Dates a type parameter (#55017)
This will allow us to resolve the `Dates` at compile-time eventually. It also fixes `TOML.Parser()` to return Dates types again.
1 parent 1ece299 commit 0ffbae8

File tree

5 files changed

+54
-36
lines changed

5 files changed

+54
-36
lines changed

base/loading.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,15 @@ const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing)
264264
LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict())
265265

266266

267-
struct TOMLCache
268-
p::TOML.Parser
267+
struct TOMLCache{Dates}
268+
p::TOML.Parser{Dates}
269269
d::Dict{String, CachedTOMLDict}
270270
end
271-
const TOML_CACHE = TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}())
271+
TOMLCache(p::TOML.Parser) = TOMLCache(p, Dict{String, CachedTOMLDict}())
272+
# TODO: Delete this converting constructor once Pkg stops using it
273+
TOMLCache(p::TOML.Parser, d::Dict{String, Dict{String, Any}}) = TOMLCache(p, convert(Dict{String, CachedTOMLDict}, d))
274+
275+
const TOML_CACHE = TOMLCache(TOML.Parser{nothing}())
272276

273277
parsed_toml(project_file::AbstractString) = parsed_toml(project_file, TOML_CACHE, require_lock)
274278
function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_lock::ReentrantLock)

base/toml_parser.jl

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const TOMLDict = Dict{String, Any}
3838
# Parser #
3939
##########
4040

41-
mutable struct Parser
41+
mutable struct Parser{Dates}
4242
str::String
4343
# 1 character look ahead
4444
current_char::Char
@@ -86,12 +86,12 @@ mutable struct Parser
8686
filepath::Union{String, Nothing}
8787

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

92-
function Parser(str::String; filepath=nothing)
92+
function Parser{Dates}(str::String; filepath=nothing) where {Dates}
9393
root = TOMLDict()
94-
l = Parser(
94+
l = Parser{Dates}(
9595
str, # str
9696
EOF_CHAR, # current_char
9797
firstindex(str), # pos
@@ -112,6 +112,7 @@ function Parser(str::String; filepath=nothing)
112112
startup(l)
113113
return l
114114
end
115+
115116
function startup(l::Parser)
116117
# Populate our one character look-ahead
117118
c = eat_char(l)
@@ -122,8 +123,10 @@ function startup(l::Parser)
122123
end
123124
end
124125

125-
Parser() = Parser("")
126-
Parser(io::IO) = Parser(read(io, String))
126+
Parser{Dates}() where {Dates} = Parser{Dates}("")
127+
Parser{Dates}(io::IO) where {Dates} = Parser{Dates}(read(io, String))
128+
129+
# Parser(...) will be defined by TOML stdlib
127130

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

1024-
function try_return_datetime(p, year, month, day, h, m, s, ms)
1025-
Dates = p.Dates
1026-
if Dates !== nothing
1027+
function try_return_datetime(p::Parser{Dates}, year, month, day, h, m, s, ms) where Dates
1028+
if Dates !== nothing || p.Dates !== nothing
1029+
mod = Dates !== nothing ? Dates : p.Dates
10271030
try
1028-
return Dates.DateTime(year, month, day, h, m, s, ms)
1031+
return mod.DateTime(year, month, day, h, m, s, ms)
10291032
catch ex
10301033
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
10311034
rethrow()
@@ -1035,11 +1038,11 @@ function try_return_datetime(p, year, month, day, h, m, s, ms)
10351038
end
10361039
end
10371040

1038-
function try_return_date(p, year, month, day)
1039-
Dates = p.Dates
1040-
if Dates !== nothing
1041+
function try_return_date(p::Parser{Dates}, year, month, day) where Dates
1042+
if Dates !== nothing || p.Dates !== nothing
1043+
mod = Dates !== nothing ? Dates : p.Dates
10411044
try
1042-
return Dates.Date(year, month, day)
1045+
return mod.Date(year, month, day)
10431046
catch ex
10441047
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
10451048
rethrow()
@@ -1058,11 +1061,11 @@ function parse_local_time(l::Parser)
10581061
return try_return_time(l, h, m, s, ms)
10591062
end
10601063

1061-
function try_return_time(p, h, m, s, ms)
1062-
Dates = p.Dates
1063-
if Dates !== nothing
1064+
function try_return_time(p::Parser{Dates}, h, m, s, ms) where Dates
1065+
if Dates !== nothing || p.Dates !== nothing
1066+
mod = Dates !== nothing ? Dates : p.Dates
10641067
try
1065-
return Dates.Time(h, m, s, ms)
1068+
return mod.Time(h, m, s, ms)
10661069
catch ex
10671070
ex isa ArgumentError && return ParserError(ErrParsingDateTime)
10681071
rethrow()

stdlib/REPL/src/Pkg_beforeload.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ end
7171
function projname(project_file::String)
7272
if isfile(project_file)
7373
name = try
74-
p = Base.TOML.Parser()
74+
# The `nothing` here means that this TOML parser does not return proper Dates.jl
75+
# objects - but that's OK since we're just checking the name here.
76+
p = Base.TOML.Parser{nothing}()
7577
Base.TOML.reinit!(p, read(project_file, String); filepath=project_file)
7678
proj = Base.TOML.parse(p)
7779
get(proj, "name", nothing)

stdlib/TOML/src/TOML.jl

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,10 @@ performance if a larger number of small files are parsed.
3838
"""
3939
const Parser = Internals.Parser
4040

41-
"""
42-
DTParser()
43-
44-
Constructor for a TOML `Parser` which returns date and time objects from Dates.
45-
"""
46-
function DTParser(args...; kwargs...)
47-
parser = Parser(args...; kwargs...)
48-
parser.Dates = Dates
49-
return parser
50-
end
41+
# Dates-enabled constructors
42+
Parser() = Parser{Dates}()
43+
Parser(io::IO) = Parser{Dates}(io)
44+
Parser(str::String; filepath=nothing) = Parser{Dates}(str; filepath)
5145

5246
"""
5347
parsefile(f::AbstractString)
@@ -59,7 +53,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a
5953
See also [`TOML.tryparsefile`](@ref).
6054
"""
6155
parsefile(f::AbstractString) =
62-
Internals.parse(DTParser(readstring(f); filepath=abspath(f)))
56+
Internals.parse(Parser(readstring(f); filepath=abspath(f)))
6357
parsefile(p::Parser, f::AbstractString) =
6458
Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f)))
6559

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

@@ -87,7 +81,7 @@ Throw a [`ParserError`](@ref) upon failure.
8781
See also [`TOML.tryparse`](@ref).
8882
"""
8983
parse(str::AbstractString) =
90-
Internals.parse(DTParser(String(str)))
84+
Internals.parse(Parser(String(str)))
9185
parse(p::Parser, str::AbstractString) =
9286
Internals.parse(Internals.reinit!(p, String(str)))
9387
parse(io::IO) = parse(read(io, String))
@@ -103,7 +97,7 @@ Return a [`ParserError`](@ref) upon failure.
10397
See also [`TOML.parse`](@ref).
10498
"""
10599
tryparse(str::AbstractString) =
106-
Internals.tryparse(DTParser(String(str)))
100+
Internals.tryparse(Parser(String(str)))
107101
tryparse(p::Parser, str::AbstractString) =
108102
Internals.tryparse(Internals.reinit!(p, String(str)))
109103
tryparse(io::IO) = tryparse(read(io, String))

stdlib/TOML/test/values.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,31 @@ using Test
44
using TOML
55
using TOML: Internals
66

7+
# Construct an explicit Parser to test the "cached" version of parsing
8+
const test_parser = TOML.Parser()
9+
710
function testval(s, v)
811
f = "foo = $s"
12+
# First, test with the standard entrypoint
913
parsed = TOML.parse(f)["foo"]
1014
return isequal(v, parsed) && typeof(v) == typeof(parsed)
15+
(!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false
16+
# Next, test with the "cached" (explicit Parser) entrypoint
17+
parsed = TOML.parse(test_parser, f)["foo"]
18+
(!isequal(v, parsed) || typeof(v) != typeof(parsed)) && return false
19+
return true
1120
end
1221

1322
function failval(s, v)
1423
f = "foo = $s"
24+
# First, test with the standard entrypoint
1525
err = TOML.tryparse(f);
1626
return err isa TOML.Internals.ParserError && err.type == v
27+
(!isa(err, TOML.Internals.ParserError) || err.type != v) && return false
28+
# Next, test with the "cached" (explicit Parser) entrypoint
29+
err = TOML.tryparse(test_parser, f);
30+
(!isa(err, TOML.Internals.ParserError) || err.type != v) && return false
31+
return true
1732
end
1833

1934
@testset "Numbers" begin

0 commit comments

Comments
 (0)