tomlrb 2.0.0 → 2.0.3

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.
@@ -13,6 +13,10 @@ module Tomlrb
13
13
  end
14
14
 
15
15
  def set_context(identifiers, is_array_of_tables: false)
16
+ if identifiers.empty?
17
+ raise ParseError, 'Array needs a name'
18
+ end
19
+
16
20
  @current_table = identifiers.dup
17
21
  @keys.add_table_key identifiers, is_array_of_tables
18
22
  @current = @output
@@ -21,7 +25,6 @@ module Tomlrb
21
25
  identifierz.each do |k|
22
26
  k = k.to_sym if @symbolize_keys
23
27
  if @current[k].is_a?(Array)
24
- @current[k] << {} if @current[k].empty?
25
28
  @current = @current[k].last
26
29
  else
27
30
  @current[k] ||= {}
@@ -32,7 +35,6 @@ module Tomlrb
32
35
  end
33
36
 
34
37
  def deal_with_array_of_tables(identifiers, is_array_of_tables)
35
- identifiers.map!{|n| n.gsub("\"", '')}
36
38
  stringified_identifier = identifiers.join('.')
37
39
 
38
40
  if is_array_of_tables
@@ -66,6 +68,32 @@ module Tomlrb
66
68
  @stack << o
67
69
  end
68
70
 
71
+ def push_inline(inline_arrays)
72
+ merged_inline = {}
73
+
74
+ inline_arrays.each do |inline_array|
75
+ current = merged_inline
76
+ value = inline_array.pop
77
+ inline_array.each_with_index do |inline_key, inline_index|
78
+ inline_key = inline_key.to_sym if @symbolize_keys
79
+ last_key = inline_index == inline_array.size - 1
80
+
81
+ if last_key
82
+ if current[inline_key].nil?
83
+ current[inline_key] = value
84
+ else
85
+ raise Key::KeyConflict, "Inline key #{inline_key} is already used"
86
+ end
87
+ else
88
+ current[inline_key] ||= {}
89
+ current = current[inline_key]
90
+ end
91
+ end
92
+ end
93
+
94
+ push(merged_inline)
95
+ end
96
+
69
97
  def start_(type)
70
98
  push([type])
71
99
  end
@@ -73,16 +101,23 @@ module Tomlrb
73
101
  def end_(type)
74
102
  array = []
75
103
  while (value = @stack.pop) != [type]
76
- raise ParseError, 'Unclosed table' if value.nil?
104
+ raise ParseError, 'Unclosed table' if @stack.empty?
77
105
  array.unshift(value)
78
106
  end
79
107
  array
80
108
  end
81
109
 
110
+ def validate_value(value)
111
+ if value.nil?
112
+ raise ParseError, 'Value must be present'
113
+ end
114
+ end
115
+
82
116
  private
83
117
 
84
118
  def assign_key_path(current, key, key_emptied)
85
119
  if key_emptied
120
+
86
121
  raise ParseError, "Cannot overwrite value with key #{key}" unless current.kind_of?(Hash)
87
122
  current[key] = @stack.pop
88
123
  return current
@@ -125,6 +160,7 @@ module Tomlrb
125
160
  end
126
161
  end
127
162
 
163
+ current.clear_children if is_array_of_tables
128
164
  current
129
165
  end
130
166
 
@@ -155,7 +191,7 @@ module Tomlrb
155
191
 
156
192
  def find_or_create_first_pair_key(current, key, declared, table_keys_empty)
157
193
  existed = current[key]
158
- if existed && existed.declared? && (existed.type == :pair) && declared && table_keys_empty
194
+ if existed && (existed.type == :pair) && declared && table_keys_empty
159
195
  raise Key::KeyConflict, "Key #{key} is already used"
160
196
  end
161
197
  k = Key.new(key, :pair, declared)
@@ -191,10 +227,14 @@ module Tomlrb
191
227
  @children[key] = existed || self.class.new(key, type, declared)
192
228
  end
193
229
 
230
+ def clear_children
231
+ @children.clear
232
+ end
233
+
194
234
  private
195
235
 
196
236
  def validate_already_declared_as_different_key(type, declared, existed)
197
- if declared && existed && existed.declared? && existed.type != type
237
+ if existed && existed.declared? && existed.type != type
198
238
  raise KeyConflict, "Key #{existed.key} is already used as #{existed.type} key"
199
239
  end
200
240
  end
