toml-rb 5.0.0 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ad0b925f6a96bd61b0e3b03278a9b5168ee7bcaefc78e9944f6b5c9e1167fba
4
- data.tar.gz: abce3cc229d44c245c02ae8dcc99acdffbdea3323ccb9c3d502f25fe4f73fab1
3
+ metadata.gz: d7514edd12c127a9f4ff3343e7590d4e526a15ebf3b13594d4d9683aea9fa1d0
4
+ data.tar.gz: a689fab092b24d2aadcecd046462af6d0cc741cdec8d4461372c65f6d249411b
5
5
  SHA512:
6
- metadata.gz: e06fafb826214f9438b2317b549b0eb9fcd03d6afda24ae1d93523b86f47dfdc004dd5b60ad935e5be55bbf21a0980cff4cf3e18ccf2c59ed4d51f552d787647
7
- data.tar.gz: 34f568d2288d58cdada406d05ecd525e9b7f491f3dcb365c7dd20425b2cb6425d93c5a106e3631417d0f8e37d7ba2fdddbcc13be5a23f8ecf6c871d744a6bff8
6
+ metadata.gz: e58611d93fbb27112dd505c43edc135500661c088e33cae0b50d6419bf30f814c94d389cc3a71f85072996a61492e74b9b9e9fce00477df5c3cd8e14691980a4
7
+ data.tar.gz: 4c5dff3f4537f96ba315410482dee6100463538be2cefe8c19fd8a1fcbce0db614870c9b0e83d52bc269fce28ef9872e8d9b78317b25fcae1b92a12b28c20cca
data/README.md CHANGED
@@ -9,7 +9,7 @@ toml-rb
9
9
  [![Code Climate](https://codeclimate.com/github/emancu/toml-rb/badges/gpa.svg)](https://codeclimate.com/github/emancu/toml-rb)
10
10
  [![RubyGem](https://img.shields.io/gem/dt/toml-rb.svg)](https://rubygems.org/gems/toml-rb)
11
11
 
12
- A [TOML](https://github.com/toml-lang/toml) parser using [Citrus](http://mjackson.github.io/citrus) library.
12
+ A pure Ruby [TOML](https://github.com/toml-lang/toml) parser and dumper with no runtime dependencies.
13
13
 
14
14
  toml-rb implements [TOML 1.1.0](https://toml.io/en/v1.1.0) and passes all [toml-test](https://github.com/toml-lang/toml-test) v2.2.0 decoder tests for that version.
15
15
 
@@ -20,7 +20,7 @@ Installation
20
20
 
21
21
  Or add this line to your Gemfile:
22
22
 
23
- gem "toml-rb", "~> 5.0"
23
+ gem "toml-rb", "~> 6.0"
24
24
 
25
25
  Parser Usage
26
26
  ------------
@@ -5,63 +5,50 @@ module TomlRB
5
5
  LocalDate = Class.new(Time)
6
6
  LocalTime = Class.new(Time)
7
7
 
8
- module DateSkeletonParser
9
- def value
10
- year, mon, day = [:year, :mon, :day].map { |s| capture(s).value }
11
- unless Date.valid_date?(year.to_i, mon.to_i, day.to_i, Date::GREGORIAN)
12
- raise ParseError, "Invalid date: #{year}-#{mon}-#{day}"
13
- end
14
- [year, mon, day]
15
- end
16
- end
8
+ module DatetimeParser
9
+ module_function
17
10
 
18
- module TimeSkeletonParser
19
- def value
20
- hour, min, sec = [:hour, :mim, :sec].map { |s| capture(s)&.value || "0" }
21
- unless hour.to_i.between?(0, 23) && min.to_i.between?(0, 59) && sec.to_i.between?(0, 60)
22
- raise ParseError, "Invalid time: #{hour}:#{min}:#{sec}"
23
- end
24
- [hour, min, sec, capture(:sec_frac) || "0"]
25
- end
26
- end
27
-
28
- module OffsetDateTimeParser
29
- def value
30
- skeleton = captures[:datetime_skeleton].first
31
- year, mon, day, hour, min, sec, sec_frac = skeleton.value
32
- offset = (captures[:date_offset].first || "+00:00").to_s
11
+ def offset_datetime(year, mon, day, hour, min, sec, sec_frac, offset)
12
+ validate_date(year, mon, day)
13
+ validate_time(hour, min, sec)
14
+ offset = "+00:00" if offset.casecmp?("Z")
33
15
  unless offset[1, 2].to_i.between?(0, 23) && offset[4, 2].to_i.between?(0, 59)
34
16
  raise ParseError, "Invalid UTC offset: #{offset}"
35
17
  end
36
- sec = "#{sec}.#{sec_frac}".to_r
37
18
 
38
- Time.new(year, mon, day, hour, min, sec, offset)
19
+ Time.new(year, mon, day, hour, min, seconds(sec, sec_frac), offset)
39
20
  end
40
- end
41
21
 
42
- module LocalDateTimeParser
43
- def value
44
- year, mon, day = captures[:date_skeleton].first.value
45
- hour, min, sec, sec_frac = captures[:time_skeleton].first.value
46
- sec = "#{sec}.#{sec_frac}".to_r
22
+ def local_datetime(year, mon, day, hour, min, sec, sec_frac)
23
+ validate_date(year, mon, day)
24
+ validate_time(hour, min, sec)
25
+ LocalDateTime.utc(year, mon, day, hour, min, seconds(sec, sec_frac))
26
+ end
47
27
 
48
- LocalDateTime.local(year, mon, day, hour, min, sec)
28
+ def local_date(year, mon, day)
29
+ validate_date(year, mon, day)
30
+ LocalDate.utc(year, mon, day)
49
31
  end
50
- end
51
32
 
52
- module LocalDateParser
53
- def value
54
- year, mon, day = captures[:date_skeleton].first.value
55
- LocalDate.local(year, mon, day)
33
+ def local_time(hour, min, sec, sec_frac)
34
+ validate_time(hour, min, sec)
35
+ LocalTime.utc(1970, 1, 1, hour, min, seconds(sec, sec_frac))
56
36
  end
57
- end
58
37
 
59
- module LocalTimeParser
60
- def value
61
- hour, min, sec, sec_frac = captures[:time_skeleton].first.value
62
- sec = "#{sec}.#{sec_frac}".to_r
38
+ def validate_date(year, mon, day)
39
+ unless Date.valid_date?(year.to_i, mon.to_i, day.to_i, Date::GREGORIAN)
40
+ raise ParseError, "Invalid date: #{year}-#{mon}-#{day}"
41
+ end
42
+ end
43
+
44
+ def validate_time(hour, min, sec)
45
+ unless hour.to_i.between?(0, 23) && min.to_i.between?(0, 59) && sec.to_i.between?(0, 60)
46
+ raise ParseError, "Invalid time: #{hour}:#{min}:#{sec || 0}"
47
+ end
48
+ end
63
49
 
64
- LocalTime.utc(1970, 1, 1, hour, min, sec)
50
+ def seconds(sec, sec_frac)
51
+ "#{sec || 0}.#{sec_frac || 0}".to_r
65
52
  end
66
53
  end
67
54
  end
@@ -19,7 +19,7 @@ module TomlRB
19
19
  attr_reader :toml_str
20
20
 
21
21
  def initialize(hash)
22
- @toml_str = ""
22
+ @toml_str = +""
23
23
 
24
24
  visit(hash, [])
25
25
  end
@@ -78,7 +78,7 @@ module TomlRB
78
78
  def dump_simple_pairs(simple_pairs)
79
79
  simple_pairs.each do |key, val|
80
80
  key = quote_key(key) unless bare_key? key
81
- @toml_str += "#{key} = #{to_toml(val)}\n"
81
+ @toml_str << "#{key} = #{to_toml(val)}\n"
82
82
  end
83
83
  end
84
84
 
@@ -108,7 +108,7 @@ module TomlRB
108
108
  new_prefix = prefix.join(".")
109
109
  new_prefix = "[" + new_prefix + "]" if extra_brackets
110
110
 
111
- @toml_str += "[" + new_prefix + "]\n"
111
+ @toml_str << "[" + new_prefix + "]\n"
112
112
  end
113
113
 
114
114
  def to_toml(obj)
@@ -15,10 +15,4 @@ module TomlRB
15
15
  result
16
16
  end
17
17
  end
18
-
19
- module InlineTableParser
20
- def value
21
- TomlRB::InlineTable.new(captures[:keyvalue].map(&:value))
22
- end
23
- end
24
18
  end
@@ -42,11 +42,4 @@ module TomlRB
42
42
  a_value.accept_visitor self
43
43
  end
44
44
  end
45
-
46
- # Used in document.citrus
47
- module KeyvalueParser
48
- def value
49
- TomlRB::Keyvalue.new(capture(:stripped_key).value, capture(:v).value)
50
- end
51
- end
52
45
  end
@@ -1,5 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+
1
5
  module TomlRB
2
6
  class Parser
7
+ BASIC_STRING = /(?:[^"\\\x00-\x08\x0a-\x1f\x7f]|\\.)*/
8
+ LITERAL_STRING = /[^'\x00-\x08\x0a-\x1f\x7f]*/
9
+ MULTILINE_STRING = /(?:[^"\\\x00-\x08\x0b-\x1f\x7f]|\r\n|\\(?:[^\r]|\r\n)|"{1,2}(?!"))*(?:"{0,2}(?="""))?/
10
+ MULTILINE_LITERAL = /(?:[^'\x00-\x08\x0b-\x1f\x7f]|\r\n|'{1,2}(?!'))*(?:'{0,2}(?='''))?/
11
+ # A UTF-8 regexp raises Encoding::CompatibilityError on binary input with high bytes.
12
+ COMMENT = /[^\x00-\x08\x0a-\x1f\x7f]*/u
13
+ DATETIME = /(\d{4})-(\d\d)-(\d\d)(?:[Tt ]([0-2]\d):([0-6]\d)(?::([0-6]\d)(?:\.(\d{1,9})\d*)?)?([Zz]|[+-]\d\d:\d\d)?)?/
14
+ TIME = /([0-2]\d):([0-6]\d)(?::([0-6]\d)(?:\.(\d{1,9})\d*)?)?/
15
+ PREFIXED_INTEGER = /0x[0-9a-fA-F](?:_?[0-9a-fA-F])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*/
16
+ DECIMAL = /[+-]?(?:0|[1-9](?:_?\d)*)(\.\d(?:_?\d)*(?:[eE][+-]?\d(?:_?\d)*)?|[eE][+-]?\d(?:_?\d)*)?/
17
+ MAX_NESTING = 100
18
+
3
19
  attr_reader :hash
4
20
 
5
21
  def initialize(content, symbolize_keys: false)
@@ -10,12 +26,13 @@ module TomlRB
10
26
  @tables = {}.compare_by_identity
11
27
  @current = @hash
12
28
  @symbolize_keys = symbolize_keys
29
+ @depth = 0
13
30
 
14
31
  begin
15
- parsed = TomlRB::Document.parse(content)
16
- parsed.matches.map(&:value).compact.each { |m| m.accept_visitor(self) }
17
- rescue Citrus::ParseError => e
18
- raise TomlRB::ParseError.new(e.message)
32
+ @scanner = StringScanner.new(source_text(content))
33
+ # The visitor runs after the whole document is parsed, so a syntax
34
+ # error wins over a key or table that the visitor rejects.
35
+ document.each { |statement| statement.accept_visitor(self) }
19
36
  rescue Encoding::CompatibilityError => e
20
37
  raise TomlRB::ParseError.new("Encoding error: #{e.message}")
21
38
  rescue ArgumentError => e
@@ -40,5 +57,240 @@ module TomlRB
40
57
  def visit_keyvalue(keyvalue)
41
58
  keyvalue.assign @current, @tables, @symbolize_keys
42
59
  end
60
+
61
+ private
62
+
63
+ def source_text(source)
64
+ if source.respond_to?(:to_path)
65
+ File.read(source.to_path)
66
+ elsif source.respond_to?(:read)
67
+ source.read
68
+ elsif source.respond_to?(:to_str)
69
+ source.to_str
70
+ else
71
+ raise ArgumentError, "Unable to parse from #{source}"
72
+ end
73
+ end
74
+
75
+ def document
76
+ statements = []
77
+ loop do
78
+ @scanner.skip(/(?:[ \t]*\r?\n)*[ \t]*/)
79
+ return statements if @scanner.eos?
80
+
81
+ statements << statement unless @scanner.match?(/#/)
82
+ line_end
83
+ end
84
+ end
85
+
86
+ def statement
87
+ if @scanner.match?(/\[/)
88
+ @scanner.match?(/\[\[/) ? table_array : table
89
+ else
90
+ keyvalue
91
+ end
92
+ end
93
+
94
+ def table_array
95
+ @scanner.skip(/\[\[[ \t]*/)
96
+ keys = key
97
+ expect(/[ \t]*\]\]/, "']]'")
98
+ TableArray.new(keys)
99
+ end
100
+
101
+ def table
102
+ @scanner.skip(/\[[ \t]*/)
103
+ keys = key
104
+ expect(/[ \t]*\]/, "']'")
105
+ Table.new(keys)
106
+ end
107
+
108
+ def keyvalue
109
+ keys = key
110
+ expect(/[ \t]*=[ \t]*/, "'='")
111
+ Keyvalue.new(keys, value)
112
+ end
113
+
114
+ def key
115
+ keys = [simple_key]
116
+ keys << simple_key while @scanner.skip(/[ \t]*\.[ \t]*/)
117
+ keys
118
+ end
119
+
120
+ def simple_key
121
+ if (bare = @scanner.scan(/[A-Za-z0-9_-]+/))
122
+ bare
123
+ elsif @scanner.match?(/"/)
124
+ basic_string
125
+ elsif @scanner.match?(/'/)
126
+ literal_string
127
+ else
128
+ syntax_error("a key")
129
+ end
130
+ end
131
+
132
+ def value
133
+ case @scanner.peek(1)
134
+ when '"'
135
+ @scanner.match?(/"""/) ? multiline_string : basic_string
136
+ when "'"
137
+ @scanner.match?(/'''/) ? multiline_literal : literal_string
138
+ when "["
139
+ nested { array }
140
+ when "{"
141
+ nested { inline_table }
142
+ when "t"
143
+ expect(/true/, "a value")
144
+ true
145
+ when "f"
146
+ expect(/false/, "a value")
147
+ false
148
+ else
149
+ datetime || local_time || number
150
+ end
151
+ end
152
+
153
+ def basic_string
154
+ @scanner.skip(/"/)
155
+ text = @scanner.scan(BASIC_STRING)
156
+ expect(/"/, %('"'))
157
+ unescape(text)
158
+ end
159
+
160
+ def literal_string
161
+ @scanner.skip(/'/)
162
+ text = @scanner.scan(LITERAL_STRING)
163
+ expect(/'/, %("'"))
164
+ text
165
+ end
166
+
167
+ def multiline_string
168
+ start = @scanner.pos
169
+ @scanner.skip(/"""(?:\r?\n)?/)
170
+ text = @scanner.scan(MULTILINE_STRING)
171
+ @scanner.skip(/"""/) || syntax_error(%('"""' to close the string at #{location(start)}))
172
+ unescape(text)
173
+ end
174
+
175
+ def multiline_literal
176
+ start = @scanner.pos
177
+ @scanner.skip(/'''(?:\r?\n)?/)
178
+ text = @scanner.scan(MULTILINE_LITERAL)
179
+ @scanner.skip(/'''/) || syntax_error(%("'''" to close the string at #{location(start)}))
180
+ text
181
+ end
182
+
183
+ def unescape(text)
184
+ text.include?("\\") ? BasicString.transform_escaped_chars(text) : text
185
+ end
186
+
187
+ def datetime
188
+ return unless @scanner.skip(DATETIME)
189
+
190
+ year, mon, day, hour, min, sec, sec_frac, offset = @scanner.values_at(1, 2, 3, 4, 5, 6, 7, 8)
191
+ if hour.nil?
192
+ DatetimeParser.local_date(year, mon, day)
193
+ elsif offset
194
+ DatetimeParser.offset_datetime(year, mon, day, hour, min, sec, sec_frac, offset)
195
+ else
196
+ DatetimeParser.local_datetime(year, mon, day, hour, min, sec, sec_frac)
197
+ end
198
+ end
199
+
200
+ def local_time
201
+ DatetimeParser.local_time(*@scanner.values_at(1, 2, 3, 4)) if @scanner.skip(TIME)
202
+ end
203
+
204
+ def number
205
+ if (text = @scanner.scan(PREFIXED_INTEGER))
206
+ Integer(text)
207
+ elsif (text = @scanner.scan(DECIMAL))
208
+ @scanner[1] ? text.to_f : text.to_i
209
+ elsif (text = @scanner.scan(/[+-]?inf/))
210
+ text.start_with?("-") ? -Float::INFINITY : Float::INFINITY
211
+ elsif @scanner.skip(/[+-]?nan/)
212
+ Float::NAN
213
+ else
214
+ syntax_error("a value")
215
+ end
216
+ end
217
+
218
+ def array
219
+ @scanner.skip(/\[/)
220
+ values = []
221
+ loop do
222
+ skip_whitespace_and_comments
223
+ return values if @scanner.skip(/\]/)
224
+
225
+ values << value
226
+ skip_whitespace_and_comments
227
+ return values if @scanner.skip(/\]/)
228
+
229
+ expect(/,/, "',' or ']'")
230
+ end
231
+ end
232
+
233
+ def inline_table
234
+ @scanner.skip(/\{/)
235
+ pairs = []
236
+ loop do
237
+ skip_whitespace_and_comments
238
+ return InlineTable.new(pairs) if @scanner.skip(/\}/)
239
+
240
+ pairs << keyvalue
241
+ skip_whitespace_and_comments
242
+ return InlineTable.new(pairs) if @scanner.skip(/\}/)
243
+
244
+ expect(/,/, "',' or '}'")
245
+ end
246
+ end
247
+
248
+ # Each nested array or inline table recurses, so the limit stops a deep
249
+ # document before it exhausts the stack.
250
+ def nested
251
+ @depth += 1
252
+ syntax_error("at most #{MAX_NESTING} nested arrays and inline tables") if @depth > MAX_NESTING
253
+ yield
254
+ ensure
255
+ @depth -= 1
256
+ end
257
+
258
+ def line_end
259
+ return if @scanner.skip(/[ \t]*\r?\n/)
260
+
261
+ @scanner.skip(/[ \t]*/)
262
+ @scanner.skip(COMMENT) if @scanner.skip(/#/)
263
+ new_line
264
+ end
265
+
266
+ def skip_whitespace_and_comments
267
+ @scanner.skip(/[ \t\r\n]*/)
268
+ while @scanner.skip(/#/)
269
+ @scanner.skip(COMMENT)
270
+ new_line
271
+ @scanner.skip(/[ \t\r\n]*/)
272
+ end
273
+ end
274
+
275
+ def new_line
276
+ @scanner.skip(/\r?\n/) || @scanner.eos? || syntax_error("a new line")
277
+ end
278
+
279
+ def expect(pattern, expected)
280
+ @scanner.skip(pattern) || syntax_error(expected)
281
+ end
282
+
283
+ def syntax_error(expected)
284
+ @scanner.skip(/[ \t]*/)
285
+ found = @scanner.eos? ? "end of input" : @scanner.check(/./m).inspect
286
+ raise ParseError, "Unexpected #{found} at #{location(@scanner.pos)}: expected #{expected}"
287
+ end
288
+
289
+ def location(pos)
290
+ consumed = @scanner.string.byteslice(0, pos)
291
+ line = consumed.count("\n") + 1
292
+ column = consumed.length - (consumed.rindex("\n") || -1)
293
+ "line #{line}, column #{column}"
294
+ end
43
295
  end
44
296
  end
@@ -1,5 +1,4 @@
1
1
  module TomlRB
2
- # Used in primitive.citrus
3
2
  module BasicString
4
3
  SPECIAL_CHARS = {
5
4
  "\\t" => "\t",
@@ -12,12 +11,6 @@ module TomlRB
12
11
  "\\\\" => "\\"
13
12
  }.freeze
14
13
 
15
- def value
16
- aux = TomlRB::BasicString.transform_escaped_chars first.value
17
-
18
- aux[1...-1]
19
- end
20
-
21
14
  # Replace the unicode escaped characters with the corresponding character
22
15
  # e.g. \u03B4 => ?
23
16
  def self.decode_unicode(str)
@@ -45,22 +38,4 @@ module TomlRB
45
38
  fail ParseError.new "Escape sequence #{m} is reserved"
46
39
  end
47
40
  end
48
-
49
- module LiteralString
50
- def value
51
- first.value[1...-1]
52
- end
53
- end
54
-
55
- module MultilineString
56
- def value
57
- TomlRB::BasicString.transform_escaped_chars captures[:text].first.value
58
- end
59
- end
60
-
61
- module MultilineLiteral
62
- def value
63
- captures[:text].first.value
64
- end
65
- end
66
41
  end
data/lib/toml-rb/table.rb CHANGED
@@ -31,11 +31,4 @@ module TomlRB
31
31
  @dotted_keys.join(".")
32
32
  end
33
33
  end
34
-
35
- # Used in document.citrus
36
- module TableParser
37
- def value
38
- TomlRB::Table.new(captures[:stripped_key].map(&:value).first)
39
- end
40
- end
41
34
  end
@@ -40,11 +40,4 @@ module TomlRB
40
40
  parser.visit_table_array self
41
41
  end
42
42
  end
43
-
44
- # Used in document.citrus
45
- module TableArrayParser
46
- def value
47
- TomlRB::TableArray.new(captures[:stripped_key].map(&:value).first)
48
- end
49
- end
50
43
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TomlRB
4
- VERSION = "5.0.0"
4
+ VERSION = "6.0.0"
5
5
  end
data/lib/toml-rb.rb CHANGED
@@ -1,7 +1,4 @@
1
- require "citrus"
2
-
3
1
  require_relative "toml-rb/errors"
4
- require_relative "toml-rb/array"
5
2
  require_relative "toml-rb/string"
6
3
  require_relative "toml-rb/datetime"
7
4
  require_relative "toml-rb/table"
@@ -11,12 +8,6 @@ require_relative "toml-rb/keyvalue"
11
8
  require_relative "toml-rb/parser"
12
9
  require_relative "toml-rb/dumper"
13
10
 
14
- File.dirname(File.expand_path(__FILE__)).tap do |root|
15
- Citrus.load "#{root}/toml-rb/grammars/helper.citrus"
16
- Citrus.load "#{root}/toml-rb/grammars/primitive.citrus"
17
- Citrus.load "#{root}/toml-rb/grammars/document.citrus"
18
- end
19
-
20
11
  module TomlRB
21
12
  # Public: Returns a hash from *TomlRB* content.
22
13
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toml-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emiliano Mancuso
@@ -10,26 +10,6 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2026-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: citrus
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '3.0'
20
- - - ">"
21
- - !ruby/object:Gem::Version
22
- version: '3.0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '3.0'
30
- - - ">"
31
- - !ruby/object:Gem::Version
32
- version: '3.0'
33
13
  - !ruby/object:Gem::Dependency
34
14
  name: minitest
35
15
  requirement: !ruby/object:Gem::Requirement
@@ -72,7 +52,7 @@ dependencies:
72
52
  - - ">="
73
53
  - !ruby/object:Gem::Version
74
54
  version: '0'
75
- description: 'A Toml parser using Citrus parsing library. '
55
+ description: A TOML 1.1.0 parser and dumper.
76
56
  email:
77
57
  - emiliano.mancuso@gmail.com
78
58
  - lucas.tolchinsky@gmail.com
@@ -83,13 +63,9 @@ files:
83
63
  - LICENSE
84
64
  - README.md
85
65
  - lib/toml-rb.rb
86
- - lib/toml-rb/array.rb
87
66
  - lib/toml-rb/datetime.rb
88
67
  - lib/toml-rb/dumper.rb
89
68
  - lib/toml-rb/errors.rb
90
- - lib/toml-rb/grammars/document.citrus
91
- - lib/toml-rb/grammars/helper.citrus
92
- - lib/toml-rb/grammars/primitive.citrus
93
69
  - lib/toml-rb/inline_table.rb
94
70
  - lib/toml-rb/keyvalue.rb
95
71
  - lib/toml-rb/parser.rb
data/lib/toml-rb/array.rb DELETED
@@ -1,8 +0,0 @@
1
- module TomlRB
2
- module ArrayParser
3
- def value
4
- elements = captures[:array_elements].first
5
- elements ? elements.value : []
6
- end
7
- end
8
- end
@@ -1,67 +0,0 @@
1
- grammar TomlRB::Document
2
- include TomlRB::Primitive
3
-
4
- rule document
5
- (comment | table_array | table | keyvalue_line | line_break | trailing_space)*
6
- end
7
-
8
- rule table_array
9
- (space? '[[' stripped_key ']]' line_end) <TomlRB::TableArrayParser>
10
- end
11
-
12
- rule table
13
- (space? '[' stripped_key ']' line_end) <TomlRB::TableParser>
14
- end
15
-
16
- rule keyvalue_line
17
- (keyvalue line_end) { capture(:keyvalue).value }
18
- end
19
-
20
- rule keyvalue
21
- (stripped_key '=' space? v:(toml_values) space) <TomlRB::KeyvalueParser>
22
- end
23
-
24
- ### Values
25
-
26
- rule inline_table
27
- (space? '{' array_comments (keyvalue (array_comments ',' array_comments keyvalue)* (array_comments ',')?)? array_comments '}' ) <TomlRB::InlineTableParser>
28
- end
29
-
30
- rule array
31
- ("[" array_comments (array_elements (array_comments ",")?)? array_comments "]") <TomlRB::ArrayParser>
32
- end
33
-
34
- rule array_array
35
- (array (space "," array_comments array)*) {
36
- captures[:array].map(&:value)
37
- }
38
- end
39
-
40
- rule array_elements
41
- (toml_values (array_comments "," array_comments toml_values)*) {
42
- captures[:toml_values].map(&:value)
43
- }
44
- end
45
-
46
- ### Helpers
47
-
48
- rule line_end
49
- (space (comment | line_break | !.))
50
- end
51
-
52
- rule trailing_space
53
- ([ \t]+ !.) { nil }
54
- end
55
-
56
- rule stripped_key
57
- (space? key space?) { captures[:key].first.value }
58
- end
59
-
60
- rule array_comments
61
- (indent? (comment indent?)*)
62
- end
63
-
64
- rule toml_values
65
- primitive | inline_table | array
66
- end
67
- end
@@ -1,17 +0,0 @@
1
- grammar TomlRB::Helper
2
- rule comment
3
- (space? "#" /[^\x00-\x08\x0a-\x1f\x7f]*(?=\r?\n|\z)/u line_break?) { nil }
4
- end
5
-
6
- rule space
7
- [ \t]*
8
- end
9
-
10
- rule indent
11
- [ \t\r\n]*
12
- end
13
-
14
- rule line_break
15
- (space "\n" space | space "\r\n" space) { nil }
16
- end
17
- end
@@ -1,176 +0,0 @@
1
- grammar TomlRB::Primitive
2
- include TomlRB::Helper
3
-
4
- rule primitive
5
- datetime | bool | number | string
6
- end
7
-
8
- ##
9
- # String rules
10
- ##
11
-
12
- rule string
13
- multiline_string | multiline_literal | basic_string | literal_string
14
- end
15
-
16
- rule basic_string
17
- (/"(?:[^"\\\x00-\x08\x0a-\x1f\x7f]|\\.)*"/ space) <TomlRB::BasicString>
18
- end
19
-
20
- rule literal_string
21
- (/'[^'\x00-\x08\x0a-\x1f\x7f]*'/ space) <TomlRB::LiteralString>
22
- end
23
-
24
- rule multiline_string
25
- ('"""' /\r?\n/? text:/(?:[^"\\\x00-\x08\x0b-\x1f\x7f]|\r\n|\\(?:[^\r]|\r\n)|"{1,2}(?!"))*"{0,2}(?=""")/ '"""' space) <TomlRB::MultilineString>
26
- end
27
-
28
- rule multiline_literal
29
- ("'''" /\r?\n/? text:/(?:[^'\x00-\x08\x0b-\x1f\x7f]|\r\n|'{1,2}(?!'))*'{0,2}(?=''')/ "'''" space) <TomlRB::MultilineLiteral>
30
- end
31
-
32
- ##
33
- # Date time rules
34
- ##
35
-
36
- rule datetime
37
- offset_datetime | local_datetime | local_date | local_time
38
- end
39
-
40
- rule offset_datetime
41
- ( skeleton:datetime_skeleton ([Zz] | date_offset) ) <TomlRB::OffsetDateTimeParser>
42
- end
43
-
44
- rule local_datetime
45
- datetime_skeleton <TomlRB::LocalDateTimeParser>
46
- end
47
-
48
- rule local_date
49
- (date_skeleton space) <TomlRB::LocalDateParser>
50
- end
51
-
52
- rule local_time
53
- (time_skeleton space) <TomlRB::LocalTimeParser>
54
- end
55
-
56
- rule datetime_skeleton
57
- (date_skeleton ([Tt]|" ") time_skeleton) {
58
- capture(:date_skeleton).value + capture(:time_skeleton).value
59
- }
60
- end
61
-
62
- rule date_skeleton
63
- (year:/\d\d\d\d/ "-" mon:/\d\d/ "-" day:/\d\d/) <TomlRB::DateSkeletonParser>
64
- end
65
-
66
- rule time_skeleton
67
- ( hour:([0-2][0-9]) ":" mim:([0-6][0-9]) (":" sec:([0-6][0-9]) ("." sec_frac:(/\d/1*9) /\d/*)?)? ) <TomlRB::TimeSkeletonParser>
68
- end
69
-
70
- rule date_offset
71
- offset:(sign /\d\d/ ":" /\d\d/)
72
- end
73
-
74
- ##
75
- # Number rules
76
- ##
77
-
78
- rule number
79
- float | integer
80
- end
81
-
82
- rule float
83
- inf | nan | exponential_float | fractional_float
84
- end
85
-
86
- rule inf
87
- (s:sign? 'inf') {
88
- sign = (capture(:s).value != '-') ? 1 : -1
89
- sign * Float::INFINITY
90
- }
91
- end
92
-
93
- rule nan
94
- (sign? 'nan') { Float::NAN }
95
- end
96
-
97
- rule exponential_float
98
- ((fractional_float | decimal_integer) [eE] exponent) { to_str.to_f }
99
- end
100
-
101
- rule exponent
102
- sign? [0-9] ([0-9] | '_' [0-9])*
103
- end
104
-
105
- rule fractional_float
106
- (decimal_integer '.' [0-9] ([0-9] | '_' [0-9])*) {
107
- to_str.to_f
108
- }
109
- end
110
-
111
- rule integer
112
- hexadecimal_integer | octal_integer | binary_integer | decimal_integer
113
- end
114
-
115
- rule hexadecimal_integer
116
- ('0x' [a-fA-F0-9] ([a-fA-F0-9] | '_' [a-fA-F0-9])*) { to_str.to_i(16) }
117
- end
118
-
119
- rule octal_integer
120
- ('0o' [0-7] ([0-7] | '_' [0-7])*) { to_str.to_i(8) }
121
- end
122
-
123
- rule binary_integer
124
- ('0b' [01] ([01] | '_' [01])*) { to_str.to_i(2) }
125
- end
126
-
127
- rule decimal_integer
128
- (sign? ('0' | [1-9] ([0-9] | '_' [0-9])*)) { to_str.to_i }
129
- end
130
-
131
- rule sign
132
- '+' | '-'
133
- end
134
-
135
- ##
136
- # Boolean rules
137
- ##
138
-
139
- rule bool
140
- true | false
141
- end
142
-
143
- rule true
144
- 'true' { true }
145
- end
146
-
147
- rule false
148
- 'false' { false }
149
- end
150
-
151
- ##
152
- # Key rules
153
- ##
154
-
155
- rule key
156
- dotted_key
157
- end
158
-
159
- rule bare_key
160
- [a-zA-Z0-9_-]+
161
- end
162
-
163
- rule quoted_key
164
- basic_string | literal_string
165
- end
166
-
167
- rule single_key
168
- quoted_key | bare_key
169
- end
170
-
171
- rule dotted_key
172
- (space? single_key space? ("." space? single_key space?)* ) {
173
- captures[:single_key].map(&:value)
174
- }
175
- end
176
- end