tomlrb 2.0.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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