nestedtext 3.2.1 → 4.2.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 +22 -5
- data/README.md +84 -17
- data/lib/nestedtext/constants.rb +6 -3
- data/lib/nestedtext/core_ext.rb +7 -12
- data/lib/nestedtext/core_ext_internal.rb +3 -1
- data/lib/nestedtext/decode.rb +11 -9
- data/lib/nestedtext/dumper.rb +74 -69
- data/lib/nestedtext/encode.rb +7 -6
- data/lib/nestedtext/encode_helpers.rb +4 -3
- data/lib/nestedtext/error.rb +2 -0
- data/lib/nestedtext/errors_internal.rb +85 -61
- data/lib/nestedtext/inline_parser.rb +134 -0
- data/lib/nestedtext/parser.rb +144 -185
- data/lib/nestedtext/scanners.rb +21 -15
- data/lib/nestedtext/version.rb +1 -1
- data/lib/nestedtext.rb +6 -6
- data/nestedtext.gemspec +18 -16
- metadata +18 -2
data/lib/nestedtext/parser.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'stringio'
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
5
|
+
require 'nestedtext/errors_internal'
|
6
|
+
require 'nestedtext/scanners'
|
7
|
+
require 'nestedtext/constants'
|
8
|
+
require 'nestedtext/inline_parser'
|
8
9
|
|
9
10
|
module NestedText
|
10
11
|
# A LL(1) recursive descent parser for NT.
|
11
|
-
class Parser
|
12
|
+
class Parser # rubocop:disable Metrics/ClassLength
|
12
13
|
def self.assert_valid_top_level_type(top_class)
|
13
|
-
|
14
|
-
|
14
|
+
if !top_class.nil? && top_class.is_a?(Class) && TOP_LEVEL_TYPES.map(&:object_id).include?(top_class.object_id)
|
15
|
+
return
|
15
16
|
end
|
17
|
+
|
18
|
+
raise Errors::UnsupportedTopLevelTypeError, top_class
|
16
19
|
end
|
17
20
|
|
18
21
|
def initialize(io, top_class, strict: false)
|
@@ -21,42 +24,64 @@ module NestedText
|
|
21
24
|
@top_class = top_class
|
22
25
|
@strict = strict
|
23
26
|
@line_scanner = LineScanner.new(io)
|
24
|
-
@inline_scanner = nil
|
25
27
|
end
|
26
28
|
|
27
29
|
def parse
|
28
30
|
result = parse_any(0)
|
29
31
|
case @top_class.object_id
|
30
32
|
when Object.object_id
|
31
|
-
|
32
|
-
!result.nil? && ![Hash, Array, String].include?(result.class) && @strict
|
33
|
+
return_object(result)
|
33
34
|
when Hash.object_id
|
34
|
-
result
|
35
|
-
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(Hash)
|
35
|
+
return_hash(result)
|
36
36
|
when Array.object_id
|
37
|
-
result
|
38
|
-
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(Array)
|
37
|
+
return_array(result)
|
39
38
|
when String.object_id
|
40
|
-
result
|
41
|
-
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(String)
|
39
|
+
return_string(result)
|
42
40
|
else
|
43
41
|
raise Errors::UnsupportedTopLevelTypeError, @top_class
|
44
42
|
end
|
45
|
-
result
|
46
43
|
end
|
47
44
|
|
48
45
|
private
|
49
46
|
|
47
|
+
def return_object(result)
|
48
|
+
raise Errors::AssertionError, 'Parsed result is of unexpected type.' if \
|
49
|
+
!result.nil? && ![Hash, Array, String].include?(result.class) && @strict
|
50
|
+
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
def return_hash(result)
|
55
|
+
result = {} if result.nil?
|
56
|
+
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(Hash)
|
57
|
+
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def return_array(result)
|
62
|
+
result = [] if result.nil?
|
63
|
+
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(Array)
|
64
|
+
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
def return_string(result)
|
69
|
+
result = '' if result.nil?
|
70
|
+
raise Errors::TopLevelTypeMismatchParsedTypeError.new(@top_class, result) unless result.instance_of?(String)
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
50
75
|
def assert_valid_input_type(input)
|
51
|
-
|
52
|
-
|
53
|
-
|
76
|
+
return if input.nil? || input.is_a?(IO) || input.is_a?(StringIO)
|
77
|
+
|
78
|
+
raise Errors::WrongInputTypeError.new([IO, StringIO], input)
|
54
79
|
end
|
55
80
|
|
56
81
|
def parse_any(indentation)
|
57
82
|
return nil if @line_scanner.peek.nil?
|
58
83
|
|
59
|
-
case @line_scanner.peek.tag
|
84
|
+
case @line_scanner.peek.tag
|
60
85
|
when :list_item
|
61
86
|
parse_list_item(indentation)
|
62
87
|
when :dict_item, :key_item
|
@@ -74,27 +99,101 @@ module NestedText
|
|
74
99
|
end
|
75
100
|
end
|
76
101
|
|
102
|
+
def parse_list_item_value(indentation, value)
|
103
|
+
return value unless value.nil?
|
104
|
+
|
105
|
+
if !@line_scanner.peek.nil? && @line_scanner.peek.indentation > indentation
|
106
|
+
parse_any(@line_scanner.peek.indentation)
|
107
|
+
elsif @line_scanner.peek.nil? || @line_scanner.peek.tag == :list_item
|
108
|
+
''
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_list_line(line, indentation)
|
113
|
+
Errors.raise_unrecognized_line(line) if line.tag == :unrecognized
|
114
|
+
raise Errors::ParseLineTypeExpectedListItemError, line unless line.tag == :list_item
|
115
|
+
raise Errors::ParseInvalidIndentationError.new(line, indentation) if line.indentation != indentation
|
116
|
+
end
|
117
|
+
|
77
118
|
def parse_list_item(indentation)
|
78
119
|
result = []
|
79
120
|
while !@line_scanner.peek.nil? && @line_scanner.peek.indentation >= indentation
|
80
121
|
line = @line_scanner.read_next
|
122
|
+
assert_list_line(line, indentation)
|
123
|
+
result << parse_list_item_value(indentation, line.attribs['value'])
|
124
|
+
end
|
125
|
+
result
|
126
|
+
end
|
81
127
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
128
|
+
def deserialize_custom_class(hash, first_line)
|
129
|
+
return hash unless !@strict && hash.length == 2 && hash.key?(CUSTOM_CLASS_KEY)
|
130
|
+
|
131
|
+
class_name = hash[CUSTOM_CLASS_KEY]
|
132
|
+
begin
|
133
|
+
clazz = class_name == 'nil' ? NilClass : Object.const_get(class_name, false)
|
134
|
+
rescue NameError
|
135
|
+
raise Errors::ParseCustomClassNotFoundError.new(first_line, class_name)
|
136
|
+
end
|
137
|
+
raise Errors::ParseCustomClassNoCreateMethodError.new(first_line, class_name) unless clazz.respond_to? :nt_create
|
138
|
+
|
139
|
+
clazz.nt_create(hash['data'])
|
140
|
+
end
|
141
|
+
|
142
|
+
def parse_kv_dict_item(indentation, line)
|
143
|
+
key = line.attribs['key']
|
144
|
+
value = line.attribs['value']
|
145
|
+
if value.nil?
|
146
|
+
value = ''
|
147
|
+
if !@line_scanner.peek.nil? && @line_scanner.peek.indentation > indentation
|
148
|
+
value = parse_any(@line_scanner.peek.indentation)
|
93
149
|
end
|
150
|
+
end
|
151
|
+
[key, value]
|
152
|
+
end
|
94
153
|
|
95
|
-
|
154
|
+
def parse_key_item_key(indentation, line)
|
155
|
+
key = line.attribs['key']
|
156
|
+
while @line_scanner.peek&.tag == :key_item && @line_scanner.peek.indentation == indentation
|
157
|
+
line = @line_scanner.read_next
|
158
|
+
key += "\n#{line.attribs['key']}"
|
96
159
|
end
|
97
|
-
|
160
|
+
key
|
161
|
+
end
|
162
|
+
|
163
|
+
def parse_key_item_value(indentation, line)
|
164
|
+
return '' if @line_scanner.peek.nil?
|
165
|
+
|
166
|
+
exp_types = %i[dict_item key_item list_item string_item]
|
167
|
+
unless exp_types.member?(@line_scanner.peek.tag)
|
168
|
+
raise Errors::ParseLineTypeNotExpectedError.new(line, exp_types, line.tag)
|
169
|
+
end
|
170
|
+
|
171
|
+
unless @line_scanner.peek.indentation > indentation
|
172
|
+
raise Errors::ParseMultilineKeyNoValueError,
|
173
|
+
line
|
174
|
+
end
|
175
|
+
|
176
|
+
parse_any(@line_scanner.peek.indentation)
|
177
|
+
end
|
178
|
+
|
179
|
+
def parse_kv_key_item(indentation, line)
|
180
|
+
key = parse_key_item_key(indentation, line)
|
181
|
+
value = parse_key_item_value(indentation, line)
|
182
|
+
[key, value]
|
183
|
+
end
|
184
|
+
|
185
|
+
def parse_dict_key_value(line, indentation)
|
186
|
+
if line.tag == :dict_item
|
187
|
+
parse_kv_dict_item(indentation, line)
|
188
|
+
else
|
189
|
+
parse_kv_key_item(indentation, line)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def assert_dict_item_line(line, indentation)
|
194
|
+
Errors.raise_unrecognized_line(line) if line.tag == :unrecognized
|
195
|
+
raise Errors::ParseInvalidIndentationError.new(line, indentation) if line.indentation != indentation
|
196
|
+
raise Errors::ParseLineTypeExpectedDictItemError, line unless %i[dict_item key_item].include? line.tag
|
98
197
|
end
|
99
198
|
|
100
199
|
def parse_dict_item(indentation)
|
@@ -103,170 +202,35 @@ module NestedText
|
|
103
202
|
while !@line_scanner.peek.nil? && @line_scanner.peek.indentation >= indentation
|
104
203
|
line = @line_scanner.read_next
|
105
204
|
first_line = line if first_line.nil?
|
106
|
-
|
107
|
-
|
108
|
-
raise Errors::ParseLineTypeExpectedDictItemError, line unless %i[dict_item key_item].include? line.tag
|
109
|
-
|
110
|
-
value = nil
|
111
|
-
key = nil
|
112
|
-
if line.tag == :dict_item
|
113
|
-
key = line.attribs["key"]
|
114
|
-
value = line.attribs["value"]
|
115
|
-
if value.nil?
|
116
|
-
value = ""
|
117
|
-
if !@line_scanner.peek.nil? && @line_scanner.peek.indentation > indentation
|
118
|
-
value = parse_any(@line_scanner.peek.indentation)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
else # :key_item
|
122
|
-
key = line.attribs["key"]
|
123
|
-
while @line_scanner.peek&.tag == :key_item && @line_scanner.peek.indentation == indentation
|
124
|
-
line = @line_scanner.read_next
|
125
|
-
key += "\n" + line.attribs["key"]
|
126
|
-
end
|
127
|
-
exp_types = %i[dict_item key_item list_item string_item]
|
128
|
-
if @line_scanner.peek.nil?
|
129
|
-
value = ""
|
130
|
-
else
|
131
|
-
unless exp_types.member?(@line_scanner.peek.tag)
|
132
|
-
raise Errors::ParseLineTypeNotExpectedError.new(line, exp_types, line.tag)
|
133
|
-
end
|
134
|
-
raise Errors::ParseMultilineKeyNoValueError, line unless @line_scanner.peek.indentation > indentation
|
135
|
-
|
136
|
-
value = parse_any(@line_scanner.peek.indentation)
|
137
|
-
end
|
138
|
-
end
|
205
|
+
assert_dict_item_line(line, indentation)
|
206
|
+
key, value = parse_dict_key_value(line, indentation)
|
139
207
|
raise Errors::ParseDictDuplicateKeyError, line if result.key? key
|
140
208
|
|
141
209
|
result[key] = value
|
142
210
|
end
|
143
211
|
|
144
|
-
|
145
|
-
|
146
|
-
class_name = result[CUSTOM_CLASS_KEY]
|
147
|
-
begin
|
148
|
-
clazz = class_name == "nil" ? NilClass : Object.const_get(class_name, false)
|
149
|
-
rescue NameError
|
150
|
-
raise Errors::ParseCustomClassNotFoundError.new(first_line, class_name)
|
151
|
-
end
|
152
|
-
if clazz.respond_to? :nt_create
|
153
|
-
result = clazz.nt_create(result["data"])
|
154
|
-
else
|
155
|
-
raise Errors::ParseCustomClassNoCreateMethodError.new(first_line, class_name)
|
156
|
-
end
|
157
|
-
end
|
212
|
+
deserialize_custom_class(result, first_line)
|
213
|
+
end
|
158
214
|
|
159
|
-
|
215
|
+
def assert_string_line(line, indentation)
|
216
|
+
raise Errors::ParseInvalidIndentationError.new(line, indentation) if line.indentation != indentation
|
217
|
+
raise Errors::ParseLineTypeNotExpectedError.new(line, %i[string_item], line.tag) unless line.tag == :string_item
|
160
218
|
end
|
161
219
|
|
162
220
|
def parse_string_item(indentation)
|
163
221
|
result = []
|
164
222
|
while !@line_scanner.peek.nil? && @line_scanner.peek.indentation >= indentation
|
165
223
|
line = @line_scanner.read_next
|
166
|
-
|
167
|
-
raise Errors::ParseLineTypeNotExpectedError.new(line, %i[string_item], line.tag) unless line.tag == :string_item
|
224
|
+
assert_string_line(line, indentation)
|
168
225
|
|
169
|
-
value = line.attribs[
|
226
|
+
value = line.attribs['value']
|
170
227
|
result << value
|
171
228
|
end
|
172
229
|
result.join("\n")
|
173
230
|
end
|
174
231
|
|
175
|
-
def parse_inline_key
|
176
|
-
key = []
|
177
|
-
until @inline_scanner.empty? || [":", "{", "}", "[", "]", ","].include?(@inline_scanner.peek)
|
178
|
-
key << @inline_scanner.read_next
|
179
|
-
end
|
180
|
-
if @inline_scanner.empty?
|
181
|
-
raise Errors::ParseInlineNoClosingDelimiterError.new(@inline_scanner.line,
|
182
|
-
@inline_scanner.pos)
|
183
|
-
end
|
184
|
-
|
185
|
-
last_char = @inline_scanner.read_next
|
186
|
-
if last_char == "}" && key.empty?
|
187
|
-
raise Errors::ParseInlineMissingValueError.new(@inline_scanner.line, @inline_scanner.pos - 1)
|
188
|
-
end
|
189
|
-
unless last_char == ":"
|
190
|
-
raise Errors::ParseInlineDictKeySyntaxError.new(@inline_scanner.line, @inline_scanner.pos - 1, last_char)
|
191
|
-
end
|
192
|
-
|
193
|
-
key.join.strip
|
194
|
-
end
|
195
|
-
|
196
|
-
def parse_inline
|
197
|
-
return nil if @inline_scanner.peek.nil?
|
198
|
-
|
199
|
-
result = nil
|
200
|
-
# Trim leading whitespaces
|
201
|
-
@inline_scanner.read_next while !@inline_scanner.empty? && [" ", "\t"].include?(@inline_scanner.peek)
|
202
|
-
case @inline_scanner.peek
|
203
|
-
when "{"
|
204
|
-
result = {}
|
205
|
-
first = true
|
206
|
-
loop do
|
207
|
-
@inline_scanner.read_next
|
208
|
-
break if first && @inline_scanner.peek == "}"
|
209
|
-
|
210
|
-
first = false
|
211
|
-
key = parse_inline_key
|
212
|
-
value = parse_inline
|
213
|
-
result[key] = value
|
214
|
-
break unless @inline_scanner.peek == ","
|
215
|
-
end
|
216
|
-
if @inline_scanner.empty?
|
217
|
-
raise Errors::ParseInlineNoClosingDelimiterError.new(@inline_scanner.line,
|
218
|
-
@inline_scanner.pos)
|
219
|
-
end
|
220
|
-
last_char = @inline_scanner.read_next
|
221
|
-
unless last_char == "}"
|
222
|
-
raise Errors::ParseInlineDictSyntaxError.new(@inline_scanner.line, @inline_scanner.pos - 1,
|
223
|
-
last_char)
|
224
|
-
end
|
225
|
-
|
226
|
-
when "["
|
227
|
-
result = []
|
228
|
-
first = true # TODO: can be replaced by checking result.empty? below?
|
229
|
-
loop do
|
230
|
-
@inline_scanner.read_next
|
231
|
-
break if first && @inline_scanner.peek == "]"
|
232
|
-
|
233
|
-
first = false
|
234
|
-
result << parse_inline
|
235
|
-
break unless @inline_scanner.peek == ","
|
236
|
-
end
|
237
|
-
if @inline_scanner.empty?
|
238
|
-
raise Errors::ParseInlineNoClosingDelimiterError.new(@inline_scanner.line,
|
239
|
-
@inline_scanner.pos)
|
240
|
-
end
|
241
|
-
last_char = @inline_scanner.read_next
|
242
|
-
|
243
|
-
if last_char != "]"
|
244
|
-
if result[-1] == ""
|
245
|
-
raise Errors::ParseInlineMissingValueError.new(@inline_scanner.line, @inline_scanner.pos - 1)
|
246
|
-
else
|
247
|
-
raise Errors::ParseInlineListSyntaxError.new(@inline_scanner.line, @inline_scanner.pos - 1,
|
248
|
-
last_char)
|
249
|
-
end
|
250
|
-
end
|
251
|
-
else # Inline string
|
252
|
-
inline_string = []
|
253
|
-
until @inline_scanner.empty? || ["{", "}", "[", "]", ","].include?(@inline_scanner.peek)
|
254
|
-
inline_string << @inline_scanner.read_next
|
255
|
-
end
|
256
|
-
result = inline_string.join.rstrip # Trim trailing whitespaces that lead up to next break point.
|
257
|
-
end
|
258
|
-
# Trim trailing whitespaces
|
259
|
-
@inline_scanner.read_next while !@inline_scanner.empty? && [" ", "\t"].include?(@inline_scanner.peek)
|
260
|
-
result
|
261
|
-
end
|
262
|
-
|
263
232
|
def parse_inline_dict
|
264
|
-
|
265
|
-
result = parse_inline
|
266
|
-
unless @inline_scanner.empty?
|
267
|
-
raise Errors::ParseInlineExtraCharactersAfterDelimiterError.new(@inline_scanner.line, @inline_scanner.pos,
|
268
|
-
@inline_scanner.remaining)
|
269
|
-
end
|
233
|
+
result = InlineParser.new(@line_scanner.read_next).parse
|
270
234
|
unless result.is_a? Hash
|
271
235
|
raise Errors::AssertionError,
|
272
236
|
"Expected inline value to be Hash but is #{result.class.name}"
|
@@ -276,12 +240,7 @@ module NestedText
|
|
276
240
|
end
|
277
241
|
|
278
242
|
def parse_inline_list
|
279
|
-
|
280
|
-
result = parse_inline
|
281
|
-
unless @inline_scanner.empty?
|
282
|
-
raise Errors::ParseInlineExtraCharactersAfterDelimiterError.new(@inline_scanner.line, @inline_scanner.pos,
|
283
|
-
@inline_scanner.remaining)
|
284
|
-
end
|
243
|
+
result = InlineParser.new(@line_scanner.read_next).parse
|
285
244
|
unless result.is_a? Array
|
286
245
|
raise Errors::AssertionError,
|
287
246
|
"Expected inline value to be Array but is #{result.class.name}"
|
data/lib/nestedtext/scanners.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'nestedtext/errors_internal'
|
4
4
|
|
5
5
|
module NestedText
|
6
6
|
class LineScanner
|
@@ -83,7 +83,7 @@ module NestedText
|
|
83
83
|
:inline_dict, # {key1: value1, key2: value2}
|
84
84
|
:inline_list, # [value1, value2]
|
85
85
|
:unrecognized # could not be determined
|
86
|
-
]
|
86
|
+
].freeze
|
87
87
|
|
88
88
|
attr_accessor :prev
|
89
89
|
attr_reader :tag, :content, :indentation, :attribs, :lineno
|
@@ -112,12 +112,11 @@ module NestedText
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def to_s
|
115
|
-
"[##{@lineno}] #{
|
115
|
+
"[##{@lineno}] #{' ' * @indentation}#{@content}"
|
116
116
|
end
|
117
117
|
|
118
118
|
private
|
119
119
|
|
120
|
-
# TODO: this regex must unit tested.
|
121
120
|
PATTERN_DICT_ITEM = /^
|
122
121
|
(?<key>[^\s].*?) # Key must start with a non-whitespace character, and goes until first
|
123
122
|
\s*: # first optional space, or :-separator
|
@@ -127,37 +126,44 @@ module NestedText
|
|
127
126
|
)?
|
128
127
|
$/x
|
129
128
|
|
130
|
-
def
|
131
|
-
@indentation += 1 while @indentation < @content.length && @content[@indentation] ==
|
129
|
+
def fast_forward_indentation
|
130
|
+
@indentation += 1 while @indentation < @content.length && @content[@indentation] == ' '
|
132
131
|
@content = @content[@indentation..]
|
132
|
+
end
|
133
133
|
|
134
|
-
|
134
|
+
def detect_line_tag
|
135
|
+
if @content.length.zero?
|
135
136
|
self.tag = :blank
|
136
|
-
elsif @content[0] ==
|
137
|
+
elsif @content[0] == '#'
|
137
138
|
self.tag = :comment
|
138
139
|
elsif @content =~ /^:(?: |$)/
|
139
140
|
self.tag = :key_item
|
140
|
-
@attribs[
|
141
|
+
@attribs['key'] = @content[2..] || ''
|
141
142
|
elsif @content =~ /^-(?: |$)/
|
142
143
|
self.tag = :list_item
|
143
|
-
@attribs[
|
144
|
+
@attribs['value'] = @content[2..]
|
144
145
|
elsif @content =~ /^>(?: |$)/
|
145
146
|
self.tag = :string_item
|
146
|
-
@attribs[
|
147
|
-
elsif @content[0] ==
|
147
|
+
@attribs['value'] = @content[2..] || ''
|
148
|
+
elsif @content[0] == '{'
|
148
149
|
self.tag = :inline_dict
|
149
|
-
elsif @content[0] ==
|
150
|
+
elsif @content[0] == '['
|
150
151
|
self.tag = :inline_list
|
151
152
|
elsif @content =~ PATTERN_DICT_ITEM
|
152
153
|
self.tag = :dict_item
|
153
|
-
@attribs[
|
154
|
-
@attribs[
|
154
|
+
@attribs['key'] = Regexp.last_match(:key)
|
155
|
+
@attribs['value'] = Regexp.last_match(:value)
|
155
156
|
else
|
156
157
|
# Don't raise error here, as this line might not have been consumed yet,
|
157
158
|
# thus could hide an error that we detect when parsing the previous line.
|
158
159
|
self.tag = :unrecognized
|
159
160
|
end
|
160
161
|
end
|
162
|
+
|
163
|
+
def detect_line_tag_and_indentation
|
164
|
+
fast_forward_indentation
|
165
|
+
detect_line_tag
|
166
|
+
end
|
161
167
|
end
|
162
168
|
private_constant :Line
|
163
169
|
end
|
data/lib/nestedtext/version.rb
CHANGED
data/lib/nestedtext.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
3
|
+
require_relative 'nestedtext/core_ext'
|
4
|
+
require_relative 'nestedtext/decode'
|
5
|
+
require_relative 'nestedtext/encode'
|
6
|
+
require_relative 'nestedtext/encode_helpers'
|
7
|
+
require_relative 'nestedtext/error'
|
8
|
+
require_relative 'nestedtext/version'
|
9
9
|
|
10
10
|
##
|
11
11
|
# # NestedText
|
data/nestedtext.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'lib/nestedtext/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'nestedtext'
|
7
7
|
spec.version = NestedText::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['Erik Westrup']
|
9
|
+
spec.email = ['erik.westrup@gmail.com']
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
15
|
-
spec.required_ruby_version = [
|
11
|
+
spec.summary = 'A ruby library for the human friendly data format NestedText (https://nestedtext.org/)'
|
12
|
+
spec.description = 'A ruby library for the human friendly data format NestedText (https://nestedtext.org/). There is support for decoding a NestedText file or string to Ruby data structures, as well as encoding Ruby objects to a NestedText file or string. Furthermore there is support for serialization and deserialization of custom classes. Support for v3.2.1 of the data format will all official tests passing.'
|
13
|
+
spec.homepage = 'https://github.com/erikw/nestedtext-ruby/'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = ['>= 3.0', '< 4']
|
16
16
|
|
17
|
-
spec.metadata[
|
18
|
-
spec.metadata[
|
19
|
-
spec.metadata[
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/erikw/nestedtext-ruby/'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/erikw/nestedtext-ruby/blob/main/CHANGELOG.md'
|
20
20
|
|
21
21
|
# For push to GitHub packages to work.
|
22
22
|
# Reference: https://github.community/t/unable-to-push-rubygem-to-package-registry-the-expected-resource-was-not-found/14596/7
|
23
23
|
spec.metadata = {
|
24
|
-
|
24
|
+
'github_repo' => 'git@github.com:erikw/nestedtext-ruby.git',
|
25
|
+
'rubygems_mfa_required' => 'true'
|
25
26
|
}
|
26
27
|
|
27
28
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
@@ -29,8 +30,9 @@ Gem::Specification.new do |spec|
|
|
29
30
|
f.match(%r{\A(?:lib/|CHANGELOG.md|CONTRIBUTING.md|LICENSE.txt|README.md|SECURITY.md|nestedtext.gemspec)})
|
30
31
|
end
|
31
32
|
end
|
32
|
-
spec.require_paths = [
|
33
|
+
spec.require_paths = ['lib']
|
33
34
|
|
34
|
-
spec.add_runtime_dependency
|
35
|
-
spec.add_runtime_dependency
|
35
|
+
spec.add_runtime_dependency 'unicode_utils', '~> 1.4'
|
36
|
+
spec.add_runtime_dependency 'warning', '~> 1.2'
|
37
|
+
spec.add_runtime_dependency 'word_wrap', '~> 1.0'
|
36
38
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nestedtext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik Westrup
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: unicode_utils
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: warning
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,6 +78,7 @@ files:
|
|
64
78
|
- lib/nestedtext/encode_helpers.rb
|
65
79
|
- lib/nestedtext/error.rb
|
66
80
|
- lib/nestedtext/errors_internal.rb
|
81
|
+
- lib/nestedtext/inline_parser.rb
|
67
82
|
- lib/nestedtext/parser.rb
|
68
83
|
- lib/nestedtext/scanners.rb
|
69
84
|
- lib/nestedtext/version.rb
|
@@ -73,6 +88,7 @@ licenses:
|
|
73
88
|
- MIT
|
74
89
|
metadata:
|
75
90
|
github_repo: git@github.com:erikw/nestedtext-ruby.git
|
91
|
+
rubygems_mfa_required: 'true'
|
76
92
|
post_install_message:
|
77
93
|
rdoc_options: []
|
78
94
|
require_paths:
|