perfect_toml 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +4 -0
- data/README.md +3 -3
- data/Rakefile +33 -7
- data/lib/perfect_toml.rb +90 -62
- data/perfect_toml.gemspec +1 -1
- data/sig/perfect_toml.rbs +3 -3
- data/tool/decoder.rb +10 -4
- data/tool/encoder.rb +11 -8
- metadata +4 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a29524e28ddec2096db536394364d7c17049e4e27ad06ddbd0fc26af198092ae
|
|
4
|
+
data.tar.gz: 2efab92700117fdedbc7d77f88246fc4037569f1dfb245bf176dac56bf368b05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ef80224ba391be10d0ba7b096e9d42e113699a08c0c0f2b9cf5bd12a562ca0f8fb8203d5f2ca54e4987805abf245e25fedb8917aa86279f6b52962cdda19a3c
|
|
7
|
+
data.tar.gz: 33182e8c0e71d39934dc63883486d1c5312fb0f9922ec3fada66dec7f5b796d53cdad2f1517b9c62c4ffe8ba2eab8b131a8a9b2584394d8b2922a1de639a02e3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# PerfectTOML Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.1 / 2026-02-26
|
|
4
|
+
|
|
5
|
+
* Add preliminary support for TOML 1.1.0 (`PerfectTOML.parse(str, version: "1.1")`)
|
|
6
|
+
* Improve the performance of datetime parsing
|
|
7
|
+
* Encode empty arrays correctly
|
|
8
|
+
* Prevent frozen-string-literal warnings
|
|
9
|
+
* Drop Ruby 2.7 and 3.0 support
|
|
10
|
+
|
|
3
11
|
## 0.9.0 / 2022-07-17
|
|
4
12
|
|
|
5
13
|
* First release.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Yet another [TOML](https://github.com/toml-lang/toml) parser and generator.
|
|
|
4
4
|
|
|
5
5
|
Features:
|
|
6
6
|
|
|
7
|
-
* Fully compliant with [TOML v1.0.0](https://toml.io/en/v1.0.0). It passes [
|
|
7
|
+
* Fully compliant with [TOML v1.0.0](https://toml.io/en/v1.0.0). It passes [toml-lang/toml-test](https://github.com/toml-lang/toml-test).
|
|
8
8
|
* Faster than existing TOML parsers for Ruby. See [Benchmark](#benchmark).
|
|
9
9
|
* Single-file, plain old Ruby script without any dependencies: [perfect_toml.rb](https://github.com/mame/perfect_toml/blob/master/lib/perfect_toml.rb).
|
|
10
10
|
|
|
@@ -59,7 +59,7 @@ PerfectTOML provides dedicated classes, respectively,
|
|
|
59
59
|
```ruby
|
|
60
60
|
require "perfect_toml"
|
|
61
61
|
|
|
62
|
-
p PerfectTOML.parse("local-date = 1970-01-01)
|
|
62
|
+
p PerfectTOML.parse("local-date = 1970-01-01")
|
|
63
63
|
#=> { "local-date" => #<PerfectTOML::LocalDate 1970-01-01> }
|
|
64
64
|
```
|
|
65
65
|
|
|
@@ -74,7 +74,7 @@ require "toml-rb"
|
|
|
74
74
|
require "tomlrb"
|
|
75
75
|
|
|
76
76
|
# https://raw.githubusercontent.com/toml-lang/toml/v0.5.0/examples/example-v0.4.0.toml
|
|
77
|
-
|
|
77
|
+
data = File.read("example-v0.4.0.toml")
|
|
78
78
|
|
|
79
79
|
Benchmark.ips do |x|
|
|
80
80
|
x.report("emancu/toml-rb") { TomlRB.parse(data) }
|
data/Rakefile
CHANGED
|
@@ -8,32 +8,58 @@ Rake::TestTask.new(:core_test) do |t|
|
|
|
8
8
|
t.test_files = FileList["test/**/*_test.rb"]
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
TOML_TEST = "./toml-test-
|
|
11
|
+
TOML_TEST = "./toml-test-v2.1.0-linux-amd64"
|
|
12
12
|
|
|
13
13
|
file TOML_TEST do
|
|
14
14
|
require "open-uri"
|
|
15
15
|
require "zlib"
|
|
16
|
-
URI.open("https://github.com/
|
|
16
|
+
URI.open("https://github.com/toml-lang/toml-test/releases/download/v2.1.0/toml-test-v2.1.0-linux-amd64.gz", "rb") do |f|
|
|
17
17
|
File.binwrite(TOML_TEST, Zlib::GzipReader.new(f).read)
|
|
18
18
|
File.chmod(0o755, TOML_TEST)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
task :
|
|
23
|
-
|
|
22
|
+
task :toml_decoder_1_0_test => TOML_TEST do
|
|
23
|
+
ENV["TOML_DECODER_VERSION"] = "1.0"
|
|
24
|
+
sh TOML_TEST, "test", "-toml", "1.0", "-decoder", "./tool/decoder.rb"
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
task :
|
|
27
|
+
task :toml_decoder_1_1_test => TOML_TEST do
|
|
28
|
+
ENV["TOML_DECODER_VERSION"] = "1.1"
|
|
29
|
+
sh TOML_TEST, "test", "-toml", "1.1", "-decoder", "./tool/decoder.rb"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
task :toml_encoder_1_0_test => TOML_TEST do
|
|
33
|
+
ENV["TOML_DECODER_VERSION"] = "1.0"
|
|
34
|
+
["0000", "1000", "0010", "0001", "0011"].each do |mode|
|
|
35
|
+
ENV["TOML_ENCODER_USE_DOT"] = mode[0]
|
|
36
|
+
ENV["TOML_ENCODER_SORT_KEYS"] = mode[1]
|
|
37
|
+
ENV["TOML_ENCODER_USE_LITERAL_STRING"] = mode[2]
|
|
38
|
+
ENV["TOML_ENCODER_USE_MULTILINE_STRING"] = mode[3]
|
|
39
|
+
ENV.delete("BURNTSUSHI_TOML_110") # https://github.com/toml-lang/toml-test/issues/173
|
|
40
|
+
sh TOML_TEST, "test", "-toml", "1.0", "-decoder", "./tool/decoder.rb", "--encoder", "./tool/encoder.rb"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
task :toml_encoder_1_1_test => TOML_TEST do
|
|
45
|
+
ENV["TOML_DECODER_VERSION"] = "1.1"
|
|
27
46
|
["0000", "1000", "0010", "0001", "0011"].each do |mode|
|
|
28
47
|
ENV["TOML_ENCODER_USE_DOT"] = mode[0]
|
|
29
48
|
ENV["TOML_ENCODER_SORT_KEYS"] = mode[1]
|
|
30
49
|
ENV["TOML_ENCODER_USE_LITERAL_STRING"] = mode[2]
|
|
31
50
|
ENV["TOML_ENCODER_USE_MULTILINE_STRING"] = mode[3]
|
|
32
|
-
|
|
51
|
+
ENV["BURNTSUSHI_TOML_110"] = "1" # https://github.com/toml-lang/toml-test/issues/173
|
|
52
|
+
sh TOML_TEST, "test", "-toml", "1.1", "-decoder", "./tool/decoder.rb", "--encoder", "./tool/encoder.rb"
|
|
33
53
|
end
|
|
34
54
|
end
|
|
35
55
|
|
|
36
|
-
task :test => [
|
|
56
|
+
task :test => [
|
|
57
|
+
:core_test,
|
|
58
|
+
:toml_decoder_1_0_test,
|
|
59
|
+
:toml_decoder_1_1_test,
|
|
60
|
+
:toml_encoder_1_0_test,
|
|
61
|
+
:toml_encoder_1_1_test,
|
|
62
|
+
]
|
|
37
63
|
|
|
38
64
|
task default: :test
|
|
39
65
|
|
data/lib/perfect_toml.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# MIT License
|
|
2
|
-
#
|
|
2
|
+
#
|
|
3
3
|
# Copyright (c) 2022 Yusuke Endoh
|
|
4
|
-
#
|
|
4
|
+
#
|
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
# of this software and associated documentation files (the "Software"), to deal
|
|
7
7
|
# in the Software without restriction, including without limitation the rights
|
|
8
8
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
9
|
# copies of the Software, and to permit persons to whom the Software is
|
|
10
10
|
# furnished to do so, subject to the following conditions:
|
|
11
|
-
#
|
|
11
|
+
#
|
|
12
12
|
# The above copyright notice and this permission notice shall be included in all
|
|
13
13
|
# copies or substantial portions of the Software.
|
|
14
|
-
#
|
|
14
|
+
#
|
|
15
15
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
16
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
17
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
require "strscan"
|
|
24
24
|
|
|
25
25
|
module PerfectTOML
|
|
26
|
-
VERSION = "0.9.
|
|
26
|
+
VERSION = "0.9.1"
|
|
27
27
|
|
|
28
28
|
class LocalDateTimeBase
|
|
29
29
|
def to_inline_toml
|
|
@@ -62,16 +62,18 @@ module PerfectTOML
|
|
|
62
62
|
#
|
|
63
63
|
# See https://toml.io/en/v1.0.0#local-date-time
|
|
64
64
|
class LocalDateTime < LocalDateTimeBase
|
|
65
|
-
def initialize(
|
|
66
|
-
@
|
|
67
|
-
@
|
|
68
|
-
|
|
69
|
-
@hour = hour.to_i
|
|
70
|
-
@min = min.to_i
|
|
71
|
-
@sec = Numeric === sec ? sec : sec.include?(".") ? Rational(sec) : sec.to_i
|
|
65
|
+
def initialize(*args)
|
|
66
|
+
@date = args[0].is_a?(LocalDate) ? args.shift : LocalDate.new(args.shift, args.shift, args.shift)
|
|
67
|
+
@time = args[0].is_a?(LocalTime) ? args.shift : LocalTime.new(args.shift, args.shift, args.shift)
|
|
68
|
+
raise ArgumentError, "wrong number of arguments" unless args.empty?
|
|
72
69
|
end
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
def year = @date.year
|
|
72
|
+
def month = @date.month
|
|
73
|
+
def day = @date.day
|
|
74
|
+
def hour = @time.hour
|
|
75
|
+
def min = @time.min
|
|
76
|
+
def sec = @time.sec
|
|
75
77
|
|
|
76
78
|
# Converts to a Time object with the local timezone.
|
|
77
79
|
#
|
|
@@ -83,7 +85,7 @@ module PerfectTOML
|
|
|
83
85
|
# ldt.to_time("UTC") #=> 1970-01-01 02:03:04 UTC
|
|
84
86
|
# ldt.to_time("+00:00") #=> 1970-01-01 02:03:04 +0000
|
|
85
87
|
def to_time(zone = nil)
|
|
86
|
-
|
|
88
|
+
Time.new(@date.year, @date.month, @date.day, @time.hour, @time.min, @time.sec, zone)
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
# Returns a string representation in RFC 3339 format
|
|
@@ -91,9 +93,7 @@ module PerfectTOML
|
|
|
91
93
|
# ldt = PerfectTOML::LocalDateTime.new(1970, 1, 1, 2, 3, 4)
|
|
92
94
|
# ldt.to_s #=> 1970-01-01T02:03:04
|
|
93
95
|
def to_s
|
|
94
|
-
|
|
95
|
-
s << ("%11.9f" % (@sec - @sec.floor))[1..] unless Integer === @sec
|
|
96
|
-
s
|
|
96
|
+
"#{ @date }T#{ @time }"
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
|
@@ -101,10 +101,15 @@ module PerfectTOML
|
|
|
101
101
|
#
|
|
102
102
|
# See https://toml.io/en/v1.0.0#local-date
|
|
103
103
|
class LocalDate < LocalDateTimeBase
|
|
104
|
+
DAYS_IN_MONTH = [31, nil, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
105
|
+
|
|
104
106
|
def initialize(year, month, day)
|
|
105
107
|
@year = year.to_i
|
|
106
108
|
@month = month.to_i
|
|
109
|
+
raise ArgumentError, "month must be in 1..12" unless 1 <= @month && @month <= 12
|
|
107
110
|
@day = day.to_i
|
|
111
|
+
max_day = DAYS_IN_MONTH[@month - 1] || (@year % 4 == 0 && (@year % 100 != 0 || @year % 400 == 0) ? 29 : 28)
|
|
112
|
+
raise ArgumentError, "day must be in 1..#{ max_day }" unless 1 <= @day && @day <= max_day
|
|
108
113
|
end
|
|
109
114
|
|
|
110
115
|
attr_reader :year, :month, :day
|
|
@@ -132,15 +137,17 @@ module PerfectTOML
|
|
|
132
137
|
end
|
|
133
138
|
end
|
|
134
139
|
|
|
135
|
-
|
|
136
140
|
# Represents TOML's Local Time
|
|
137
141
|
#
|
|
138
142
|
# See https://toml.io/en/v1.0.0#local-time
|
|
139
143
|
class LocalTime < LocalDateTimeBase
|
|
140
144
|
def initialize(hour, min, sec)
|
|
141
145
|
@hour = hour.to_i
|
|
146
|
+
raise ArgumentError, "hour must be in 0..23" unless 0 <= @hour && @hour <= 23
|
|
142
147
|
@min = min.to_i
|
|
148
|
+
raise ArgumentError, "min must be in 0..59" unless 0 <= @min && @min <= 59
|
|
143
149
|
@sec = Numeric === sec ? sec : sec.include?(".") ? Rational(sec) : sec.to_i
|
|
150
|
+
raise ArgumentError, "sec must be in 0..60" unless 0 <= @sec && @sec <= 60
|
|
144
151
|
end
|
|
145
152
|
|
|
146
153
|
attr_reader :hour, :min, :sec
|
|
@@ -172,7 +179,7 @@ module PerfectTOML
|
|
|
172
179
|
|
|
173
180
|
# call-seq:
|
|
174
181
|
#
|
|
175
|
-
# parse(toml_src, symbolize_names: boolean) -> Object
|
|
182
|
+
# parse(toml_src, version: String, symbolize_names: boolean) -> Object
|
|
176
183
|
#
|
|
177
184
|
# Decodes a TOML string.
|
|
178
185
|
#
|
|
@@ -184,6 +191,10 @@ module PerfectTOML
|
|
|
184
191
|
# END
|
|
185
192
|
# PerfectTOML.parse(src) #=> { "foo" => { "bar" => "baz" } }
|
|
186
193
|
#
|
|
194
|
+
# The keyword `version` specifies the TOML version to parse.
|
|
195
|
+
# Supported versions are "1.0.0" and "1.1.0" (which isn't released yet and may change).
|
|
196
|
+
# Defaults to "1.0.0", but the default will change in future releases.
|
|
197
|
+
#
|
|
187
198
|
# All keys in the Hash are String by default.
|
|
188
199
|
# If a keyword `symbolize_names` is specficied as truthy,
|
|
189
200
|
# all keys in the Hash will be Symbols.
|
|
@@ -195,8 +206,14 @@ module PerfectTOML
|
|
|
195
206
|
# bar = "baz"
|
|
196
207
|
# END
|
|
197
208
|
# PerfectTOML.parse(src, symbolize_names: true) #=> { :key => { :bar => "baz" } }
|
|
198
|
-
def self.parse(toml_src,
|
|
199
|
-
|
|
209
|
+
def self.parse(toml_src, version: "1.0.0", symbolize_names: false)
|
|
210
|
+
case version
|
|
211
|
+
when "1.0", "1.0.0" then version = :TOML_1_0_0
|
|
212
|
+
when "1.1", "1.1.0" then version = :TOML_1_1_0
|
|
213
|
+
else
|
|
214
|
+
raise ArgumentError, "unsupported TOML version: #{ version.inspect }"
|
|
215
|
+
end
|
|
216
|
+
Parser.new(toml_src, version, symbolize_names).parse
|
|
200
217
|
end
|
|
201
218
|
|
|
202
219
|
# call-seq:
|
|
@@ -278,7 +295,7 @@ module PerfectTOML
|
|
|
278
295
|
# # output:
|
|
279
296
|
# # a.b.c.d = 42
|
|
280
297
|
def self.generate(hash, **opts)
|
|
281
|
-
out = ""
|
|
298
|
+
out = +""
|
|
282
299
|
Generator.new(hash, out, **opts).generate
|
|
283
300
|
out
|
|
284
301
|
end
|
|
@@ -304,10 +321,22 @@ module PerfectTOML
|
|
|
304
321
|
class ParseError < StandardError; end
|
|
305
322
|
|
|
306
323
|
class Parser # :nodoc:
|
|
307
|
-
def initialize(src, symbolize_names
|
|
324
|
+
def initialize(src, version, symbolize_names)
|
|
308
325
|
@s = StringScanner.new(src)
|
|
309
326
|
@symbolize_names = symbolize_names
|
|
310
327
|
@root_node = @topic_node = Node.new(1, nil)
|
|
328
|
+
|
|
329
|
+
@re_escape_char = version == :TOML_1_0_0 ?
|
|
330
|
+
/([btnmfr"\\])|u([0-9A-Fa-f]{4})|U([0-9A-Fa-f]{8})/ :
|
|
331
|
+
/([btnmfre"\\])|x([0-9A-Fa-f]{2})|u([0-9A-Fa-f]{4})|U([0-9A-Fa-f]{8})/
|
|
332
|
+
@re_multiline_basic_string = version == :TOML_1_0_0 ?
|
|
333
|
+
/[^\x00-\x08\x0b\x0c\x0e-\x1f\x7f"\\]*/ :
|
|
334
|
+
/([^\x00-\x08\x0b-\x1f\x7f"\\]|\r\n)*/
|
|
335
|
+
@re_multiline_literal_string = version == :TOML_1_0_0 ?
|
|
336
|
+
/[^\x00-\x08\x0b\x0c\x0e-\x1f\x7f']*/ :
|
|
337
|
+
/([^\x00-\x08\x0b-\x1f\x7f']|\r\n)*/
|
|
338
|
+
@omitted_seconds = version != :TOML_1_0_0 ? "0" : nil
|
|
339
|
+
@allow_newline_in_inline_table = version != :TOML_1_0_0
|
|
311
340
|
end
|
|
312
341
|
|
|
313
342
|
def parse
|
|
@@ -377,13 +406,13 @@ module PerfectTOML
|
|
|
377
406
|
# parsing for strings
|
|
378
407
|
|
|
379
408
|
ESCAPE_CHARS = {
|
|
380
|
-
?b => ?\b, ?t => ?\t, ?n => ?\n, ?f => ?\f, ?r => ?\r, ?" => ?", ?\\ => ?\\
|
|
409
|
+
?b => ?\b, ?t => ?\t, ?n => ?\n, ?f => ?\f, ?r => ?\r, ?e => ?\e, ?" => ?", ?\\ => ?\\
|
|
381
410
|
}
|
|
382
411
|
|
|
383
412
|
def parse_escape_char
|
|
384
413
|
if @s.skip(/\\/)
|
|
385
|
-
if @s.skip(
|
|
386
|
-
@s[1] ? ESCAPE_CHARS[@s[1]] : (@s[2] || @s[3]).hex.chr("UTF-8")
|
|
414
|
+
if @s.skip(@re_escape_char)
|
|
415
|
+
@s[1] ? ESCAPE_CHARS[@s[1]] : (@s[2] || @s[3] || @s[4]).hex.chr("UTF-8")
|
|
387
416
|
else
|
|
388
417
|
unterminated_string_error if @s.eos?
|
|
389
418
|
error "invalid escape character in string: #{ @s.peek(1).dump }"
|
|
@@ -395,7 +424,7 @@ module PerfectTOML
|
|
|
395
424
|
end
|
|
396
425
|
|
|
397
426
|
def parse_basic_string
|
|
398
|
-
str = ""
|
|
427
|
+
str = +""
|
|
399
428
|
while true
|
|
400
429
|
str << @s.scan(/[^\x00-\x08\x0a-\x1f\x7f"\\]*/)
|
|
401
430
|
return str if @s.skip(/"/)
|
|
@@ -407,9 +436,9 @@ module PerfectTOML
|
|
|
407
436
|
# skip a newline
|
|
408
437
|
@s.skip(/\n|\r\n/)
|
|
409
438
|
|
|
410
|
-
str = ""
|
|
439
|
+
str = +""
|
|
411
440
|
while true
|
|
412
|
-
str << @s.scan(
|
|
441
|
+
str << @s.scan(@re_multiline_basic_string)
|
|
413
442
|
delimiter = @s.skip(/"{1,5}/)
|
|
414
443
|
if delimiter
|
|
415
444
|
str << "\"" * (delimiter % 3)
|
|
@@ -432,9 +461,9 @@ module PerfectTOML
|
|
|
432
461
|
# skip a newline
|
|
433
462
|
@s.skip(/\n|\r\n/)
|
|
434
463
|
|
|
435
|
-
str = ""
|
|
464
|
+
str = +""
|
|
436
465
|
while true
|
|
437
|
-
str << @s.scan(
|
|
466
|
+
str << @s.scan(@re_multiline_literal_string)
|
|
438
467
|
if delimiter = @s.skip(/'{1,5}/)
|
|
439
468
|
str << "'" * (delimiter % 3)
|
|
440
469
|
return str if delimiter >= 3
|
|
@@ -448,33 +477,23 @@ module PerfectTOML
|
|
|
448
477
|
# parsing for date/time
|
|
449
478
|
|
|
450
479
|
def parse_datetime(preread_len)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
zone = @s.scan(/(Z)|[-+]\d{2}:\d{2}/i)
|
|
459
|
-
time = Time.new(year, month, day, hour, min, sec.to_r, @s[1] ? "UTC" : zone)
|
|
460
|
-
if zone
|
|
461
|
-
time
|
|
462
|
-
else
|
|
463
|
-
LocalDateTime.new(year, month, day, hour, min, sec)
|
|
464
|
-
end
|
|
480
|
+
date = LocalDate.new(@s[1], @s[2], @s[3])
|
|
481
|
+
if @s[4]
|
|
482
|
+
time = LocalTime.new(@s[4], @s[5], @s[6] || @omitted_seconds || error("seconds field is required"))
|
|
483
|
+
datetime = LocalDateTime.new(date, time)
|
|
484
|
+
zone = @s[7]
|
|
485
|
+
datetime = datetime.to_time(zone == "z" ? "Z" : zone) if zone
|
|
486
|
+
datetime
|
|
465
487
|
else
|
|
466
|
-
|
|
467
|
-
LocalDate.new(year, month, day)
|
|
488
|
+
date
|
|
468
489
|
end
|
|
469
490
|
rescue ArgumentError
|
|
470
|
-
@s.pos
|
|
471
|
-
error "failed to parse date or datetime \"#{
|
|
491
|
+
@s.pos -= preread_len
|
|
492
|
+
error "failed to parse date or datetime \"#{ @s[0] }\""
|
|
472
493
|
end
|
|
473
494
|
|
|
474
495
|
def parse_time(preread_len)
|
|
475
|
-
|
|
476
|
-
Time.new(1970, 1, 1, hour, min, sec.to_r, "Z") # parse check
|
|
477
|
-
LocalTime.new(hour, min, sec)
|
|
496
|
+
LocalTime.new(@s[1], @s[2], @s[3] || @omitted_seconds || error("seconds field is required"))
|
|
478
497
|
rescue ArgumentError
|
|
479
498
|
@s.pos -= preread_len
|
|
480
499
|
error "failed to parse time \"#{ @s[0] }\""
|
|
@@ -496,8 +515,16 @@ module PerfectTOML
|
|
|
496
515
|
ary
|
|
497
516
|
end
|
|
498
517
|
|
|
518
|
+
def skip_inline_table_spaces
|
|
519
|
+
if @allow_newline_in_inline_table
|
|
520
|
+
skip_spaces
|
|
521
|
+
else
|
|
522
|
+
@s.skip(/[\t ]*/)
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
|
|
499
526
|
def parse_inline_table
|
|
500
|
-
|
|
527
|
+
skip_inline_table_spaces
|
|
501
528
|
if @s.skip(/\}/)
|
|
502
529
|
{}
|
|
503
530
|
else
|
|
@@ -505,13 +532,14 @@ module PerfectTOML
|
|
|
505
532
|
while true
|
|
506
533
|
@keys_start_pos = @s.pos
|
|
507
534
|
keys = parse_keys
|
|
508
|
-
|
|
509
|
-
unexpected_error unless @s.skip(
|
|
535
|
+
skip_inline_table_spaces
|
|
536
|
+
unexpected_error unless @s.skip(/=/)
|
|
537
|
+
skip_inline_table_spaces
|
|
510
538
|
define_value(tmp_node, keys)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
break if @s.skip(/\}/)
|
|
514
|
-
unexpected_error
|
|
539
|
+
skip_inline_table_spaces
|
|
540
|
+
skip_inline_table_spaces if comma_seen = @s.skip(/,/)
|
|
541
|
+
break if (@allow_newline_in_inline_table || !comma_seen) && @s.skip(/\}/)
|
|
542
|
+
unexpected_error unless comma_seen
|
|
515
543
|
end
|
|
516
544
|
tmp_node.table
|
|
517
545
|
end
|
|
@@ -547,9 +575,9 @@ module PerfectTOML
|
|
|
547
575
|
@s.skip(/""/) ? parse_multiline_basic_string : parse_basic_string
|
|
548
576
|
when @s.skip(/'/)
|
|
549
577
|
@s.skip(/''/) ? parse_multiline_literal_string : parse_literal_string
|
|
550
|
-
when len = @s.skip(/(-?\d{4})-(\d{2})-(\d{2})
|
|
578
|
+
when len = @s.skip(/(-?\d{4})-(\d{2})-(\d{2})(?:[tT ](\d{2}):(\d{2})(?::(\d{2}(?:\.\d+)?))?([zZ]|[-+]\d{2}:\d{2})?)?/)
|
|
551
579
|
parse_datetime(len)
|
|
552
|
-
when len = @s.skip(/(\d{2}):(\d{2})
|
|
580
|
+
when len = @s.skip(/(\d{2}):(\d{2})(?::(\d{2}(?:\.\d+)?))?/)
|
|
553
581
|
parse_time(len)
|
|
554
582
|
when val = @s.scan(/0x\h(?:_?\h)*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*/)
|
|
555
583
|
Integer(val)
|
|
@@ -846,7 +874,7 @@ module PerfectTOML
|
|
|
846
874
|
break unless v
|
|
847
875
|
v
|
|
848
876
|
end
|
|
849
|
-
if ary
|
|
877
|
+
if ary && !ary.empty?
|
|
850
878
|
children << [:array, k, ary]
|
|
851
879
|
else
|
|
852
880
|
values << [k, val]
|
data/perfect_toml.gemspec
CHANGED
|
@@ -15,7 +15,7 @@ It is fully compliant with TOML v1.0.0, and faster than existing TOML parsers fo
|
|
|
15
15
|
END
|
|
16
16
|
spec.homepage = "https://github.com/mame/perfect_toml"
|
|
17
17
|
spec.license = "MIT"
|
|
18
|
-
spec.required_ruby_version = ">=
|
|
18
|
+
spec.required_ruby_version = ">= 3.1.0"
|
|
19
19
|
|
|
20
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
21
21
|
spec.metadata["source_code_uri"] = "https://github.com/mame/perfect_toml"
|
data/sig/perfect_toml.rbs
CHANGED
|
@@ -4,9 +4,9 @@ module PerfectTOML
|
|
|
4
4
|
def self.parse: (String toml_src, symbolize_names: bool) -> untyped
|
|
5
5
|
def self.load_file: (String filename, symbolize_names: bool) -> untyped
|
|
6
6
|
| (IO io, symbolize_names: bool) -> untyped
|
|
7
|
-
def self.generate: (
|
|
8
|
-
def self.save_file: (String filename, untyped data, sort_keys: boolean, use_literal_string: boolean, use_multiline_string: boolean, use_dot: boolean) ->
|
|
9
|
-
| (IO io, untyped data, sort_keys: boolean, use_literal_string: boolean, use_multiline_string: boolean, use_dot: boolean) ->
|
|
7
|
+
def self.generate: (untyped data, sort_keys: boolean, use_literal_string: boolean, use_multiline_string: boolean, use_dot: boolean) -> String
|
|
8
|
+
def self.save_file: (String filename, untyped data, sort_keys: boolean, use_literal_string: boolean, use_multiline_string: boolean, use_dot: boolean) -> void
|
|
9
|
+
| (IO io, untyped data, sort_keys: boolean, use_literal_string: boolean, use_multiline_string: boolean, use_dot: boolean) -> void
|
|
10
10
|
|
|
11
11
|
class LocalDateTime
|
|
12
12
|
def initialize: (untyped year, untyped month, untyped day, untyped hour, untyped min, untyped sec) -> void
|
data/tool/decoder.rb
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
# usage: TOML_DECODER_VERSION=1.0 /path/to/toml-test-v2.1.0-linux-amd64 test -toml 1.0 -decoder tool/decoder.rb
|
|
4
|
+
# usage: TOML_DECODER_VERSION=1.1 /path/to/toml-test-v2.1.0-linux-amd64 test -toml 1.1 -decoder tool/decoder.rb
|
|
5
|
+
|
|
3
6
|
require_relative "../lib/perfect_toml"
|
|
4
7
|
require "json"
|
|
5
8
|
|
|
6
|
-
def
|
|
9
|
+
def toml_to_json(toml)
|
|
7
10
|
case toml
|
|
8
|
-
when Hash then toml.to_h {|k, v| [k,
|
|
9
|
-
when Array then toml.map {|v|
|
|
11
|
+
when Hash then toml.to_h {|k, v| [k, toml_to_json(v)] }
|
|
12
|
+
when Array then toml.map {|v| toml_to_json(v) }
|
|
10
13
|
when String then { "type" => "string", "value" => toml }
|
|
11
14
|
when Integer then { "type" => "integer", "value" => toml.to_s }
|
|
12
15
|
when Float
|
|
@@ -32,4 +35,7 @@ def convert(toml)
|
|
|
32
35
|
end
|
|
33
36
|
end
|
|
34
37
|
|
|
35
|
-
puts JSON.generate(
|
|
38
|
+
puts JSON.generate(toml_to_json(PerfectTOML.parse(
|
|
39
|
+
$stdin.read.force_encoding("UTF-8"),
|
|
40
|
+
version: ENV.fetch("TOML_DECODER_VERSION", "1.0.0")
|
|
41
|
+
)))
|
data/tool/encoder.rb
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
# usage: TOML_DECODER_VERSION=1.0 /path/to/toml-test-v2.1.0-linux-amd64 test -toml 1.0 -decoder tool/decoder.rb -encoder tool/encoder.rb
|
|
4
|
+
# usage: TOML_DECODER_VERSION=1.1 /path/to/toml-test-v2.1.0-linux-amd64 test -toml 1.1 -decoder tool/decoder.rb -encoder tool/encoder.rb
|
|
5
|
+
|
|
3
6
|
require_relative "../lib/perfect_toml"
|
|
4
7
|
require "json"
|
|
5
8
|
require "time"
|
|
6
9
|
|
|
7
|
-
def
|
|
8
|
-
return json.map {|v|
|
|
10
|
+
def json_to_toml(json)
|
|
11
|
+
return json.map {|v| json_to_toml(v) } if Array === json
|
|
9
12
|
if json.key?("type")
|
|
10
13
|
type, val = json["type"], json["value"]
|
|
11
14
|
case type
|
|
12
15
|
when "integer" then val.to_i
|
|
13
16
|
when "float"
|
|
14
|
-
case val
|
|
17
|
+
case val.downcase
|
|
15
18
|
when "inf", "+inf" then Float::INFINITY
|
|
16
19
|
when "-inf" then -Float::INFINITY
|
|
17
20
|
when "nan" then -Float::NAN
|
|
@@ -31,17 +34,17 @@ def convert(json)
|
|
|
31
34
|
raise if val !~ /\A(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)\z/
|
|
32
35
|
PerfectTOML::LocalTime.new($1, $2, $3)
|
|
33
36
|
else
|
|
34
|
-
|
|
37
|
+
json.to_h {|k, v| [k, json_to_toml(v)] }
|
|
35
38
|
end
|
|
36
39
|
else
|
|
37
|
-
json.to_h {|k, v| [k,
|
|
40
|
+
json.to_h {|k, v| [k, json_to_toml(v)] }
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
puts PerfectTOML.generate(
|
|
45
|
+
json_to_toml(JSON.parse($stdin.read.force_encoding("UTF-8"))),
|
|
42
46
|
use_dot: ENV["TOML_ENCODER_USE_DOT"] == "1",
|
|
43
47
|
sort_keys: ENV["TOML_ENCODER_SORT_KEYS"] == "1",
|
|
44
48
|
use_literal_string: ENV["TOML_ENCODER_USE_LITERAL_STRING"] == "1",
|
|
45
49
|
use_multiline_string: ENV["TOML_ENCODER_USE_MULTILINE_STRING"] == "1",
|
|
46
|
-
|
|
47
|
-
puts PerfectTOML.generate(convert(JSON.parse($stdin.read)), **opts)
|
|
50
|
+
)
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: perfect_toml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Endoh
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: |
|
|
14
13
|
PerfectTOML is yet another TOML parser.
|
|
@@ -37,7 +36,6 @@ metadata:
|
|
|
37
36
|
homepage_uri: https://github.com/mame/perfect_toml
|
|
38
37
|
source_code_uri: https://github.com/mame/perfect_toml
|
|
39
38
|
changelog_uri: https://github.com/mame/perfect_toml/blob/main/CHANGELOG.md
|
|
40
|
-
post_install_message:
|
|
41
39
|
rdoc_options: []
|
|
42
40
|
require_paths:
|
|
43
41
|
- lib
|
|
@@ -45,15 +43,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
45
43
|
requirements:
|
|
46
44
|
- - ">="
|
|
47
45
|
- !ruby/object:Gem::Version
|
|
48
|
-
version:
|
|
46
|
+
version: 3.1.0
|
|
49
47
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
48
|
requirements:
|
|
51
49
|
- - ">="
|
|
52
50
|
- !ruby/object:Gem::Version
|
|
53
51
|
version: '0'
|
|
54
52
|
requirements: []
|
|
55
|
-
rubygems_version:
|
|
56
|
-
signing_key:
|
|
53
|
+
rubygems_version: 4.1.0.dev
|
|
57
54
|
specification_version: 4
|
|
58
55
|
summary: A fast TOML parser gem fully compliant with TOML v1.0.0
|
|
59
56
|
test_files: []
|