tomlrb 1.3.0 → 2.0.4
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/lib/tomlrb/generated_parser.rb +512 -274
- data/lib/tomlrb/handler.rb +211 -5
- data/lib/tomlrb/local_date.rb +36 -0
- data/lib/tomlrb/local_date_time.rb +44 -0
- data/lib/tomlrb/local_time.rb +41 -0
- data/lib/tomlrb/parser.rb +3 -1
- data/lib/tomlrb/parser.y +169 -45
- data/lib/tomlrb/scanner.rb +63 -29
- data/lib/tomlrb/string_utils.rb +9 -2
- data/lib/tomlrb/version.rb +3 -1
- data/lib/tomlrb.rb +10 -5
- metadata +21 -7
data/lib/tomlrb/handler.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
1
3
|
module Tomlrb
|
|
2
4
|
class Handler
|
|
3
5
|
attr_reader :output, :symbolize_keys
|
|
@@ -7,17 +9,24 @@ module Tomlrb
|
|
|
7
9
|
@current = @output
|
|
8
10
|
@stack = []
|
|
9
11
|
@array_names = []
|
|
12
|
+
@current_table = []
|
|
13
|
+
@keys = Keys.new
|
|
10
14
|
@symbolize_keys = options[:symbolize_keys]
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def set_context(identifiers, is_array_of_tables: false)
|
|
18
|
+
if identifiers.empty?
|
|
19
|
+
raise ParseError, 'Array needs a name'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@current_table = identifiers.dup
|
|
23
|
+
@keys.add_table_key identifiers, is_array_of_tables
|
|
14
24
|
@current = @output
|
|
15
25
|
|
|
16
26
|
deal_with_array_of_tables(identifiers, is_array_of_tables) do |identifierz|
|
|
17
27
|
identifierz.each do |k|
|
|
18
28
|
k = k.to_sym if @symbolize_keys
|
|
19
29
|
if @current[k].is_a?(Array)
|
|
20
|
-
@current[k] << {} if @current[k].empty?
|
|
21
30
|
@current = @current[k].last
|
|
22
31
|
else
|
|
23
32
|
@current[k] ||= {}
|
|
@@ -28,7 +37,6 @@ module Tomlrb
|
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
def deal_with_array_of_tables(identifiers, is_array_of_tables)
|
|
31
|
-
identifiers.map!{|n| n.gsub("\"", '')}
|
|
32
40
|
stringified_identifier = identifiers.join('.')
|
|
33
41
|
|
|
34
42
|
if is_array_of_tables
|
|
@@ -43,20 +51,56 @@ module Tomlrb
|
|
|
43
51
|
if is_array_of_tables
|
|
44
52
|
last_identifier = last_identifier.to_sym if @symbolize_keys
|
|
45
53
|
@current[last_identifier] ||= []
|
|
54
|
+
raise ParseError, "Cannot use key #{last_identifier} for both table and array at once" unless @current[last_identifier].respond_to?(:<<)
|
|
46
55
|
@current[last_identifier] << {}
|
|
47
56
|
@current = @current[last_identifier].last
|
|
48
57
|
end
|
|
49
58
|
end
|
|
50
59
|
|
|
51
60
|
def assign(k)
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
@keys.add_pair_key k, @current_table
|
|
62
|
+
current = @current
|
|
63
|
+
while (key = k.shift)
|
|
64
|
+
key = key.to_sym if @symbolize_keys
|
|
65
|
+
current = assign_key_path(current, key, k.empty?)
|
|
66
|
+
end
|
|
54
67
|
end
|
|
55
68
|
|
|
56
69
|
def push(o)
|
|
57
70
|
@stack << o
|
|
58
71
|
end
|
|
59
72
|
|
|
73
|
+
def push_inline(inline_arrays)
|
|
74
|
+
merged_inline = {}
|
|
75
|
+
keys = Keys.new
|
|
76
|
+
|
|
77
|
+
inline_arrays.each do |inline_array|
|
|
78
|
+
current = merged_inline
|
|
79
|
+
value = inline_array.pop
|
|
80
|
+
keys.add_table_key inline_array, value.is_a?(Array)
|
|
81
|
+
|
|
82
|
+
inline_array.each_with_index do |inline_key, inline_index|
|
|
83
|
+
inline_key = inline_key.to_sym if @symbolize_keys
|
|
84
|
+
last_key = inline_index == inline_array.size - 1
|
|
85
|
+
|
|
86
|
+
if last_key
|
|
87
|
+
if current[inline_key].nil?
|
|
88
|
+
keys.add_pair_key [inline_key], []
|
|
89
|
+
|
|
90
|
+
current[inline_key] = value
|
|
91
|
+
else
|
|
92
|
+
raise Key::KeyConflict, "Inline key #{inline_key} is already used"
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
current[inline_key] ||= {}
|
|
96
|
+
current = current[inline_key]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
push(merged_inline)
|
|
102
|
+
end
|
|
103
|
+
|
|
60
104
|
def start_(type)
|
|
61
105
|
push([type])
|
|
62
106
|
end
|
|
@@ -64,10 +108,172 @@ module Tomlrb
|
|
|
64
108
|
def end_(type)
|
|
65
109
|
array = []
|
|
66
110
|
while (value = @stack.pop) != [type]
|
|
67
|
-
raise ParseError, 'Unclosed table' if
|
|
111
|
+
raise ParseError, 'Unclosed table' if @stack.empty?
|
|
112
|
+
|
|
68
113
|
array.unshift(value)
|
|
69
114
|
end
|
|
70
115
|
array
|
|
71
116
|
end
|
|
117
|
+
|
|
118
|
+
def validate_value(value)
|
|
119
|
+
if value.nil?
|
|
120
|
+
raise ParseError, 'Value must be present'
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def assign_key_path(current, key, key_emptied)
|
|
127
|
+
existed = current.key?(key)
|
|
128
|
+
raise ParseError, "Cannot overwrite value with key #{key}" if existed && !current[key].is_a?(Hash)
|
|
129
|
+
if key_emptied
|
|
130
|
+
raise ParseError, "Cannot overwrite value with key #{key}" unless current.is_a?(Hash)
|
|
131
|
+
|
|
132
|
+
value = @stack.pop
|
|
133
|
+
raise ParseError, "Cannot overwrite value with key #{key}" if current[key].is_a?(Hash) && !value.is_a?(Hash)
|
|
134
|
+
current[key] = value
|
|
135
|
+
return current
|
|
136
|
+
end
|
|
137
|
+
current[key] ||= {}
|
|
138
|
+
current[key]
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class Keys
|
|
143
|
+
def initialize
|
|
144
|
+
@keys = {}
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def add_table_key(keys, is_array_of_tables = false)
|
|
148
|
+
self << [keys, [], is_array_of_tables]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def add_pair_key(keys, context)
|
|
152
|
+
self << [context, keys, false]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def <<(keys)
|
|
156
|
+
table_keys, pair_keys, is_array_of_tables = keys
|
|
157
|
+
current = @keys
|
|
158
|
+
current = append_table_keys(current, table_keys, pair_keys.empty?, is_array_of_tables)
|
|
159
|
+
append_pair_keys(current, pair_keys, table_keys.empty?, is_array_of_tables)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def append_table_keys(current, table_keys, pair_keys_empty, is_array_of_tables)
|
|
165
|
+
table_keys.each_with_index do |key, index|
|
|
166
|
+
declared = (index == table_keys.length - 1) && pair_keys_empty
|
|
167
|
+
if index.zero?
|
|
168
|
+
current = find_or_create_first_table_key(current, key, declared, is_array_of_tables)
|
|
169
|
+
else
|
|
170
|
+
current <<= [key, :table, declared, is_array_of_tables]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
current.clear_children if is_array_of_tables
|
|
175
|
+
current
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def find_or_create_first_table_key(current, key, declared, is_array_of_tables)
|
|
179
|
+
existed = current[key]
|
|
180
|
+
if existed && existed.type == :pair
|
|
181
|
+
raise Key::KeyConflict, "Key #{key} is already used as #{existed.type} key"
|
|
182
|
+
end
|
|
183
|
+
if existed && existed.declared? && declared && ! is_array_of_tables
|
|
184
|
+
raise Key::KeyConflict, "Key #{key} is already used"
|
|
185
|
+
end
|
|
186
|
+
k = existed || Key.new(key, :table, declared)
|
|
187
|
+
k.declared = k.declared? || declared
|
|
188
|
+
current[key] = k
|
|
189
|
+
k
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def append_pair_keys(current, pair_keys, table_keys_empty, is_array_of_tables)
|
|
193
|
+
pair_keys.each_with_index do |key, index|
|
|
194
|
+
declared = index == pair_keys.length - 1
|
|
195
|
+
if index.zero? && table_keys_empty
|
|
196
|
+
current = find_or_create_first_pair_key(current, key, declared, table_keys_empty)
|
|
197
|
+
else
|
|
198
|
+
key = current << [key, :pair, declared, is_array_of_tables]
|
|
199
|
+
current = key
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def find_or_create_first_pair_key(current, key, declared, table_keys_empty)
|
|
205
|
+
existed = current[key]
|
|
206
|
+
if existed && (existed.type == :pair) && declared && table_keys_empty
|
|
207
|
+
raise Key::KeyConflict, "Key #{key} is already used"
|
|
208
|
+
end
|
|
209
|
+
k = Key.new(key, :pair, declared)
|
|
210
|
+
current[key] = k
|
|
211
|
+
k
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
class Key
|
|
216
|
+
class KeyConflict < ParseError; end
|
|
217
|
+
|
|
218
|
+
attr_reader :key, :type
|
|
219
|
+
attr_writer :declared
|
|
220
|
+
|
|
221
|
+
def initialize(key, type, declared = false)
|
|
222
|
+
@key = key
|
|
223
|
+
@type = type
|
|
224
|
+
@declared = declared
|
|
225
|
+
@children = {}
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def declared?
|
|
229
|
+
@declared
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def <<(key_type_declared)
|
|
233
|
+
key, type, declared, is_array_of_tables = key_type_declared
|
|
234
|
+
existed = @children[key]
|
|
235
|
+
validate_already_declared_as_different_key(type, declared, existed)
|
|
236
|
+
validate_already_declared_as_non_array_table(type, is_array_of_tables, declared, existed)
|
|
237
|
+
validate_path_already_created_as_different_type(type, declared, existed)
|
|
238
|
+
validate_path_already_declared_as_different_type(type, declared, existed)
|
|
239
|
+
validate_already_declared_as_same_key(declared, existed)
|
|
240
|
+
@children[key] = existed || self.class.new(key, type, declared)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def clear_children
|
|
244
|
+
@children.clear
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
private
|
|
248
|
+
|
|
249
|
+
def validate_already_declared_as_different_key(type, _declared, existed)
|
|
250
|
+
if existed && existed.declared? && existed.type != type
|
|
251
|
+
raise KeyConflict, "Key #{existed.key} is already used as #{existed.type} key"
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def validate_already_declared_as_non_array_table(type, is_array_of_tables, declared, existed)
|
|
256
|
+
if declared && type == :table && existed && existed.declared? && !is_array_of_tables
|
|
257
|
+
raise KeyConflict, "Key #{existed.key} is already used"
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def validate_path_already_created_as_different_type(type, declared, existed)
|
|
262
|
+
if declared && (type == :table) && existed && (existed.type == :pair) && !existed.declared?
|
|
263
|
+
raise KeyConflict, "Key #{existed.key} is already used as #{existed.type} key"
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def validate_path_already_declared_as_different_type(type, declared, existed)
|
|
268
|
+
if !declared && (type == :pair) && existed && (existed.type == :pair) && existed.declared?
|
|
269
|
+
raise KeyConflict, "Key #{key} is already used as #{type} key"
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def validate_already_declared_as_same_key(declared, existed)
|
|
274
|
+
if existed && !existed.declared? && declared
|
|
275
|
+
raise KeyConflict, "Key #{existed.key} is already used as #{existed.type} key"
|
|
276
|
+
end
|
|
277
|
+
end
|
|
72
278
|
end
|
|
73
279
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Tomlrb
|
|
6
|
+
class LocalDate
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_delegators :@time, :year, :month, :day
|
|
10
|
+
|
|
11
|
+
def initialize(year, month, day)
|
|
12
|
+
@time = Time.utc(year, month, day, 0, 0, 0)
|
|
13
|
+
raise ArgumentError, "Invalid Local Date: #{year}-#{month}-#{day}" unless day.to_i == @time.day && month.to_i == @time.month && year.to_i == @time.year
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @param offset see {LocalDateTime#to_time}
|
|
17
|
+
# @return [Time] 00:00:00 of the date
|
|
18
|
+
def to_time(offset = '-00:00')
|
|
19
|
+
return @time if offset == '-00:00'
|
|
20
|
+
Time.new(year, month, day, 0, 0, 0, offset)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_s
|
|
24
|
+
@time.strftime('%F')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ==(other)
|
|
28
|
+
other.is_a?(self.class) &&
|
|
29
|
+
to_time == other.to_time
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def inspect
|
|
33
|
+
"#<#{self.class}: #{self}>"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Tomlrb
|
|
6
|
+
class LocalDateTime
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_delegators :@time, :year, :month, :day, :hour, :min, :sec, :usec, :nsec
|
|
10
|
+
|
|
11
|
+
def initialize(year, month, day, hour, min, sec) # rubocop:disable Metrics/ParameterLists
|
|
12
|
+
@time = Time.utc(year, month, day, hour, min, sec)
|
|
13
|
+
raise ArgumentError, "Invalid Local Date-Time: #{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}" unless min.to_i == @time.min && hour.to_i == @time.hour && day.to_i == @time.day && month.to_i == @time.month && year.to_i == @time.year
|
|
14
|
+
|
|
15
|
+
@sec = sec
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param offset [String, Symbol, Numeric, nil] time zone offset.
|
|
19
|
+
# * when +String+, must be '+HH:MM' format, '-HH:MM' format, 'UTC', 'A'..'I' or 'K'..'Z'. Arguments excluding '+-HH:MM' are supporeted at Ruby >= 2.7.0
|
|
20
|
+
# * when +Symbol+, must be +:dst+(for summar time for local) or +:std+(for standard time).
|
|
21
|
+
# * when +Numeric+, it is time zone offset in second.
|
|
22
|
+
# * when +nil+, local time zone offset is used.
|
|
23
|
+
# @return [Time]
|
|
24
|
+
def to_time(offset = '-00:00')
|
|
25
|
+
return @time if offset == '-00:00'
|
|
26
|
+
Time.new(year, month, day, hour, min, @sec, offset)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
frac = (@sec - sec)
|
|
31
|
+
frac_str = frac.zero? ? '' : frac.to_s[1..-1]
|
|
32
|
+
@time.strftime('%FT%T') << frac_str
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(other)
|
|
36
|
+
other.is_a?(self.class) &&
|
|
37
|
+
to_time == other.to_time
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def inspect
|
|
41
|
+
"#<#{self.class}: #{self}>"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Tomlrb
|
|
6
|
+
class LocalTime
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def_delegators :@time, :hour, :min, :sec, :usec, :nsec
|
|
10
|
+
|
|
11
|
+
def initialize(hour, min, sec)
|
|
12
|
+
@time = Time.utc(0, 1, 1, hour, min, sec)
|
|
13
|
+
raise ArgumentError, "Invalid Local Time: #{hour}-#{min}-#{sec}" unless min.to_i == @time.min && hour.to_i == @time.hour
|
|
14
|
+
@sec = sec
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param year [Integer]
|
|
18
|
+
# @param month [Integer]
|
|
19
|
+
# @param day [Integer]
|
|
20
|
+
# @param offset see {LocalDateTime#to_time}
|
|
21
|
+
# @return [Time] the time of the date specified by params
|
|
22
|
+
def to_time(year, month, day, offset = '-00:00')
|
|
23
|
+
Time.new(year, month, day, hour, min, @sec, offset)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
frac = (@sec - sec)
|
|
28
|
+
frac_str = frac.zero? ? '' : frac.to_s[1..-1]
|
|
29
|
+
@time.strftime('%T') << frac_str
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ==(other)
|
|
33
|
+
other.is_a?(self.class) &&
|
|
34
|
+
@time == other.to_time(0, 1, 1)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def inspect
|
|
38
|
+
"#<#{self.class}: #{self}>"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/tomlrb/parser.rb
CHANGED
data/lib/tomlrb/parser.y
CHANGED
|
@@ -1,102 +1,226 @@
|
|
|
1
1
|
class Tomlrb::GeneratedParser
|
|
2
|
-
token IDENTIFIER STRING_MULTI STRING_BASIC STRING_LITERAL_MULTI STRING_LITERAL DATETIME INTEGER FLOAT
|
|
2
|
+
token IDENTIFIER STRING_MULTI STRING_BASIC STRING_LITERAL_MULTI STRING_LITERAL DATETIME LOCAL_TIME INTEGER NON_DEC_INTEGER FLOAT FLOAT_KEYWORD BOOLEAN NEWLINE EOS
|
|
3
3
|
rule
|
|
4
4
|
expressions
|
|
5
5
|
| expressions expression
|
|
6
|
+
| expressions EOS
|
|
6
7
|
;
|
|
7
8
|
expression
|
|
8
9
|
: table
|
|
9
10
|
| assignment
|
|
10
11
|
| inline_table
|
|
12
|
+
| NEWLINE
|
|
11
13
|
;
|
|
12
14
|
table
|
|
13
|
-
: table_start
|
|
15
|
+
: table_start table_identifier table_end newlines
|
|
16
|
+
| table_start table_identifier table_end EOS
|
|
17
|
+
| table_start table_end newlines
|
|
18
|
+
| table_start table_end EOS
|
|
14
19
|
;
|
|
15
20
|
table_start
|
|
16
21
|
: '[' '[' { @handler.start_(:array_of_tables) }
|
|
17
22
|
| '[' { @handler.start_(:table) }
|
|
18
23
|
;
|
|
19
|
-
table_continued
|
|
20
|
-
: ']' ']' { array = @handler.end_(:array_of_tables); @handler.set_context(array, is_array_of_tables: true) }
|
|
21
|
-
| ']' { array = @handler.end_(:table); @handler.set_context(array) }
|
|
22
|
-
| table_identifier table_next
|
|
23
24
|
;
|
|
24
|
-
|
|
25
|
+
table_end
|
|
25
26
|
: ']' ']' { array = @handler.end_(:array_of_tables); @handler.set_context(array, is_array_of_tables: true) }
|
|
26
27
|
| ']' { array = @handler.end_(:table); @handler.set_context(array) }
|
|
27
|
-
| '.' table_continued
|
|
28
28
|
;
|
|
29
29
|
table_identifier
|
|
30
|
-
:
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
: table_identifier '.' table_identifier_component { @handler.push(val[2]) }
|
|
31
|
+
| table_identifier '.' FLOAT { val[2].split('.').each { |k| @handler.push(k) } }
|
|
32
|
+
| FLOAT {
|
|
33
|
+
keys = val[0].split('.')
|
|
34
|
+
@handler.start_(:table)
|
|
35
|
+
keys.each { |key| @handler.push(key) }
|
|
36
|
+
}
|
|
37
|
+
| table_identifier_component { @handler.push(val[0]) }
|
|
38
|
+
;
|
|
39
|
+
table_identifier_component
|
|
40
|
+
: IDENTIFIER
|
|
41
|
+
| STRING_BASIC { result = StringUtils.replace_escaped_chars(val[0]) }
|
|
42
|
+
| STRING_LITERAL
|
|
43
|
+
| INTEGER
|
|
44
|
+
| NON_DEC_INTEGER
|
|
45
|
+
| FLOAT_KEYWORD
|
|
46
|
+
| BOOLEAN
|
|
47
|
+
| DATETIME { result = val[0][0] }
|
|
48
|
+
| LOCAL_TIME { result = val[0][0] }
|
|
36
49
|
;
|
|
37
50
|
inline_table
|
|
38
|
-
: inline_table_start
|
|
51
|
+
: inline_table_start inline_table_end
|
|
52
|
+
| inline_table_start inline_continued inline_table_end
|
|
39
53
|
;
|
|
40
54
|
inline_table_start
|
|
41
55
|
: '{' { @handler.start_(:inline) }
|
|
42
56
|
;
|
|
57
|
+
inline_table_end
|
|
58
|
+
: '}' {
|
|
59
|
+
array = @handler.end_(:inline)
|
|
60
|
+
@handler.push_inline(array)
|
|
61
|
+
}
|
|
62
|
+
;
|
|
43
63
|
inline_continued
|
|
44
|
-
:
|
|
45
|
-
|
|
|
64
|
+
: inline_assignment
|
|
65
|
+
| inline_assignment inline_next
|
|
46
66
|
;
|
|
47
67
|
inline_next
|
|
48
|
-
: '
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
: ',' inline_continued
|
|
69
|
+
;
|
|
70
|
+
inline_assignment
|
|
71
|
+
: inline_assignment_key '=' value {
|
|
72
|
+
keys = @handler.end_(:inline_keys)
|
|
73
|
+
@handler.push(keys)
|
|
52
74
|
}
|
|
53
|
-
| ',' inline_continued
|
|
54
75
|
;
|
|
55
76
|
inline_assignment_key
|
|
56
|
-
:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
: inline_assignment_key '.' assignment_key_component {
|
|
78
|
+
@handler.push(val[2])
|
|
79
|
+
}
|
|
80
|
+
| inline_assignment_key '.' FLOAT { val[2].split('.').each { |k| @handler.push(k) } }
|
|
81
|
+
| FLOAT {
|
|
82
|
+
keys = val[0].split('.')
|
|
83
|
+
@handler.start_(:inline_keys)
|
|
84
|
+
keys.each { |key| @handler.push(key) }
|
|
85
|
+
}
|
|
86
|
+
| assignment_key_component {
|
|
87
|
+
@handler.start_(:inline_keys)
|
|
88
|
+
@handler.push(val[0])
|
|
89
|
+
}
|
|
60
90
|
;
|
|
61
91
|
assignment
|
|
62
|
-
:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
: assignment_key '=' value EOS {
|
|
93
|
+
keys = @handler.end_(:keys)
|
|
94
|
+
value = keys.pop
|
|
95
|
+
@handler.validate_value(value)
|
|
96
|
+
@handler.push(value)
|
|
97
|
+
@handler.assign(keys)
|
|
98
|
+
}
|
|
99
|
+
| assignment_key '=' value NEWLINE {
|
|
100
|
+
keys = @handler.end_(:keys)
|
|
101
|
+
value = keys.pop
|
|
102
|
+
@handler.validate_value(value)
|
|
103
|
+
@handler.push(value)
|
|
104
|
+
@handler.assign(keys)
|
|
105
|
+
}
|
|
106
|
+
;
|
|
107
|
+
assignment_key
|
|
108
|
+
: assignment_key '.' assignment_key_component { @handler.push(val[2]) }
|
|
109
|
+
| assignment_key '.' FLOAT { val[2].split('.').each { |k| @handler.push(k) } }
|
|
110
|
+
| FLOAT {
|
|
111
|
+
keys = val[0].split('.')
|
|
112
|
+
@handler.start_(:keys)
|
|
113
|
+
keys.each { |key| @handler.push(key) }
|
|
114
|
+
}
|
|
115
|
+
| assignment_key_component { @handler.start_(:keys); @handler.push(val[0]) }
|
|
116
|
+
;
|
|
117
|
+
assignment_key_component
|
|
118
|
+
: IDENTIFIER
|
|
119
|
+
| STRING_BASIC { result = StringUtils.replace_escaped_chars(val[0]) }
|
|
120
|
+
| STRING_LITERAL
|
|
121
|
+
| INTEGER
|
|
122
|
+
| NON_DEC_INTEGER
|
|
123
|
+
| FLOAT_KEYWORD
|
|
124
|
+
| BOOLEAN
|
|
125
|
+
| DATETIME { result = val[0][0] }
|
|
126
|
+
| LOCAL_TIME { result = val[0][0] }
|
|
68
127
|
;
|
|
69
128
|
array
|
|
70
|
-
: start_array
|
|
129
|
+
: start_array array_first_value array_values comma end_array
|
|
130
|
+
| start_array array_first_value array_values end_array
|
|
131
|
+
| start_array array_first_value comma end_array
|
|
132
|
+
| start_array array_first_value end_array
|
|
133
|
+
| start_array end_array
|
|
71
134
|
;
|
|
72
|
-
|
|
73
|
-
:
|
|
74
|
-
|
|
|
135
|
+
array_first_value
|
|
136
|
+
: newlines non_nil_value
|
|
137
|
+
| non_nil_value
|
|
75
138
|
;
|
|
76
|
-
|
|
77
|
-
:
|
|
78
|
-
|
|
|
139
|
+
array_values
|
|
140
|
+
: array_values array_value
|
|
141
|
+
| array_value
|
|
142
|
+
;
|
|
143
|
+
array_value
|
|
144
|
+
: comma newlines non_nil_value
|
|
145
|
+
| comma non_nil_value
|
|
79
146
|
;
|
|
80
147
|
start_array
|
|
81
148
|
: '[' { @handler.start_(:array) }
|
|
82
149
|
;
|
|
150
|
+
end_array
|
|
151
|
+
: newlines ']' { array = @handler.end_(:array); @handler.push(array.compact) }
|
|
152
|
+
| ']' { array = @handler.end_(:array); @handler.push(array.compact) }
|
|
153
|
+
;
|
|
154
|
+
comma
|
|
155
|
+
: newlines ','
|
|
156
|
+
| ','
|
|
157
|
+
;
|
|
158
|
+
newlines
|
|
159
|
+
: newlines NEWLINE
|
|
160
|
+
| NEWLINE
|
|
161
|
+
;
|
|
83
162
|
value
|
|
84
163
|
: scalar { @handler.push(val[0]) }
|
|
85
164
|
| array
|
|
86
165
|
| inline_table
|
|
87
166
|
;
|
|
167
|
+
non_nil_value
|
|
168
|
+
: non_nil_scalar { @handler.push(val[0]) }
|
|
169
|
+
| array
|
|
170
|
+
| inline_table
|
|
171
|
+
;
|
|
88
172
|
scalar
|
|
89
173
|
: string
|
|
90
174
|
| literal
|
|
91
175
|
;
|
|
176
|
+
non_nil_scalar
|
|
177
|
+
: string
|
|
178
|
+
| non_nil_literal
|
|
179
|
+
;
|
|
92
180
|
literal
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
|
|
181
|
+
| non_nil_literal
|
|
182
|
+
;
|
|
183
|
+
non_nil_literal
|
|
184
|
+
: FLOAT { result = val[0].to_f }
|
|
185
|
+
| FLOAT_KEYWORD {
|
|
186
|
+
v = val[0]
|
|
187
|
+
result = if v.end_with?('nan')
|
|
188
|
+
Float::NAN
|
|
189
|
+
else
|
|
190
|
+
(v[0] == '-' ? -1 : 1) * Float::INFINITY
|
|
191
|
+
end
|
|
192
|
+
}
|
|
96
193
|
| INTEGER { result = val[0].to_i }
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
|
|
194
|
+
| NON_DEC_INTEGER {
|
|
195
|
+
base = case val[0][1]
|
|
196
|
+
when 'x' then 16
|
|
197
|
+
when 'o' then 8
|
|
198
|
+
when 'b' then 2
|
|
199
|
+
end
|
|
200
|
+
result = val[0].to_i(base)
|
|
201
|
+
}
|
|
202
|
+
| BOOLEAN { result = val[0] == 'true' ? true : false }
|
|
203
|
+
| DATETIME {
|
|
204
|
+
_str, year, month, day, hour, min, sec, offset = val[0]
|
|
205
|
+
result = if offset.nil?
|
|
206
|
+
if hour.nil?
|
|
207
|
+
LocalDate.new(year, month, day)
|
|
208
|
+
else
|
|
209
|
+
LocalDateTime.new(year, month, day, hour, min || 0, sec.to_f)
|
|
210
|
+
end
|
|
211
|
+
else
|
|
212
|
+
# Patch for 24:00:00 which Ruby parses
|
|
213
|
+
if hour.to_i == 24 && min.to_i == 0 && sec.to_i == 0
|
|
214
|
+
hour = (hour.to_i + 1).to_s
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
time = Time.new(year, month, day, hour || 0, min || 0, sec.to_f, offset)
|
|
218
|
+
# Should be out of parser.y?
|
|
219
|
+
raise ArgumentError, "Invalid Offset Date-Time: #{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}#{offset}" unless min.to_i == time.min && hour.to_i == time.hour && day.to_i == time.day && month.to_i == time.month && year.to_i == time.year
|
|
220
|
+
time
|
|
221
|
+
end
|
|
222
|
+
}
|
|
223
|
+
| LOCAL_TIME { result = LocalTime.new(*val[0][1..-1]) }
|
|
100
224
|
;
|
|
101
225
|
string
|
|
102
226
|
: STRING_MULTI { result = StringUtils.replace_escaped_chars(StringUtils.multiline_replacements(val[0])) }
|