Skip to content

Commit 9f80d46

Browse files
committed
update TOML from upstream library
1 parent 6fc3d2c commit 9f80d46

File tree

270 files changed

+302
-1292
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

270 files changed

+302
-1292
lines changed

base/toml_parser.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ end
194194
# Inline tables
195195
ErrExpectedCommaBetweenItemsInlineTable
196196
ErrTrailingCommaInlineTable
197+
ErrInlineTableRedefine
197198

198199
# Numbers
199200
ErrUnderscoreNotSurroundedByDigits
@@ -202,6 +203,7 @@ end
202203
ErrLeadingDot
203204
ErrNoTrailingDigitAfterDot
204205
ErrTrailingUnderscoreNumber
206+
ErrSignInNonBase10Number
205207

206208
# DateTime
207209
ErrParsingDateTime
@@ -229,6 +231,7 @@ const err_message = Dict(
229231
ErrUnexpectedEndString => "string literal ended unexpectedly",
230232
ErrExpectedEndOfTable => "expected end of table ']'",
231233
ErrAddKeyToInlineTable => "tried to add a new key to an inline table",
234+
ErrInlineTableRedefine => "inline table overwrote key from other table",
232235
ErrArrayTreatedAsDictionary => "tried to add a key to an array",
233236
ErrAddArrayToStaticArray => "tried to append to a statically defined array",
234237
ErrGenericValueError => "failed to parse value",
@@ -244,7 +247,8 @@ const err_message = Dict(
244247
ErrOverflowError => "overflowed when parsing integer",
245248
ErrInvalidUnicodeScalar => "invalid unicode scalar",
246249
ErrInvalidEscapeCharacter => "invalid escape character",
247-
ErrUnexpectedEofExpectedValue => "unexpected end of file, expected a value"
250+
ErrUnexpectedEofExpectedValue => "unexpected end of file, expected a value",
251+
ErrSignInNonBase10Number => "number not in base 10 is not allowed to have a sign",
248252
)
249253

250254
for err in instances(ErrorType)
@@ -467,15 +471,15 @@ function parse_toplevel(l::Parser)::Err{Nothing}
467471
l.active_table = l.root
468472
@try parse_table(l)
469473
skip_ws_comment(l)
470-
if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == EOF_CHAR)
474+
if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == '#' || peek(l) == EOF_CHAR)
471475
eat_char(l)
472476
return ParserError(ErrExpectedNewLineKeyValue)
473477
end
474478
else
475479
@try parse_entry(l, l.active_table)
476480
skip_ws_comment(l)
477481
# SPEC: "There must be a newline (or EOF) after a key/value pair."
478-
if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == EOF_CHAR)
482+
if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == '#' || peek(l) == EOF_CHAR)
479483
c = eat_char(l)
480484
return ParserError(ErrExpectedNewLineKeyValue)
481485
end
@@ -563,6 +567,10 @@ function parse_entry(l::Parser, d)::Union{Nothing, ParserError}
563567

564568
skip_ws(l)
565569
value = @try parse_value(l)
570+
# Not allowed to overwrite a value with an inline dict
571+
if value isa Dict && haskey(d, last_key_part)
572+
return ParserError(ErrInlineTableRedefine)
573+
end
566574
# TODO: Performance, hashing `last_key_part` again here
567575
d[last_key_part] = value
568576
return
@@ -789,9 +797,11 @@ function parse_number_or_date_start(l::Parser)
789797

790798
set_marker!(l)
791799
sgn = 1
800+
parsed_sign = false
792801
if accept(l, '+')
793-
# do nothing
802+
parsed_sign = true
794803
elseif accept(l, '-')
804+
parsed_sign = true
795805
sgn = -1
796806
end
797807
if accept(l, 'i')
@@ -811,12 +821,15 @@ function parse_number_or_date_start(l::Parser)
811821
if ok_end_value(peek(l))
812822
return Int64(0)
813823
elseif accept(l, 'x')
824+
parsed_sign && return ParserError(ErrSignInNonBase10Number)
814825
ate, contains_underscore = @try accept_batch_underscore(l, isvalid_hex)
815826
ate && return parse_int(l, contains_underscore)
816827
elseif accept(l, 'o')
828+
parsed_sign && return ParserError(ErrSignInNonBase10Number)
817829
ate, contains_underscore = @try accept_batch_underscore(l, isvalid_oct)
818830
ate && return parse_int(l, contains_underscore)
819831
elseif accept(l, 'b')
832+
parsed_sign && return ParserError(ErrSignInNonBase10Number)
820833
ate, contains_underscore = @try accept_batch_underscore(l, isvalid_binary)
821834
ate && return parse_int(l, contains_underscore)
822835
elseif accept(l, isdigit)

