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.
@@ -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
- k = k.to_sym if @symbolize_keys
53
- @current[k] = @stack.pop
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 value.nil?
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
@@ -1,4 +1,6 @@
1
- require "tomlrb/generated_parser"
1
+ # frozen-string-literal: true
2
+
3
+ require 'tomlrb/generated_parser'
2
4
 
3
5
  class Tomlrb::Parser < Tomlrb::GeneratedParser
4
6
 
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 FLOAT_INF FLOAT_NAN TRUE FALSE
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 table_continued
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
- table_next
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
- : IDENTIFIER { @handler.push(val[0]) }
31
- | STRING_BASIC { @handler.push(val[0]) }
32
- | STRING_LITERAL { @handler.push(val[0]) }
33
- | INTEGER { @handler.push(val[0]) }
34
- | TRUE { @handler.push(val[0]) }
35
- | FALSE { @handler.push(val[0]) }
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 inline_continued
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
- : '}' { array = @handler.end_(:inline); @handler.push(Hash[*array]) }
45
- | inline_assignment_key inline_assignment_value inline_next
64
+ : inline_assignment
65
+ | inline_assignment inline_next
46
66
  ;
47
67
  inline_next
48
- : '}' {
49
- array = @handler.end_(:inline)
50
- array.map!.with_index{ |n,i| i.even? ? n.to_sym : n } if @handler.symbolize_keys
51
- @handler.push(Hash[*array])
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
- : IDENTIFIER { @handler.push(val[0]) }
57
- ;
58
- inline_assignment_value
59
- : '=' value
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
- : IDENTIFIER '=' value { @handler.assign(val[0]) }
63
- | STRING_BASIC '=' value { @handler.assign(val[0]) }
64
- | STRING_LITERAL '=' value { @handler.assign(val[0]) }
65
- | INTEGER '=' value { @handler.assign(val[0]) }
66
- | TRUE '=' value { @handler.assign(val[0]) }
67
- | FALSE '=' value { @handler.assign(val[0]) }
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 array_continued
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
- array_continued
73
- : ']' { array = @handler.end_(:array); @handler.push(array) }
74
- | value array_next
135
+ array_first_value
136
+ : newlines non_nil_value
137
+ | non_nil_value
75
138
  ;
76
- array_next
77
- : ']' { array = @handler.end_(:array); @handler.push(array) }
78
- | ',' array_continued
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
- | FLOAT { result = val[0].to_f }
94
- | FLOAT_INF { result = (val[0][0] == '-' ? -1 : 1) * Float::INFINITY }
95
- | FLOAT_NAN { result = Float::NAN }
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
- | TRUE { result = true }
98
- | FALSE { result = false }
99
- | DATETIME { result = Time.new(*val[0])}
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])) }