@@ -22,7 +22,7 @@ module Tomlrb
22
22
  end
23
23
 
24
24
  def ==(other)
25
- other.respond_to?(:to_time) &&
25
+ other.kind_of?(self.class) &&
26
26
  to_time == other.to_time
27
27
  end
28
28
 
@@ -29,7 +29,7 @@ module Tomlrb
29
29
  end
30
30
 
31
31
  def ==(other)
32
- other.respond_to?(:to_time) &&
32
+ other.kind_of?(self.class) &&
33
33
  to_time == other.to_time
34
34
  end
35
35
 
@@ -27,7 +27,7 @@ module Tomlrb
27
27
  end
28
28
 
29
29
  def ==(other)
30
- other.respond_to?(:to_time) &&
30
+ other.kind_of?(self.class) &&
31
31
  @time == other.to_time(0, 1, 1)
32
32
  end
33
33
 
data/lib/tomlrb/parser.y CHANGED
@@ -1,5 +1,5 @@
1
1
  class Tomlrb::GeneratedParser
2
- token IDENTIFIER STRING_MULTI STRING_BASIC STRING_LITERAL_MULTI STRING_LITERAL DATETIME LOCAL_DATETIME LOCAL_DATE LOCAL_TIME INTEGER HEX_INTEGER OCT_INTEGER BIN_INTEGER FLOAT FLOAT_INF FLOAT_NAN TRUE FALSE NEWLINE EOS
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
@@ -12,7 +12,8 @@ rule
12
12
  | NEWLINE
13
13
  ;
14
14
  table
15
- : table_start table_continued
15
+ : table_start table_continued NEWLINE
16
+ | table_start table_continued EOS
16
17
  ;
17
18
  table_start
18
19
  : '[' '[' { @handler.start_(:array_of_tables) }
@@ -40,16 +41,12 @@ rule
40
41
  ;
41
42
  table_identifier_component
42
43
  : IDENTIFIER
43
- | STRING_BASIC
44
+ | STRING_BASIC { result = StringUtils.replace_escaped_chars(val[0]) }
44
45
  | STRING_LITERAL
45
46
  | INTEGER
46
- | HEX_INTEGER
47
- | OCT_INTEGER
48
- | BIN_INTEGER
49
- | FLOAT_INF
50
- | FLOAT_NAN
51
- | TRUE
52
- | FALSE
47
+ | NON_DEC_INTEGER
48
+ | FLOAT_KEYWORD
49
+ | BOOLEAN
53
50
  ;
54
51
  inline_table
55
52
  : inline_table_start inline_table_end
@@ -61,38 +58,50 @@ rule
61
58
  inline_table_end
62
59
  : '}' {
63
60
  array = @handler.end_(:inline)
64
- array.map!.with_index{ |n,i| i.even? ? n.to_sym : n } if @handler.symbolize_keys
65
- @handler.push(Hash[*array])
61
+ @handler.push_inline(array)
66
62
  }
67
63
  ;
68
64
  inline_continued
69
- : inline_assignment_key inline_assignment_value
70
- | inline_assignment_key inline_assignment_value inline_next
65
+ : inline_assignment
66
+ | inline_assignment inline_next
71
67
  ;
72
68
  inline_next
73
69
  : ',' inline_continued
74
70
  ;