stdlib/TOML/Project.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
name = "TOML"
22
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
3-
version = "1.0.0"
3+
version = "1.0.3"
44

55
[deps]
66
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
77

8+
[compat]
9+
julia = "1.6"
10+
811
[extras]
12+
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
913
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
14+
Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
15+
p7zip_jll = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
1016

1117
[targets]
12-
test = ["Test"]
18+
test = ["Downloads", "p7zip_jll", "Tar", "Test"]

stdlib/TOML/src/print.jl

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ function print_toml_escaped(io::IO, s::AbstractString)
3333
end
3434
end
3535

36+
const MbyFunc = Union{Function, Nothing}
37+
const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString}
38+
39+
40+
########
41+
# Keys #
42+
########
43+
3644
function printkey(io::IO, keys::Vector{String})
3745
for (i, k) in enumerate(keys)
3846
i != 1 && Base.print(io, ".")
@@ -50,48 +58,74 @@ function printkey(io::IO, keys::Vector{String})
5058
end
5159
end
5260

53-
const MbyFunc = Union{Function, Nothing}
54-
const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString}
55-
function printvalue(f::MbyFunc, io::IO, value::AbstractVector; sorted=false, by=identity)
61+
function to_toml_value(f::MbyFunc, value)
62+
if f === nothing
63+
error("type `$(typeof(value))` is not a valid TOML type, pass a conversion function to `TOML.print`")
64+
end
65+
toml_value = f(value)
66+
if !(toml_value isa TOMLValue)
67+
error("TOML syntax function for type `$(typeof(value))` did not return a valid TOML type but a `$(typeof(toml_value))`")
68+
end
69+
return toml_value
70+
end
71+
72+
##########
73+
# Values #
74+
##########
75+
76+
# Fallback
77+
function printvalue(f::MbyFunc, io::IO, value)
78+
toml_value = to_toml_value(f, value)
79+
@invokelatest printvalue(f, io, toml_value)
80+
end
81+
82+
function printvalue(f::MbyFunc, io::IO, value::AbstractVector)
5683
Base.print(io, "[")
5784
for (i, x) in enumerate(value)
5885
i != 1 && Base.print(io, ", ")
59-
if isa(x, AbstractDict)
60-
_print(f, io, x; sorted, by)
61-
else
62-
printvalue(f, io, x; sorted, by)
63-
end
86+
printvalue(f, io, x)
6487
end
6588
Base.print(io, "]")
6689
end
67-
printvalue(f::MbyFunc, io::IO, value::AbstractDict; sorted=false, by=identity) =
68-
_print(f, io, value; sorted, by)
69-
printvalue(f::MbyFunc, io::IO, value::Dates.DateTime; _...) =
70-
Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z"))
71-
printvalue(f::MbyFunc, io::IO, value::Dates.Time; _...) =
72-
Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss"))
73-
printvalue(f::MbyFunc, io::IO, value::Dates.Date; _...) =
74-
Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd"))
75-
printvalue(f::MbyFunc, io::IO, value::Bool; _...) =
76-
Base.print(io, value ? "true" : "false")
77-
printvalue(f::MbyFunc, io::IO, value::Integer; _...) =
78-
Base.print(io, Int64(value)) # TOML specifies 64-bit signed long range for integer
79-
printvalue(f::MbyFunc, io::IO, value::AbstractFloat; _...) =
80-
Base.print(io, isnan(value) ? "nan" :
81-
!(isfinite(value)::Bool) ? string(value > 0 ? "+" : "-", "inf") :
82-
Float64(value)) # TOML specifies IEEE 754 binary64 for float
83-
function printvalue(f::MbyFunc, io::IO, value::AbstractString; _...)
84-
Base.print(io, "\"")
85-
print_toml_escaped(io, value)
86-
Base.print(io, "\"")
90+
91+
function printvalue(f::MbyFunc, io::IO, value::TOMLValue)
92+
value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) :
93+
value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) :
94+
value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) :
95+
value isa Bool ? Base.print(io, value ? "true" : "false") :
96+
value isa Integer ? Base.print(io, Int64(value)) : # TOML specifies 64-bit signed long range for integer
97+
value isa AbstractFloat ? Base.print(io, isnan(value) ? "nan" :
98+
isinf(value) ? string(value > 0 ? "+" : "-", "inf") :
99+
Float64(value)) : # TOML specifies IEEE 754 binary64 for float
100+
value isa AbstractString ? (Base.print(io, "\"");
101+
print_toml_escaped(io, value);
102+
Base.print(io, "\"")) :
103+
value isa AbstractDict ? print_inline_table(f, io, value) :
104+
error("internal error in TOML printing, unhandled value")
105+
end
106+
107+
function print_inline_table(f::MbyFunc, io::IO, value::AbstractDict)
108+
Base.print(io, "{")
109+
for (i, (k,v)) in enumerate(value)
110+
i != 1 && Base.print(io, ", ")
111+
printkey(io, [String(k)])
112+
Base.print(io, " = ")
113+
printvalue(f, io, v)
114+
end
115+
Base.print(io, "}")
87116
end
88117

