tomlrb 2.0.4 → 3.0.0
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 +103 -0
- data/README.md +85 -0
- data/lib/tomlrb/local_date.rb +24 -13
- data/lib/tomlrb/local_date_time.rb +33 -20
- data/lib/tomlrb/local_time.rb +36 -16
- data/lib/tomlrb/parser.rb +789 -14
- data/lib/tomlrb/version.rb +2 -2
- data/lib/tomlrb.rb +18 -32
- data/sig/tomlrb.rbs +75 -0
- metadata +16 -26
- data/lib/tomlrb/generated_parser.rb +0 -808
- data/lib/tomlrb/handler.rb +0 -279
- data/lib/tomlrb/parser.y +0 -230
- data/lib/tomlrb/scanner.rb +0 -92
- data/lib/tomlrb/string_utils.rb +0 -40
data/lib/tomlrb/handler.rb
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# frozen-string-literal: true
|
|
2
|
-
|
|
3
|
-
module Tomlrb
|
|
4
|
-
class Handler
|
|
5
|
-
attr_reader :output, :symbolize_keys
|
|
6
|
-
|
|
7
|
-
def initialize(**options)
|
|
8
|
-
@output = {}
|
|
9
|
-
@current = @output
|
|
10
|
-
@stack = []
|
|
11
|
-
@array_names = []
|
|
12
|
-
@current_table = []
|
|
13
|
-
@keys = Keys.new
|
|
14
|
-
@symbolize_keys = options[:symbolize_keys]
|
|
15
|
-
end
|
|
16
|
-
|
|
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
|
|
24
|
-
@current = @output
|
|
25
|
-
|
|
26
|
-
deal_with_array_of_tables(identifiers, is_array_of_tables) do |identifierz|
|
|
27
|
-
identifierz.each do |k|
|
|
28
|
-
k = k.to_sym if @symbolize_keys
|
|
29
|
-
if @current[k].is_a?(Array)
|
|
30
|
-
@current = @current[k].last
|
|
31
|
-
else
|
|
32
|
-
@current[k] ||= {}
|
|
33
|
-
@current = @current[k]
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def deal_with_array_of_tables(identifiers, is_array_of_tables)
|
|
40
|
-
stringified_identifier = identifiers.join('.')
|
|
41
|
-
|
|
42
|
-
if is_array_of_tables
|
|
43
|
-
@array_names << stringified_identifier
|
|
44
|
-
last_identifier = identifiers.pop
|
|
45
|
-
elsif @array_names.include?(stringified_identifier)
|
|
46
|
-
raise ParseError, 'Cannot define a normal table with the same name as an already established array'
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
yield(identifiers)
|
|
50
|
-
|
|
51
|
-
if is_array_of_tables
|
|
52
|
-
last_identifier = last_identifier.to_sym if @symbolize_keys
|
|
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?(:<<)
|
|
55
|
-
@current[last_identifier] << {}
|
|
56
|
-
@current = @current[last_identifier].last
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def assign(k)
|
|
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
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def push(o)
|
|
70
|
-
@stack << o
|
|
71
|
-
end
|
|
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
|
-
|
|
104
|
-
def start_(type)
|
|
105
|
-
push([type])
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def end_(type)
|
|
109
|
-
array = []
|
|
110
|
-
while (value = @stack.pop) != [type]
|
|
111
|
-
raise ParseError, 'Unclosed table' if @stack.empty?
|
|
112
|
-
|
|
113
|
-
array.unshift(value)
|
|
114
|
-
end
|
|
115
|
-
array
|
|
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
|
|
278
|
-
end
|
|
279
|
-
end
|
data/lib/tomlrb/parser.y
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
class Tomlrb::GeneratedParser
|
|
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
|
-
rule
|
|
4
|
-
expressions
|
|
5
|
-
| expressions expression
|
|
6
|
-
| expressions EOS
|
|
7
|
-
;
|
|
8
|
-
expression
|
|
9
|
-
: table
|
|
10
|
-
| assignment
|
|
11
|
-
| inline_table
|
|
12
|
-
| NEWLINE
|
|
13
|
-
;
|
|
14
|
-
table
|
|
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
|
|
19
|
-
;
|
|
20
|
-
table_start
|
|
21
|
-
: '[' '[' { @handler.start_(:array_of_tables) }
|
|
22
|
-
| '[' { @handler.start_(:table) }
|
|
23
|
-
;
|
|
24
|
-
;
|
|
25
|
-
table_end
|
|
26
|
-
: ']' ']' { array = @handler.end_(:array_of_tables); @handler.set_context(array, is_array_of_tables: true) }
|
|
27
|
-
| ']' { array = @handler.end_(:table); @handler.set_context(array) }
|
|
28
|
-
;
|
|
29
|
-
table_identifier
|
|
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] }
|
|
49
|
-
;
|
|
50
|
-
inline_table
|
|
51
|
-
: inline_table_start inline_table_end
|
|
52
|
-
| inline_table_start inline_continued inline_table_end
|
|
53
|
-
;
|
|
54
|
-
inline_table_start
|
|
55
|
-
: '{' { @handler.start_(:inline) }
|
|
56
|
-
;
|
|
57
|
-
inline_table_end
|
|
58
|
-
: '}' {
|
|
59
|
-
array = @handler.end_(:inline)
|
|
60
|
-
@handler.push_inline(array)
|
|
61
|
-
}
|
|
62
|
-
;
|
|
63
|
-
inline_continued
|
|
64
|
-
: inline_assignment
|
|
65
|
-
| inline_assignment inline_next
|
|
66
|
-
;
|
|
67
|
-
inline_next
|
|
68
|
-
: ',' inline_continued
|
|
69
|
-
;
|
|
70
|
-
inline_assignment
|
|
71
|
-
: inline_assignment_key '=' value {
|
|
72
|
-
keys = @handler.end_(:inline_keys)
|
|
73
|
-
@handler.push(keys)
|
|
74
|
-
}
|
|
75
|
-
;
|
|
76
|
-
inline_assignment_key
|
|
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
|
-
}
|
|
90
|
-
;
|
|
91
|
-
assignment
|
|
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] }
|
|
127
|
-
;
|
|
128
|
-
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
|
|
134
|
-
;
|
|
135
|
-
array_first_value
|
|
136
|
-
: newlines non_nil_value
|
|
137
|
-
| non_nil_value
|
|
138
|
-
;
|
|
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
|
|
146
|
-
;
|
|
147
|
-
start_array
|
|
148
|
-
: '[' { @handler.start_(:array) }
|
|
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
|
-
;
|
|
162
|
-
value
|
|
163
|
-
: scalar { @handler.push(val[0]) }
|
|
164
|
-
| array
|
|
165
|
-
| inline_table
|
|
166
|
-
;
|
|
167
|
-
non_nil_value
|
|
168
|
-
: non_nil_scalar { @handler.push(val[0]) }
|
|
169
|
-
| array
|
|
170
|
-
| inline_table
|
|
171
|
-
;
|
|
172
|
-
scalar
|
|
173
|
-
: string
|
|
174
|
-
| literal
|
|
175
|
-
;
|
|
176
|
-
non_nil_scalar
|
|
177
|
-
: string
|
|
178
|
-
| non_nil_literal
|
|
179
|
-
;
|
|
180
|
-
literal
|
|
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
|
-
}
|
|
193
|
-
| INTEGER { result = val[0].to_i }
|
|
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]) }
|
|
224
|
-
;
|
|
225
|
-
string
|
|
226
|
-
: STRING_MULTI { result = StringUtils.replace_escaped_chars(StringUtils.multiline_replacements(val[0])) }
|
|
227
|
-
| STRING_BASIC { result = StringUtils.replace_escaped_chars(val[0]) }
|
|
228
|
-
| STRING_LITERAL_MULTI { result = StringUtils.strip_spaces(val[0]) }
|
|
229
|
-
| STRING_LITERAL { result = val[0] }
|
|
230
|
-
;
|
data/lib/tomlrb/scanner.rb
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# frozen-string-literal: true
|
|
2
|
-
|
|
3
|
-
require 'strscan'
|
|
4
|
-
|
|
5
|
-
module Tomlrb
|
|
6
|
-
class Scanner
|
|
7
|
-
COMMENT =
|
|
8
|
-
/#[^\u0000-\u0008\u000A-\u001F\u007F]*/.freeze
|
|
9
|
-
IDENTIFIER =
|
|
10
|
-
/[A-Za-z0-9_-]+/.freeze
|
|
11
|
-
SPACE =
|
|
12
|
-
/[ \t]/.freeze
|
|
13
|
-
NEWLINE =
|
|
14
|
-
/(?:[ \t]*(?:\r?\n)[ \t]*)+/.freeze
|
|
15
|
-
STRING_BASIC =
|
|
16
|
-
/(")(?:\\?[^\u0000-\u0008\u000A-\u001F\u007F\\]|(?:\\[^\u0000-\u0008\u000A-\u001F\u007F]))*?\1/.freeze
|
|
17
|
-
STRING_MULTI =
|
|
18
|
-
/"{3}([^\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]*?(?<!\\)"{3,5})/m.freeze
|
|
19
|
-
STRING_LITERAL =
|
|
20
|
-
/(')(?:\\?[^\u0000-\u0008\u000A-\u001F\u007F])*?\1/.freeze
|
|
21
|
-
STRING_LITERAL_MULTI =
|
|
22
|
-
/'{3}([^\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]*?'{3,5})/m.freeze
|
|
23
|
-
DATETIME =
|
|
24
|
-
/(-?\d{4})-([01]\d)-([0-3]\d)(?:(?:t|\s)([0-2]\d):([0-5]\d):([0-6]\d(?:\.\d+)?))?(z|[-+][01]\d:\d{2})?/i.freeze
|
|
25
|
-
LOCAL_TIME =
|
|
26
|
-
/([0-2]\d):([0-5]\d):([0-6]\d(?:\.\d+)?)/.freeze
|
|
27
|
-
FLOAT =
|
|
28
|
-
/[+-]?(?:(?:\d|[1-9](?:_?\d)*)\.\d(?:_?\d)*|\d+(?=[eE]))(?:[eE][+-]?[0-9]+(_[0-9])*[0-9]*)?(?!\w)/.freeze
|
|
29
|
-
FLOAT_KEYWORD =
|
|
30
|
-
/[+-]?(?:inf|nan)\b/.freeze
|
|
31
|
-
INTEGER =
|
|
32
|
-
/[+-]?([1-9](_?\d)*|0)(?![A-Za-z0-9_-]+)/.freeze
|
|
33
|
-
NON_DEC_INTEGER =
|
|
34
|
-
/0(?:x[0-9A-Fa-f](_?[0-9A-Fa-f])*|o[0-7](_?[0-7])*|b[01](_?[01])*)/.freeze
|
|
35
|
-
BOOLEAN =
|
|
36
|
-
/true|false/.freeze
|
|
37
|
-
SPACED_ARRAY_OF_TABLES_START =
|
|
38
|
-
/^\[[ \t]+\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\]\]$/.freeze
|
|
39
|
-
SPACED_ARRAY_OF_TABLES_END =
|
|
40
|
-
/^\[\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\][ \t]+\]$/.freeze
|
|
41
|
-
SPACED_ARRAY_OF_TABLES_BOTH =
|
|
42
|
-
/^\[[ \t]+\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\][ \t]+\]$/.freeze
|
|
43
|
-
|
|
44
|
-
def initialize(io)
|
|
45
|
-
@ss = StringScanner.new(io.read)
|
|
46
|
-
@eos = false
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def next_token
|
|
50
|
-
case
|
|
51
|
-
when @ss.scan(NEWLINE) then [:NEWLINE, nil]
|
|
52
|
-
when @ss.scan(SPACED_ARRAY_OF_TABLES_START) then raise ParseError.new('Array of tables has spaces in starting brackets')
|
|
53
|
-
when @ss.scan(SPACED_ARRAY_OF_TABLES_END) then raise ParseError.new('Array of tables has spaces in ending brackets')
|
|
54
|
-
when @ss.scan(SPACED_ARRAY_OF_TABLES_BOTH) then raise ParseError.new('Array of tables has spaces in starting and ending brackets')
|
|
55
|
-
when @ss.scan(SPACE) then next_token
|
|
56
|
-
when @ss.scan(COMMENT) then next_token
|
|
57
|
-
when @ss.scan(DATETIME) then process_datetime
|
|
58
|
-
when @ss.scan(LOCAL_TIME) then process_local_time
|
|
59
|
-
when text = @ss.scan(STRING_MULTI) then [:STRING_MULTI, text[3..-4]]
|
|
60
|
-
when text = @ss.scan(STRING_BASIC) then [:STRING_BASIC, text[1..-2]]
|
|
61
|
-
when text = @ss.scan(STRING_LITERAL_MULTI) then [:STRING_LITERAL_MULTI, text[3..-4]]
|
|
62
|
-
when text = @ss.scan(STRING_LITERAL) then [:STRING_LITERAL, text[1..-2]]
|
|
63
|
-
when text = @ss.scan(FLOAT) then [:FLOAT, text]
|
|
64
|
-
when text = @ss.scan(FLOAT_KEYWORD) then [:FLOAT_KEYWORD, text]
|
|
65
|
-
when text = @ss.scan(INTEGER) then [:INTEGER, text]
|
|
66
|
-
when text = @ss.scan(NON_DEC_INTEGER) then [:NON_DEC_INTEGER, text]
|
|
67
|
-
when text = @ss.scan(BOOLEAN) then [:BOOLEAN, text]
|
|
68
|
-
when text = @ss.scan(IDENTIFIER) then [:IDENTIFIER, text]
|
|
69
|
-
when @ss.eos? then process_eos
|
|
70
|
-
else x = @ss.getch; [x, x]
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def process_datetime
|
|
75
|
-
offset = @ss[7].gsub(/[zZ]/, '+00:00') if @ss[7]
|
|
76
|
-
args = [@ss[0], @ss[1], @ss[2], @ss[3], @ss[4], @ss[5], @ss[6], offset]
|
|
77
|
-
[:DATETIME, args]
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def process_local_time
|
|
81
|
-
args = [@ss[0], @ss[1], @ss[2], @ss[3].to_f]
|
|
82
|
-
[:LOCAL_TIME, args]
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def process_eos
|
|
86
|
-
return if @eos
|
|
87
|
-
|
|
88
|
-
@eos = true
|
|
89
|
-
[:EOS, nil]
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
data/lib/tomlrb/string_utils.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen-string-literal: true
|
|
2
|
-
|
|
3
|
-
module Tomlrb
|
|
4
|
-
class StringUtils
|
|
5
|
-
SPECIAL_CHARS = {
|
|
6
|
-
'\\t' => "\t",
|
|
7
|
-
'\\b' => "\b",
|
|
8
|
-
'\\f' => "\f",
|
|
9
|
-
'\\n' => "\n",
|
|
10
|
-
'\\r' => "\r",
|
|
11
|
-
'\\"' => '"',
|
|
12
|
-
'\\\\' => '\\'
|
|
13
|
-
}.freeze
|
|
14
|
-
|
|
15
|
-
def self.multiline_replacements(str)
|
|
16
|
-
strip_spaces(str).gsub(/\\+\s*\n\s*/) do |matched|
|
|
17
|
-
if matched.match(/\\+/)[0].length.odd?
|
|
18
|
-
matched.gsub(/\\\s*\n\s*/, '')
|
|
19
|
-
else
|
|
20
|
-
matched
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def self.replace_escaped_chars(str)
|
|
26
|
-
str.gsub(/\\(u[\da-fA-F]{4}|U[\da-fA-F]{8}|.)/) do |m|
|
|
27
|
-
if m.size == 2
|
|
28
|
-
SPECIAL_CHARS[m] || (raise Tomlrb::ParseError.new "Escape sequence #{m} is reserved")
|
|
29
|
-
else
|
|
30
|
-
m[2..-1].to_i(16).chr(Encoding::UTF_8)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def self.strip_spaces(str)
|
|
36
|
-
str[0] = '' if str[0] == "\n"
|
|
37
|
-
str
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|