75
- inline_assignment_key
76
- : inline_assignment_key '.' IDENTIFIER {
77
- array = @handler.end_(:inline)
78
- array.each { |key| @handler.push(key) }
79
- @handler.start_(:inline)
80
- @handler.push(val[2])
71
+ inline_assignment
72
+ : inline_assignment_key '=' value {
73
+ keys = @handler.end_(:inline_keys)
74
+ @handler.push(keys)
81
75
  }
82
- | IDENTIFIER { @handler.push(val[0]) }
83
76
  ;
84
- inline_assignment_value
85
- : '=' value
77
+ inline_assignment_key
78
+ : inline_assignment_key '.' assignment_key_component {
79
+ @handler.push(val[2])
80
+ }
81
+ | inline_assignment_key '.' FLOAT { val[2].split('.').each { |k| @handler.push(k) } }
82
+ | FLOAT {
83
+ keys = val[0].split('.')
84
+ @handler.start_(:inline_keys)
85
+ keys.each { |key| @handler.push(key) }
86
+ }
87
+ | assignment_key_component {
88
+ @handler.start_(:inline_keys)
89
+ @handler.push(val[0])
90
+ }
86
91
  ;
87
92
  assignment
88
93
  : assignment_key '=' value EOS {
89
94
  keys = @handler.end_(:keys)
90
- @handler.push(keys.pop)
95
+ value = keys.pop
96
+ @handler.validate_value(value)
97
+ @handler.push(value)
91
98
  @handler.assign(keys)
92
99
  }
93
100
  | assignment_key '=' value NEWLINE {
94
101
  keys = @handler.end_(:keys)
95
- @handler.push(keys.pop)
102
+ value = keys.pop
103
+ @handler.validate_value(value)
104
+ @handler.push(value)
96
105
  @handler.assign(keys)
97
106
  }
98
107
  ;
@@ -108,30 +117,25 @@ rule
108
117
  ;
109
118
  assignment_key_component
110
119
  : IDENTIFIER
111
- | STRING_BASIC
120
+ | STRING_BASIC { result = StringUtils.replace_escaped_chars(val[0]) }
112
121
  | STRING_LITERAL
113
122
  | INTEGER
114
- | HEX_INTEGER
115
- | OCT_INTEGER
116
- | BIN_INTEGER
117
- | FLOAT_INF
118
- | FLOAT_NAN
119
- | TRUE
120
- | FALSE
123
+ | NON_DEC_INTEGER
124
+ | FLOAT_KEYWORD
125
+ | BOOLEAN
121
126
  ;
122
127
  array
123
128
  : start_array array_continued
124
129
  ;
125
130
  array_continued
126
- : ']' { array = @handler.end_(:array); @handler.push(array) }
131
+ : ']' { array = @handler.end_(:array); @handler.push(array.compact) }
127
132
  | value array_next
128
133
  | NEWLINE array_continued
129
134
  ;
130
135
  array_next
131
- : ']' { array = @handler.end_(:array); @handler.push(array) }
136
+ : ']' { array = @handler.end_(:array); @handler.push(array.compact) }
132
137
  | ',' array_continued
133
138
  | NEWLINE array_continued
134
- | ',' NEWLINE array_continued
135
139
  ;
136
140
  start_array
137
141
  : '[' { @handler.start_(:array) }
@@ -147,17 +151,41 @@ rule
147
151
  ;
148
152
  literal
149
153
  | FLOAT { result = val[0].to_f }
150
- | FLOAT_INF { result = (val[0][0] == '-' ? -1 : 1) * Float::INFINITY }
151
- | FLOAT_NAN { result = Float::NAN }
154
+ | FLOAT_KEYWORD {
155
+ v = val[0]
156
+ result = if v.end_with?('nan')
157
+ Float::NAN
158
+ else
159
+ (v[0] == '-' ? -1 : 1) * Float::INFINITY
160
+ end
161
+ }
152
162
  | INTEGER { result = val[0].to_i }
153
- | HEX_INTEGER { result = val[0].to_i(16) }
154
- | OCT_INTEGER { result = val[0].to_i(8) }
155
- | BIN_INTEGER { result = val[0].to_i(2) }
156
- | TRUE { result = true }
157
- | FALSE { result = false }
158
- | DATETIME { result = Time.new(*val[0])}
159
- | LOCAL_DATETIME { result = LocalDateTime.new(*val[0]) }
160
- | LOCAL_DATE { result = LocalDate.new(*val[0]) }
163
+ | NON_DEC_INTEGER {
164
+ base = case val[0][1]
165
+ when "x" then 16
166
+ when "o" then 8
167
+ when "b" then 2
168
+ end
169
+ result = val[0].to_i(base)
170
+ }
171
+ | BOOLEAN { result = val[0] == 'true' ? true : false }
172
+ | DATETIME {
173
+ v = val[0]
174
+ result = if v[6].nil?
175
+ if v[4].nil?
176
+ LocalDate.new(v[0], v[1], v[2])
177
+ else
178
+ LocalDateTime.new(v[0], v[1], v[2], v[3] || 0, v[4] || 0, v[5].to_f)
179
+ end
180
+ else
181
+ # Patch for 24:00:00 which Ruby parses
182
+ if v[3].to_i == 24 && v[4].to_i == 0 && v[5].to_i == 0
183
+ v[3] = (v[3].to_i + 1).to_s
184
+ end
185
+
186
+ Time.new(v[0], v[1], v[2], v[3] || 0, v[4] || 0, v[5].to_f, v[6])
187
+ end
188
+ }
161
189
  | LOCAL_TIME { result = LocalTime.new(*val[0]) }
162
190
  ;
163
191
  string
@@ -5,24 +5,21 @@ module Tomlrb
5
5
  COMMENT = /#[^\u0000-\u0008\u000A-\u001F\u007F]*/
6
6
  IDENTIFIER = /[A-Za-z0-9_-]+/
7
7
  SPACE = /[ \t]/
8
- NEWLINE = /(\r?\n)/
8
+ NEWLINE = /(?:[ \t]*(?:\r?\n)[ \t]*)+/
9
9
  STRING_BASIC = /(["])(?:\\?[^\u0000-\u0008\u000A-\u001F\u007F])*?\1/
10
10
  STRING_MULTI = /"{3}([^\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]*?(?<!\\)"{3,5})/m
11
11
  STRING_LITERAL = /(['])(?:\\?[^\u0000-\u0008\u000A-\u001F\u007F])*?\1/
12
12
  STRING_LITERAL_MULTI = /'{3}([^\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]*?'{3,5})/m
13
- DATETIME = /(-?\d{4})-(\d{2})-(\d{2})(?:(?:t|\s)(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?))?(z|[-+]\d{2}:\d{2})/i
14
- LOCAL_DATETIME = /(-?\d{4})-(\d{2})-(\d{2})(?:t|\s)(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/i
15
- LOCAL_DATE = /(-?\d{4})-(\d{2})-(\d{2})/
13
+ DATETIME = /(-?\d{4})-(\d{2})-(\d{2})(?:(?:t|\s)(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?))?(z|[-+]\d{2}:\d{2})?/i
16
14
  LOCAL_TIME = /(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/
17
- FLOAT = /[+-]?(?:(?:\d|[1-9](?:_?\d)*)\.\d(?:_?\d)*|\d+(?=[eE]))(?:[eE][+-]?[0-9_]+)?(?!\w)/
18
- FLOAT_INF = /[+-]?inf\b/
19
- FLOAT_NAN = /[+-]?nan\b/
15
+ FLOAT = /[+-]?(?:(?:\d|[1-9](?:_?\d)*)\.\d(?:_?\d)*|\d+(?=[eE]))(?:[eE][+-]?[0-9]+(_[0-9])*[0-9]*)?(?!\w)/
16
+ FLOAT_KEYWORD = /[+-]?(?:inf|nan)\b/
20
17
  INTEGER = /[+-]?([1-9](_?\d)*|0)(?![A-Za-z0-9_-]+)/
21
- HEX_INTEGER = /0x[0-9A-Fa-f][0-9A-Fa-f_]*/
22
- OCT_INTEGER = /0o[0-7][0-7_]*/
23
- BIN_INTEGER = /0b[01][01_]*/
24
- TRUE = /true/
25
- FALSE = /false/
18
+ NON_DEC_INTEGER = /0(?:x[0-9A-Fa-f]+(?:_[0-9A-Fa-f])*[0-9A-Fa-f]*|o[0-7]+(?:_[0-7])*[0-7]*|b[01]+(?:_[01])*[01]*)/
19
+ BOOLEAN = /true|false/
20
+ SPACED_ARRAY_OF_TABLES_START = /^\[[ \t]+\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\]\]$/
21
+ SPACED_ARRAY_OF_TABLES_END = /^\[\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\][ \t]+\]$/
22
+ SPACED_ARRAY_OF_TABLES_BOTH = /^\[[ \t]+\[(#{IDENTIFIER}|#{STRING_BASIC}|#{STRING_LITERAL}|#{INTEGER}|#{NON_DEC_INTEGER}|#{FLOAT_KEYWORD}|#{BOOLEAN})\][ \t]+\]$/
26
23
 
27
24
  def initialize(io)
28
25
  @ss = StringScanner.new(io.read)
@@ -31,52 +28,37 @@ module Tomlrb
31
28
 
32
29
  def next_token
33
30
  case
34
- when @ss.eos? then process_eos
31
+ when @ss.scan(NEWLINE) then [:NEWLINE, nil]
32
+ when @ss.scan(SPACED_ARRAY_OF_TABLES_START) then raise ParseError.new("Array of tables has spaces in starting brackets")
33
+ when @ss.scan(SPACED_ARRAY_OF_TABLES_END) then raise ParseError.new("Array of tables has spaces in ending brackets")
34
+ when @ss.scan(SPACED_ARRAY_OF_TABLES_BOTH) then raise ParseError.new("Array of tables has spaces in starting and ending brackets")
35
35
  when @ss.scan(SPACE) then next_token
36
36
  when @ss.scan(COMMENT) then next_token
37
37
  when @ss.scan(DATETIME) then process_datetime
38
- when @ss.scan(LOCAL_DATETIME) then process_local_datetime
39
- when @ss.scan(LOCAL_DATE) then process_local_date
40
38
  when @ss.scan(LOCAL_TIME) then process_local_time
41
39
  when text = @ss.scan(STRING_MULTI) then [:STRING_MULTI, text[3..-4]]
42
40
  when text = @ss.scan(STRING_BASIC) then [:STRING_BASIC, text[1..-2]]
43
41
  when text = @ss.scan(STRING_LITERAL_MULTI) then [:STRING_LITERAL_MULTI, text[3..-4]]
44
42
  when text = @ss.scan(STRING_LITERAL) then [:STRING_LITERAL, text[1..-2]]
45
43
  when text = @ss.scan(FLOAT) then [:FLOAT, text]
46
- when text = @ss.scan(FLOAT_INF) then [:FLOAT_INF, text]
47
- when text = @ss.scan(FLOAT_NAN) then [:FLOAT_NAN, text]
44
+ when text = @ss.scan(FLOAT_KEYWORD) then [:FLOAT_KEYWORD, text]
48
45
  when text = @ss.scan(INTEGER) then [:INTEGER, text]
49
- when text = @ss.scan(HEX_INTEGER) then [:HEX_INTEGER, text]
50
- when text = @ss.scan(OCT_INTEGER) then [:OCT_INTEGER, text]
51
- when text = @ss.scan(BIN_INTEGER) then [:BIN_INTEGER, text]
52
- when text = @ss.scan(TRUE) then [:TRUE, text]
53
- when text = @ss.scan(FALSE) then [:FALSE, text]
54
- when text = @ss.scan(NEWLINE) then [:NEWLINE, text]
46
+ when text = @ss.scan(NON_DEC_INTEGER) then [:NON_DEC_INTEGER, text]
47
+ when text = @ss.scan(BOOLEAN) then [:BOOLEAN, text]
55
48
  when text = @ss.scan(IDENTIFIER) then [:IDENTIFIER, text]
49
+ when @ss.eos? then process_eos
56
50
  else x = @ss.getch; [x, x]
57
51
  end
58
52
  end
59
53
 
60
54
  def process_datetime
61
- if @ss[7].nil?
62
- offset = '+00:00'
63
- else
64
- offset = @ss[7].gsub('Z', '+00:00')
55
+ if @ss[7]
56
+ offset = @ss[7].gsub(/[zZ]/, '+00:00')
65
57
  end
66
- args = [@ss[1], @ss[2], @ss[3], @ss[4] || 0, @ss[5] || 0, @ss[6].to_f, offset]
58
+ args = [@ss[1], @ss[2], @ss[3], @ss[4], @ss[5], @ss[6], offset]
67
59
  [:DATETIME, args]
68
60
  end
69
61
 
70
- def process_local_datetime
71
- args = [@ss[1], @ss[2], @ss[3], @ss[4] || 0, @ss[5] || 0, @ss[6].to_f]
72
- [:LOCAL_DATETIME, args]
73
- end
74
-
75
- def process_local_date
76
- args = [@ss[1], @ss[2], @ss[3]]
77
- [:LOCAL_DATE, args]
78
- end
79
-
80
62
  def process_local_time
81
63
  args = [@ss[1], @ss[2], @ss[3].to_f]
82
64
  [:LOCAL_TIME, args]
@@ -1,3 +1,3 @@
1
1
  module Tomlrb
2
- VERSION = '2.0.0'.freeze
2
+ VERSION = '2.0.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tomlrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francois Bernier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-25 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-05-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: psych
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
13
27
  description: A racc based toml parser
14
28
  email:
15
29
  - frankbernier@gmail.com
@@ -48,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
62
  - !ruby/object:Gem::Version
49
63
  version: '0'
50
64
  requirements: []
51
- rubygems_version: 3.1.4
65
+ rubygems_version: 3.3.7
52
66
  signing_key:
53
67
  specification_version: 4
54
68
  summary: A racc based toml parser