118+
119+
##########
120+
# Tables #
121+
##########
122+
89123
is_table(value) = isa(value, AbstractDict)
90124
is_array_of_tables(value) = isa(value, AbstractArray) &&
91125
length(value) > 0 && isa(value[1], AbstractDict)
92126
is_tabular(value) = is_table(value) || is_array_of_tables(value)
93127

94-
function _print(f::MbyFunc, io::IO, a::AbstractDict,
128+
function print_table(f::MbyFunc, io::IO, a::AbstractDict,
95129
ks::Vector{String} = String[];
96130
indent::Int = 0,
97131
first_block::Bool = true,
@@ -100,37 +134,30 @@ function _print(f::MbyFunc, io::IO, a::AbstractDict,
100134
)
101135
akeys = keys(a)
102136
if sorted
103-
akeys = sort!(collect(akeys); by)
137+
akeys = sort!(collect(akeys); by=by)
104138
end
105139

106140
# First print non-tabular entries
107141
for key in akeys
108142
value = a[key]
109-
is_tabular(value) && continue
110143
if !isa(value, TOMLValue)
111-
if f === nothing
112-
error("type `$(typeof(value))` is not a valid TOML type, pass a conversion function to `TOML.print`")
113-
end
114-
toml_value = f(value)
115-
if !(toml_value isa TOMLValue)
116-
error("TOML syntax function for type `$(typeof(value))` did not return a valid TOML type but a `$(typeof(toml_value))`")
117-
end
118-
value = toml_value
119-
end
120-
if is_tabular(value)
121-
_print(f, io, Dict(key => value); indent, first_block, sorted, by)
122-
else
123-
Base.print(io, ' '^4max(0,indent-1))
124-
printkey(io, [String(key)])
125-
Base.print(io, " = ") # print separator
126-
printvalue(f, io, value; sorted, by)
127-
Base.print(io, "\n") # new line?
144+
value = to_toml_value(f, value)
128145
end
146+
is_tabular(value) && continue
147+
148+
Base.print(io, ' '^4max(0,indent-1))
149+
printkey(io, [String(key)])
150+
Base.print(io, " = ") # print separator
151+
printvalue(f, io, value)
152+
Base.print(io, "\n") # new line?
129153
first_block = false
130154
end
131155

132156
for key in akeys
133157
value = a[key]
158+
if !isa(value, TOMLValue)
159+
value = to_toml_value(f, value)
160+
end
134161
if is_table(value)
135162
push!(ks, String(key))
136163
header = isempty(value) || !all(is_tabular(v) for v in values(value))::Bool
@@ -144,7 +171,7 @@ function _print(f::MbyFunc, io::IO, a::AbstractDict,
144171
Base.print(io,"]\n")
145172
end
146173
# Use runtime dispatch here since the type of value seems not to be enforced other than as AbstractDict
147-
@invokelatest _print(f, io, value, ks; indent = indent + header, first_block = header, sorted, by)
174+
@invokelatest print_table(f, io, value, ks; indent = indent + header, first_block = header, sorted=sorted, by=by)
148175
pop!(ks)
149176
elseif is_array_of_tables(value)
150177
# print array of tables
@@ -158,14 +185,19 @@ function _print(f::MbyFunc, io::IO, a::AbstractDict,
158185
Base.print(io,"]]\n")
159186
# TODO, nicer error here
160187
!isa(v, AbstractDict) && error("array should contain only tables")
161-
@invokelatest _print(f, io, v, ks; indent = indent + 1, sorted, by)
188+
@invokelatest print_table(f, io, v, ks; indent = indent + 1, sorted=sorted, by=by)
162189
end
163190
pop!(ks)
164191
end
165192
end
166193
end
167194

168-
print(f::MbyFunc, io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = _print(f, io, a; sorted, by)
169-
print(f::MbyFunc, a::AbstractDict; sorted::Bool=false, by=identity) = print(f, stdout, a; sorted, by)
170-
print(io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = _print(nothing, io, a; sorted, by)
171-
print(a::AbstractDict; sorted::Bool=false, by=identity) = print(nothing, stdout, a; sorted, by)
195+
196+
#######
197+
# API #
198+
#######
199+
200+
print(f::MbyFunc, io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(f, io, a; sorted=sorted, by=by)
201+
print(f::MbyFunc, a::AbstractDict; sorted::Bool=false, by=identity) = print(f, stdout, a; sorted=sorted, by=by)
202+
print(io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(nothing, io, a; sorted=sorted, by=by)
203+
print(a::AbstractDict; sorted::Bool=false, by=identity) = print(nothing, stdout, a; sorted=sorted, by=by)

stdlib/TOML/test/print.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,43 @@ end
7171
d = Dict("str" => string(Char(0xd800)))
7272
@test_throws ErrorException TOML.print(devnull, d)
7373
end
74+
75+
str = """
76+
[[dataset.loader]]
77+
driver = "nested"
78+
loaders = ["gzip", { driver = "csv", args = {delim = "\t"}}]
79+
"""
80+
@test roundtrip(str)
81+
82+
83+
struct Foo
84+
a::Int64
85+
b::Float64
86+
end
87+
88+
struct Bar
89+
c::Float64
90+
d::String
91+
end
92+
93+
94+
f = Foo(2,9.9)
95+
b = Bar(1.345, "hello")
96+
97+
dd = Dict("hello"=>"world", "f"=>f, "b"=>b)
98+
99+
to_dict(foo::Foo) = Dict("a"=>foo.a, "b"=>foo.b)
100+
to_dict(bar::Bar) = Dict("c"=>bar.c, "d"=>bar.d)
101+
102+
@test toml_str(to_dict, dd) ==
103+
"""
104+
hello = "world"
105+
106+
[f]
107+
b = 9.9
108+
a = 2
109+
110+
[b]
111+
c = 1.345
112+
d = "hello"
113+
"""

stdlib/TOML/test/readme.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ contributors = [
613613
{ name = \"Baz Qux\", email = \"[email protected]\", url = \"https://example.com/bazqux\" }
614614
]
615615
"""
616-
@test_broken roundtrip(str) # Printer doesn't handle inline tables in arrays?
616+
@test roundtrip(str)
617617
d = parse(str)
618618
@test d["integers"] == [1,2,3]
619619
@test d["colors"] == ["red", "yellow", "green"]

stdlib/TOML/test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ function roundtrip(data)
1616
end
1717

1818
include("readme.jl")
19+
include("utils/utils.jl")
1920
include("toml_test.jl")
2021
include("values.jl")
2122
include("invalids.jl")

stdlib/TOML/test/testfiles/COPYING

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)