psych-pure 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -2
- data/README.md +1 -1
- data/lib/psych/pure/version.rb +1 -1
- data/lib/psych/pure.rb +371 -242
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d126f1c02cd1f7eba6b74a19f76dc938a6c828dc8dfea96a5663b3b4c8ac5bac
|
4
|
+
data.tar.gz: 6df58931a63090a6248a44d94986057fa60faa5c6ec2015b832da7a4d1fefe7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00f44a5e4889ac4674d844f5dbfbff8a39fb1126f40b7135a33165539a3e6d27d5d5303b0289259471ba3d8c3dfd7e58e75f2fa8c5339258239a89b58e314c2d
|
7
|
+
data.tar.gz: feaacc73c47c767edf1f454de1ecf4db4c812557f10578d88f715508b1975c524edc9acf5c21b40f77c40e1fc9eb25e7e79ee6496d8322cfb49e0030d457be3c
|
data/CHANGELOG.md
CHANGED
@@ -6,11 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
-
## [0.1.
|
9
|
+
## [0.1.1] - 2025-02-13
|
10
|
+
|
11
|
+
- Fix up comment handling to preserve within hashes.
|
12
|
+
- Do not duplicate comments when the parser backtracks.
|
13
|
+
- Trim locations of sequences and mappings before trailing comments.
|
14
|
+
|
15
|
+
## [0.1.0] - 2025-02-12
|
10
16
|
|
11
17
|
### Added
|
12
18
|
|
13
19
|
- 🎉 Initial release. 🎉
|
14
20
|
|
15
|
-
[unreleased]: https://github.com/kddnewton/psych-pure/compare/v0.1.
|
21
|
+
[unreleased]: https://github.com/kddnewton/psych-pure/compare/v0.1.1...HEAD
|
22
|
+
[0.1.1]: https://github.com/kddnewton/psych-pure/compare/v0.1.0...v0.1.1
|
16
23
|
[0.1.0]: https://github.com/kddnewton/psych-pure/compare/24de62...v0.1.0
|
data/README.md
CHANGED
@@ -41,7 +41,7 @@ Or install it yourself as:
|
|
41
41
|
|
42
42
|
All of the various `parse` APIs come with the additional `comments:` keyword option. This option tells the parser to parse out comments and attach them to the resulting tree. Nodes in the tree are then responsible for maintaining their own leading and trailing comments.
|
43
43
|
|
44
|
-
All of the various `load` APIs also come with the additional `comments:` keyword option. This also gets fed into the parser. Because `load` is responsible for loading Ruby objects, the comments are then attached to the loaded objects via
|
44
|
+
All of the various `load` APIs also come with the additional `comments:` keyword option. This also gets fed into the parser. Because `load` is responsible for loading Ruby objects, the comments are then attached to the loaded objects via delegators that wraps the objects and stores the leading and trailing comments. Those objects are then taken into account in the various `dump` APIs to dump out the comments as well. For example:
|
45
45
|
|
46
46
|
```ruby
|
47
47
|
result = Psych::Pure.load("- a # comment1\n- c # comment2\n", comments: true)
|
data/lib/psych/pure/version.rb
CHANGED
data/lib/psych/pure.rb
CHANGED
@@ -9,33 +9,185 @@ require "stringio"
|
|
9
9
|
module Psych
|
10
10
|
# A YAML parser written in Ruby.
|
11
11
|
module Pure
|
12
|
+
# An internal exception is an exception that should not have occurred. It is
|
13
|
+
# effectively an assertion.
|
14
|
+
class InternalException < Exception
|
15
|
+
def initialize(message = "An internal exception occurred")
|
16
|
+
super(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# A source is wraps the input string and provides methods to access line and
|
21
|
+
# column information from a byte offset.
|
22
|
+
class Source
|
23
|
+
def initialize(string)
|
24
|
+
@line_offsets = []
|
25
|
+
@trimmable_lines = []
|
26
|
+
|
27
|
+
offset = 0
|
28
|
+
string.each_line do |line|
|
29
|
+
@line_offsets << offset
|
30
|
+
@trimmable_lines << line.match?(/\A(?: *#.*)?\n\z/)
|
31
|
+
offset += line.bytesize
|
32
|
+
end
|
33
|
+
|
34
|
+
@line_offsets << offset
|
35
|
+
@trimmable_lines << true
|
36
|
+
end
|
37
|
+
|
38
|
+
def trim(offset)
|
39
|
+
while (l = line(offset)) != 0 && (offset == @line_offsets[l]) && @trimmable_lines[l - 1]
|
40
|
+
offset = @line_offsets[l - 1]
|
41
|
+
end
|
42
|
+
|
43
|
+
offset
|
44
|
+
end
|
45
|
+
|
46
|
+
def line(offset)
|
47
|
+
index = @line_offsets.bsearch_index { |line_offset| line_offset > offset }
|
48
|
+
return @line_offsets.size - 1 if index.nil?
|
49
|
+
index - 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def column(offset)
|
53
|
+
offset - @line_offsets[line(offset)]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# A location represents a range of bytes in the input string.
|
58
|
+
class Location
|
59
|
+
protected attr_reader :pos_end
|
60
|
+
|
61
|
+
def initialize(source, pos_start, pos_end)
|
62
|
+
@source = source
|
63
|
+
@pos_start = pos_start
|
64
|
+
@pos_end = pos_end
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_line
|
68
|
+
@source.line(@pos_start)
|
69
|
+
end
|
70
|
+
|
71
|
+
def start_column
|
72
|
+
@source.column(@pos_start)
|
73
|
+
end
|
74
|
+
|
75
|
+
def end_line
|
76
|
+
@source.line(@pos_end)
|
77
|
+
end
|
78
|
+
|
79
|
+
def end_column
|
80
|
+
@source.column(@pos_end)
|
81
|
+
end
|
82
|
+
|
83
|
+
def join(other)
|
84
|
+
@pos_end = other.pos_end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Trim trailing whitespace and comments from this location.
|
88
|
+
def trim
|
89
|
+
Location.new(@source, @pos_start, @source.trim(@pos_end))
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_a
|
93
|
+
[start_line, start_column, end_line, end_column]
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.point(source, pos)
|
97
|
+
new(source, pos, pos)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
12
101
|
# Represents a comment in the input.
|
13
102
|
class Comment
|
14
|
-
attr_reader :
|
103
|
+
attr_reader :location, :value
|
15
104
|
|
16
|
-
def initialize(
|
105
|
+
def initialize(location, value, inline)
|
106
|
+
@location = location
|
17
107
|
@value = value
|
18
|
-
@start_line = start_line
|
19
|
-
@start_column = start_column
|
20
|
-
@end_line = end_line
|
21
|
-
@end_column = end_column
|
22
108
|
@inline = inline
|
23
109
|
end
|
24
110
|
|
25
111
|
def inline?
|
26
112
|
@inline
|
27
113
|
end
|
114
|
+
|
115
|
+
def start_line
|
116
|
+
location.start_line
|
117
|
+
end
|
118
|
+
|
119
|
+
def start_column
|
120
|
+
location.start_column
|
121
|
+
end
|
122
|
+
|
123
|
+
def end_line
|
124
|
+
location.end_line
|
125
|
+
end
|
126
|
+
|
127
|
+
def end_column
|
128
|
+
location.end_column
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Represents the set of comments on a node.
|
133
|
+
class Comments
|
134
|
+
attr_reader :leading, :trailing
|
135
|
+
|
136
|
+
def initialize
|
137
|
+
@leading = []
|
138
|
+
@trailing = []
|
139
|
+
end
|
140
|
+
|
141
|
+
def leading_comment(comment)
|
142
|
+
@leading << comment
|
143
|
+
end
|
144
|
+
|
145
|
+
def trailing_comment(comment)
|
146
|
+
@trailing << comment
|
147
|
+
end
|
28
148
|
end
|
29
149
|
|
30
|
-
# Wraps a Ruby object with its
|
31
|
-
|
32
|
-
|
33
|
-
attr_reader :yaml_leading_comments, :yaml_trailing_comments
|
150
|
+
# Wraps a Ruby object with its comments from the source input.
|
151
|
+
class CommentsObject < SimpleDelegator
|
152
|
+
attr_reader :psych_comments
|
34
153
|
|
35
|
-
def initialize(object,
|
154
|
+
def initialize(object, psych_comments)
|
155
|
+
@psych_comments = psych_comments
|
36
156
|
super(object)
|
37
|
-
|
38
|
-
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Wraps a Ruby hash with its comments from the source input.
|
161
|
+
class CommentsHash < SimpleDelegator
|
162
|
+
attr_reader :psych_comments, :psych_key_comments
|
163
|
+
|
164
|
+
def initialize(object, psych_comments, psych_key_comments = {})
|
165
|
+
@psych_comments = psych_comments
|
166
|
+
@psych_key_comments = psych_key_comments
|
167
|
+
commentless = {}
|
168
|
+
|
169
|
+
object.each do |key, value|
|
170
|
+
if key.is_a?(CommentsObject)
|
171
|
+
@psych_key_comments[key.__getobj__] = key.psych_comments
|
172
|
+
commentless[key.__getobj__] = value
|
173
|
+
else
|
174
|
+
commentless[key] = value
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
super(commentless)
|
179
|
+
end
|
180
|
+
|
181
|
+
def []=(key, value)
|
182
|
+
if (previous = self[key])
|
183
|
+
if previous.is_a?(CommentsObject)
|
184
|
+
value = CommentsObject.new(value, previous.psych_comments)
|
185
|
+
elsif previous.is_a?(CommentsHash)
|
186
|
+
value = CommentsHash.new(value, previous.psych_comments, previous.psych_key_comments)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
super(key, value)
|
39
191
|
end
|
40
192
|
end
|
41
193
|
|
@@ -45,7 +197,7 @@ module Psych
|
|
45
197
|
# Extend the Handler to be able to handle comments coming out of the
|
46
198
|
# parser.
|
47
199
|
module Handler
|
48
|
-
def comment(value
|
200
|
+
def comment(value)
|
49
201
|
end
|
50
202
|
end
|
51
203
|
|
@@ -55,8 +207,8 @@ module Psych
|
|
55
207
|
@comments ||= []
|
56
208
|
end
|
57
209
|
|
58
|
-
def comment(value
|
59
|
-
comments <<
|
210
|
+
def comment(value)
|
211
|
+
comments << value
|
60
212
|
end
|
61
213
|
|
62
214
|
def end_stream
|
@@ -96,10 +248,11 @@ module Psych
|
|
96
248
|
preceding = nil
|
97
249
|
following = nil
|
98
250
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
251
|
+
location = comment.location
|
252
|
+
comment_start_line = location.start_line
|
253
|
+
comment_start_column = location.start_column
|
254
|
+
comment_end_line = location.end_line
|
255
|
+
comment_end_column = location.end_column
|
103
256
|
|
104
257
|
left = 0
|
105
258
|
right = candidates.length
|
@@ -159,46 +312,60 @@ module Psych
|
|
159
312
|
|
160
313
|
# Extend the nodes to be able to store comments.
|
161
314
|
module Node
|
162
|
-
def leading_comments
|
163
|
-
@leading_comments ||= []
|
164
|
-
end
|
165
|
-
|
166
315
|
def leading_comment(comment)
|
167
|
-
|
316
|
+
comments.leading_comment(comment)
|
168
317
|
end
|
169
318
|
|
170
|
-
def
|
171
|
-
|
319
|
+
def trailing_comment(comment)
|
320
|
+
comments.trailing_comment(comment)
|
172
321
|
end
|
173
322
|
|
174
|
-
def
|
175
|
-
|
323
|
+
def comments
|
324
|
+
@comments ||= Comments.new
|
176
325
|
end
|
177
326
|
|
178
327
|
def comments?
|
179
|
-
|
180
|
-
|
328
|
+
defined?(@comments)
|
329
|
+
end
|
330
|
+
|
331
|
+
def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, comments: false)
|
332
|
+
Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, comments: comments).accept(self)
|
181
333
|
end
|
182
334
|
end
|
183
335
|
|
184
336
|
# Extend the ToRuby visitor to be able to attach comments to the resulting
|
185
337
|
# Ruby objects.
|
186
338
|
module ToRuby
|
339
|
+
attr_reader :comments
|
340
|
+
|
341
|
+
def initialize(ss, class_loader, symbolize_names: false, freeze: false, comments: false)
|
342
|
+
super(ss, class_loader, symbolize_names: symbolize_names, freeze: freeze)
|
343
|
+
@comments = comments
|
344
|
+
end
|
345
|
+
|
187
346
|
def accept(node)
|
188
347
|
result = super
|
189
348
|
|
190
|
-
if
|
191
|
-
result
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
)
|
349
|
+
if @comments
|
350
|
+
if result.is_a?(Hash)
|
351
|
+
result = CommentsHash.new(result, node.comments? ? node.comments : nil)
|
352
|
+
elsif node.comments?
|
353
|
+
result = CommentsObject.new(result, node.comments)
|
354
|
+
end
|
197
355
|
end
|
198
356
|
|
199
357
|
result
|
200
358
|
end
|
201
359
|
end
|
360
|
+
|
361
|
+
# Extend the ToRuby singleton to be able to pass the comments option.
|
362
|
+
module ToRubySingleton
|
363
|
+
def create(symbolize_names: false, freeze: false, strict_integer: false, comments: false)
|
364
|
+
class_loader = ClassLoader.new
|
365
|
+
scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer)
|
366
|
+
new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze, comments: comments)
|
367
|
+
end
|
368
|
+
end
|
202
369
|
end
|
203
370
|
|
204
371
|
::Psych::Handler.prepend(CommentExtensions::Handler)
|
@@ -206,68 +373,7 @@ module Psych
|
|
206
373
|
::Psych::Handlers::DocumentStream.prepend(CommentExtensions::DocumentStream)
|
207
374
|
::Psych::Nodes::Node.prepend(CommentExtensions::Node)
|
208
375
|
::Psych::Visitors::ToRuby.prepend(CommentExtensions::ToRuby)
|
209
|
-
|
210
|
-
# An internal exception is an exception that should not have occurred. It is
|
211
|
-
# effectively an assertion.
|
212
|
-
class InternalException < Exception
|
213
|
-
def initialize(message = "An internal exception occurred")
|
214
|
-
super(message)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# A source is wraps the input string and provides methods to access line and
|
219
|
-
# column information from a byte offset.
|
220
|
-
class Source
|
221
|
-
def initialize(string)
|
222
|
-
@line_offsets = []
|
223
|
-
|
224
|
-
offset = 0
|
225
|
-
string.each_line do |line|
|
226
|
-
@line_offsets << offset
|
227
|
-
offset += line.bytesize
|
228
|
-
end
|
229
|
-
|
230
|
-
@line_offsets << offset
|
231
|
-
end
|
232
|
-
|
233
|
-
def line(offset)
|
234
|
-
index = @line_offsets.bsearch_index { |line_offset| line_offset > offset }
|
235
|
-
return @line_offsets.size - 1 if index.nil?
|
236
|
-
index - 1
|
237
|
-
end
|
238
|
-
|
239
|
-
def column(offset)
|
240
|
-
offset - @line_offsets[line(offset)]
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
# A location represents a range of bytes in the input string.
|
245
|
-
class Location
|
246
|
-
protected attr_reader :pos_end
|
247
|
-
|
248
|
-
def initialize(source, pos_start, pos_end)
|
249
|
-
@source = source
|
250
|
-
@pos_start = pos_start
|
251
|
-
@pos_end = pos_end
|
252
|
-
end
|
253
|
-
|
254
|
-
def join(other)
|
255
|
-
@pos_end = other.pos_end
|
256
|
-
end
|
257
|
-
|
258
|
-
def to_a
|
259
|
-
[
|
260
|
-
@source.line(@pos_start),
|
261
|
-
@source.column(@pos_start),
|
262
|
-
@source.line(@pos_end),
|
263
|
-
@source.column(@pos_end)
|
264
|
-
]
|
265
|
-
end
|
266
|
-
|
267
|
-
def self.point(source, pos)
|
268
|
-
new(source, pos, pos)
|
269
|
-
end
|
270
|
-
end
|
376
|
+
::Psych::Visitors::ToRuby.singleton_class.prepend(CommentExtensions::ToRubySingleton)
|
271
377
|
|
272
378
|
# An alias event represents a reference to a previously defined anchor.
|
273
379
|
class Alias
|
@@ -349,7 +455,7 @@ module Psych
|
|
349
455
|
end
|
350
456
|
|
351
457
|
def accept(handler)
|
352
|
-
handler.event_location(*@location)
|
458
|
+
handler.event_location(*@location.trim)
|
353
459
|
handler.end_mapping
|
354
460
|
end
|
355
461
|
end
|
@@ -409,7 +515,7 @@ module Psych
|
|
409
515
|
end
|
410
516
|
|
411
517
|
def accept(handler)
|
412
|
-
handler.event_location(*@location)
|
518
|
+
handler.event_location(*@location.trim)
|
413
519
|
handler.end_sequence
|
414
520
|
end
|
415
521
|
end
|
@@ -489,7 +595,7 @@ module Psych
|
|
489
595
|
|
490
596
|
# This parser can optionally parse comments and attach them to the
|
491
597
|
# resulting tree, if the option is passed.
|
492
|
-
@comments =
|
598
|
+
@comments = nil
|
493
599
|
end
|
494
600
|
|
495
601
|
# Top-level parse function that starts the parsing process.
|
@@ -512,10 +618,12 @@ module Psych
|
|
512
618
|
@scanner = StringScanner.new(yaml)
|
513
619
|
@filename = filename
|
514
620
|
@source = Source.new(yaml)
|
515
|
-
@comments = comments
|
621
|
+
@comments = {} if comments
|
516
622
|
|
517
623
|
raise_syntax_error("Parser failed to complete") unless parse_l_yaml_stream
|
518
624
|
raise_syntax_error("Parser finished before end of input") unless @scanner.eos?
|
625
|
+
|
626
|
+
@comments = nil if comments
|
519
627
|
true
|
520
628
|
end
|
521
629
|
|
@@ -621,10 +729,18 @@ module Psych
|
|
621
729
|
# :section: Event handling
|
622
730
|
# ------------------------------------------------------------------------
|
623
731
|
|
732
|
+
# Flush the comments into the handler once we get to a safe point.
|
733
|
+
def comments_flush
|
734
|
+
return unless @comments
|
735
|
+
@comments.each { |_, comment| @handler.comment(comment) }
|
736
|
+
@comments.clear
|
737
|
+
end
|
738
|
+
|
624
739
|
# If there is a document end event, then flush it to the list of events
|
625
740
|
# and reset back to the starting state to parse the next document.
|
626
741
|
def document_end_event_flush
|
627
742
|
if @document_end_event
|
743
|
+
comments_flush
|
628
744
|
@document_end_event.accept(@handler)
|
629
745
|
@document_start_event = DocumentStart.new(Location.point(@source, @scanner.pos))
|
630
746
|
@tag_directives = @document_start_event.tag_directives
|
@@ -985,7 +1101,7 @@ module Psych
|
|
985
1101
|
pos = @scanner.pos - 1
|
986
1102
|
star { parse_nb_char }
|
987
1103
|
|
988
|
-
@
|
1104
|
+
@comments[pos] ||= Comment.new(Location.new(@source, pos, @scanner.pos), from(pos), inline) if @comments
|
989
1105
|
true
|
990
1106
|
end
|
991
1107
|
|
@@ -2623,7 +2739,7 @@ module Psych
|
|
2623
2739
|
|
2624
2740
|
if try { plus { try { parse_s_indent(n + m) && parse_ns_l_block_map_entry(n + m) } } }
|
2625
2741
|
events_cache_flush
|
2626
|
-
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
|
2742
|
+
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos))) # TODO
|
2627
2743
|
true
|
2628
2744
|
else
|
2629
2745
|
events_cache_pop
|
@@ -2739,7 +2855,7 @@ module Psych
|
|
2739
2855
|
star { try { parse_s_indent(n) && parse_ns_l_block_map_entry(n) } }
|
2740
2856
|
} then
|
2741
2857
|
events_cache_flush
|
2742
|
-
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
|
2858
|
+
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos))) # TODO
|
2743
2859
|
true
|
2744
2860
|
else
|
2745
2861
|
events_cache_pop
|
@@ -2989,12 +3105,11 @@ module Psych
|
|
2989
3105
|
# aliases, since we may find that we need to add an anchor after the
|
2990
3106
|
# object has already been flushed.
|
2991
3107
|
class Node
|
2992
|
-
attr_reader :value, :
|
3108
|
+
attr_reader :value, :comments
|
2993
3109
|
|
2994
|
-
def initialize(value,
|
3110
|
+
def initialize(value, comments)
|
2995
3111
|
@value = value
|
2996
|
-
@
|
2997
|
-
@trailing_comments = trailing_comments
|
3112
|
+
@comments = comments
|
2998
3113
|
@anchor = nil
|
2999
3114
|
end
|
3000
3115
|
|
@@ -3235,22 +3350,48 @@ module Psych
|
|
3235
3350
|
# Print out the leading and trailing comments of a node, as well as
|
3236
3351
|
# yielding the value of the node to the block.
|
3237
3352
|
def with_comments(node)
|
3238
|
-
node.
|
3239
|
-
|
3353
|
+
if (comments = node.comments) && (leading = comments.leading).any?
|
3354
|
+
line = nil
|
3355
|
+
|
3356
|
+
leading.each do |comment|
|
3357
|
+
while line && line < comment.start_line
|
3358
|
+
@q.breakable
|
3359
|
+
line += 1
|
3360
|
+
end
|
3361
|
+
|
3362
|
+
@q.text(comment.value)
|
3363
|
+
line = comment.end_line
|
3364
|
+
end
|
3365
|
+
|
3240
3366
|
@q.breakable
|
3241
3367
|
end
|
3242
3368
|
|
3243
3369
|
yield node.value
|
3244
3370
|
|
3245
|
-
if (
|
3246
|
-
|
3247
|
-
|
3371
|
+
if comments && (trailing = comments.trailing).any?
|
3372
|
+
line = nil
|
3373
|
+
index = 0
|
3374
|
+
|
3375
|
+
if trailing[0].inline?
|
3376
|
+
inline_comment = trailing[0]
|
3377
|
+
index += 1
|
3378
|
+
|
3248
3379
|
@q.trailer { @q.text(" "); @q.text(inline_comment.value) }
|
3380
|
+
line = inline_comment.end_line
|
3249
3381
|
end
|
3250
3382
|
|
3251
|
-
|
3252
|
-
|
3383
|
+
trailing[index..-1].each do |comment|
|
3384
|
+
if line.nil?
|
3385
|
+
@q.breakable
|
3386
|
+
else
|
3387
|
+
while line < comment.start_line
|
3388
|
+
@q.breakable
|
3389
|
+
line += 1
|
3390
|
+
end
|
3391
|
+
end
|
3392
|
+
|
3253
3393
|
@q.text(comment.value)
|
3394
|
+
line = comment.end_line
|
3254
3395
|
end
|
3255
3396
|
end
|
3256
3397
|
end
|
@@ -3325,59 +3466,39 @@ module Psych
|
|
3325
3466
|
private
|
3326
3467
|
|
3327
3468
|
# Walk through the given object and convert it into a tree of nodes.
|
3328
|
-
def dump(base_object)
|
3469
|
+
def dump(base_object, comments = nil)
|
3329
3470
|
object = base_object
|
3330
|
-
leading_comments = []
|
3331
|
-
trailing_comments = []
|
3332
3471
|
|
3333
|
-
if base_object.is_a?(
|
3472
|
+
if base_object.is_a?(CommentsObject) || base_object.is_a?(CommentsHash)
|
3334
3473
|
object = base_object.__getobj__
|
3335
|
-
|
3336
|
-
trailing_comments.concat(base_object.yaml_trailing_comments)
|
3474
|
+
comments = base_object.psych_comments
|
3337
3475
|
end
|
3338
3476
|
|
3339
3477
|
if object.nil?
|
3340
|
-
NilNode.new(object,
|
3478
|
+
NilNode.new(object, comments)
|
3341
3479
|
elsif @object_nodes.key?(object)
|
3342
|
-
AliasNode.new(
|
3343
|
-
@object_nodes[object].anchor = (@object_anchors[object] ||= (@object_anchor += 1)),
|
3344
|
-
leading_comments,
|
3345
|
-
trailing_comments
|
3346
|
-
)
|
3480
|
+
AliasNode.new(@object_nodes[object].anchor = (@object_anchors[object] ||= (@object_anchor += 1)), comments)
|
3347
3481
|
else
|
3348
3482
|
case object
|
3349
3483
|
when Psych::Omap
|
3350
|
-
@object_nodes[object] =
|
3351
|
-
OmapNode.new(
|
3352
|
-
object.map { |(key, value)| HashNode.new({ dump(key) => dump(value) }, [], []) },
|
3353
|
-
leading_comments,
|
3354
|
-
trailing_comments
|
3355
|
-
)
|
3484
|
+
@object_nodes[object] = OmapNode.new(object.map { |(key, value)| HashNode.new({ dump(key) => dump(value) }, nil) }, comments)
|
3356
3485
|
when Psych::Set
|
3357
|
-
@object_nodes[object] =
|
3358
|
-
SetNode.new(
|
3359
|
-
object.to_h { |key, value| [dump(key), dump(value)] },
|
3360
|
-
leading_comments,
|
3361
|
-
trailing_comments
|
3362
|
-
)
|
3486
|
+
@object_nodes[object] = SetNode.new(object.to_h { |key, value| [dump(key), dump(value)] }, comments)
|
3363
3487
|
when Array
|
3364
|
-
@object_nodes[object] =
|
3365
|
-
ArrayNode.new(
|
3366
|
-
object.map { |element| dump(element) },
|
3367
|
-
leading_comments,
|
3368
|
-
trailing_comments
|
3369
|
-
)
|
3488
|
+
@object_nodes[object] = ArrayNode.new(object.map { |element| dump(element) }, comments)
|
3370
3489
|
when Hash
|
3371
|
-
|
3372
|
-
|
3373
|
-
object.to_h { |key, value| [dump(key), dump(value)] }
|
3374
|
-
|
3375
|
-
|
3376
|
-
|
3490
|
+
dumped =
|
3491
|
+
if base_object.is_a?(CommentsHash)
|
3492
|
+
object.to_h { |key, value| [dump(key, base_object.psych_key_comments[key]), dump(value)] }
|
3493
|
+
else
|
3494
|
+
object.to_h { |key, value| [dump(key), dump(value)] }
|
3495
|
+
end
|
3496
|
+
|
3497
|
+
@object_nodes[object] = HashNode.new(dumped, comments)
|
3377
3498
|
when String
|
3378
|
-
StringNode.new(object,
|
3499
|
+
StringNode.new(object, comments)
|
3379
3500
|
else
|
3380
|
-
ObjectNode.new(object,
|
3501
|
+
ObjectNode.new(object, comments)
|
3381
3502
|
end
|
3382
3503
|
end
|
3383
3504
|
end
|
@@ -3417,7 +3538,13 @@ module Psych
|
|
3417
3538
|
private
|
3418
3539
|
|
3419
3540
|
# Dump the given object, ensuring that it is a permitted object.
|
3420
|
-
def dump(
|
3541
|
+
def dump(base_object, comments = nil)
|
3542
|
+
object = base_object
|
3543
|
+
|
3544
|
+
if base_object.is_a?(CommentsObject) || base_object.is_a?(CommentsHash)
|
3545
|
+
object = base_object.__getobj__
|
3546
|
+
end
|
3547
|
+
|
3421
3548
|
if !@aliases && @object_nodes.key?(object)
|
3422
3549
|
raise BadAlias, "Tried to dump an aliased object"
|
3423
3550
|
end
|
@@ -3435,7 +3562,7 @@ module Psych
|
|
3435
3562
|
end
|
3436
3563
|
|
3437
3564
|
# --------------------------------------------------------------------------
|
3438
|
-
# :section: Public API
|
3565
|
+
# :section: Public API mirroring Psych
|
3439
3566
|
# --------------------------------------------------------------------------
|
3440
3567
|
|
3441
3568
|
# Create a new default parser.
|
@@ -3443,6 +3570,22 @@ module Psych
|
|
3443
3570
|
Pure::Parser.new(TreeBuilder.new)
|
3444
3571
|
end
|
3445
3572
|
|
3573
|
+
def self.parse(yaml, filename: nil, comments: false)
|
3574
|
+
parse_stream(yaml, filename: filename, comments: comments) do |node|
|
3575
|
+
return node
|
3576
|
+
end
|
3577
|
+
|
3578
|
+
false
|
3579
|
+
end
|
3580
|
+
|
3581
|
+
def self.parse_file(filename, fallback: false, comments: false)
|
3582
|
+
result = File.open(filename, "r:bom|utf-8") do |f|
|
3583
|
+
parse(f, filename: filename, comments: comments)
|
3584
|
+
end
|
3585
|
+
|
3586
|
+
result || fallback
|
3587
|
+
end
|
3588
|
+
|
3446
3589
|
# Parse a YAML stream and return the root node.
|
3447
3590
|
def self.parse_stream(yaml, filename: nil, comments: false, &block)
|
3448
3591
|
if block_given?
|
@@ -3455,6 +3598,76 @@ module Psych
|
|
3455
3598
|
end
|
3456
3599
|
end
|
3457
3600
|
|
3601
|
+
def self.unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
|
3602
|
+
result = parse(yaml, filename: filename, comments: comments)
|
3603
|
+
return fallback unless result
|
3604
|
+
|
3605
|
+
result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, comments: comments)
|
3606
|
+
end
|
3607
|
+
|
3608
|
+
def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
|
3609
|
+
result = parse(yaml, filename: filename, comments: comments)
|
3610
|
+
return fallback unless result
|
3611
|
+
|
3612
|
+
class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s), permitted_symbols.map(&:to_s))
|
3613
|
+
scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer)
|
3614
|
+
visitor =
|
3615
|
+
if aliases
|
3616
|
+
Visitors::ToRuby.new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze, comments: comments)
|
3617
|
+
else
|
3618
|
+
Visitors::NoAliasRuby.new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze, comments: comments)
|
3619
|
+
end
|
3620
|
+
|
3621
|
+
visitor.accept(result)
|
3622
|
+
end
|
3623
|
+
|
3624
|
+
def self.load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
|
3625
|
+
safe_load(
|
3626
|
+
yaml,
|
3627
|
+
permitted_classes: permitted_classes,
|
3628
|
+
permitted_symbols: permitted_symbols,
|
3629
|
+
aliases: aliases,
|
3630
|
+
filename: filename,
|
3631
|
+
fallback: fallback,
|
3632
|
+
symbolize_names: symbolize_names,
|
3633
|
+
freeze: freeze,
|
3634
|
+
strict_integer: strict_integer,
|
3635
|
+
comments: comments
|
3636
|
+
)
|
3637
|
+
end
|
3638
|
+
|
3639
|
+
def self.load_stream(yaml, filename: nil, fallback: [], comments: false, **kwargs)
|
3640
|
+
result =
|
3641
|
+
if block_given?
|
3642
|
+
parse_stream(yaml, filename: filename, comments: comments) do |node|
|
3643
|
+
yield node.to_ruby(**kwargs)
|
3644
|
+
end
|
3645
|
+
else
|
3646
|
+
parse_stream(yaml, filename: filename, comments: comments).children.map { |node| node.to_ruby(**kwargs) }
|
3647
|
+
end
|
3648
|
+
|
3649
|
+
return fallback if result.is_a?(Array) && result.empty?
|
3650
|
+
result
|
3651
|
+
end
|
3652
|
+
|
3653
|
+
def self.unsafe_load_file(filename, **kwargs)
|
3654
|
+
File.open(filename, "r:bom|utf-8") do |f|
|
3655
|
+
self.unsafe_load(f, filename: filename, **kwargs)
|
3656
|
+
end
|
3657
|
+
end
|
3658
|
+
|
3659
|
+
def self.safe_load_file(filename, **kwargs)
|
3660
|
+
File.open(filename, "r:bom|utf-8") do |f|
|
3661
|
+
self.safe_load(f, filename: filename, **kwargs)
|
3662
|
+
end
|
3663
|
+
end
|
3664
|
+
|
3665
|
+
def self.load_file(filename, **kwargs)
|
3666
|
+
File.open(filename, "r:bom|utf-8") do |f|
|
3667
|
+
self.load(f, filename: filename, **kwargs)
|
3668
|
+
end
|
3669
|
+
end
|
3670
|
+
|
3458
3671
|
# Dump an object to a YAML string.
|
3459
3672
|
def self.dump(o, io = nil, options = {})
|
3460
3673
|
if Hash === io
|
@@ -3489,89 +3702,5 @@ module Psych
|
|
3489
3702
|
objects.each { |object| emitter << object }
|
3490
3703
|
io || real_io.string
|
3491
3704
|
end
|
3492
|
-
|
3493
|
-
# --------------------------------------------------------------------------
|
3494
|
-
# :section: Public API copied directly from Psych
|
3495
|
-
# --------------------------------------------------------------------------
|
3496
|
-
|
3497
|
-
def self.unsafe_load yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, comments: false
|
3498
|
-
result = parse(yaml, filename: filename, comments: comments)
|
3499
|
-
return fallback unless result
|
3500
|
-
result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer)
|
3501
|
-
end
|
3502
|
-
|
3503
|
-
def self.safe_load yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false
|
3504
|
-
result = parse(yaml, filename: filename, comments: comments)
|
3505
|
-
return fallback unless result
|
3506
|
-
|
3507
|
-
class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s),
|
3508
|
-
permitted_symbols.map(&:to_s))
|
3509
|
-
scanner = ScalarScanner.new class_loader, strict_integer: strict_integer
|
3510
|
-
visitor = if aliases
|
3511
|
-
Visitors::ToRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
|
3512
|
-
else
|
3513
|
-
Visitors::NoAliasRuby.new scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze
|
3514
|
-
end
|
3515
|
-
result = visitor.accept result
|
3516
|
-
result
|
3517
|
-
end
|
3518
|
-
|
3519
|
-
def self.load yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false
|
3520
|
-
safe_load yaml, permitted_classes: permitted_classes,
|
3521
|
-
permitted_symbols: permitted_symbols,
|
3522
|
-
aliases: aliases,
|
3523
|
-
filename: filename,
|
3524
|
-
fallback: fallback,
|
3525
|
-
symbolize_names: symbolize_names,
|
3526
|
-
freeze: freeze,
|
3527
|
-
strict_integer: strict_integer,
|
3528
|
-
comments: comments
|
3529
|
-
end
|
3530
|
-
|
3531
|
-
def self.parse yaml, filename: nil, comments: false
|
3532
|
-
parse_stream(yaml, filename: filename, comments: comments) do |node|
|
3533
|
-
return node
|
3534
|
-
end
|
3535
|
-
|
3536
|
-
false
|
3537
|
-
end
|
3538
|
-
|
3539
|
-
def self.parse_file filename, fallback: false, comments: false
|
3540
|
-
result = File.open filename, 'r:bom|utf-8' do |f|
|
3541
|
-
parse f, filename: filename, comments: comments
|
3542
|
-
end
|
3543
|
-
result || fallback
|
3544
|
-
end
|
3545
|
-
|
3546
|
-
def self.load_stream yaml, filename: nil, fallback: [], comments: false, **kwargs
|
3547
|
-
result = if block_given?
|
3548
|
-
parse_stream(yaml, filename: filename, comments: comments) do |node|
|
3549
|
-
yield node.to_ruby(**kwargs)
|
3550
|
-
end
|
3551
|
-
else
|
3552
|
-
parse_stream(yaml, filename: filename, comments: comments).children.map { |node| node.to_ruby(**kwargs) }
|
3553
|
-
end
|
3554
|
-
|
3555
|
-
return fallback if result.is_a?(Array) && result.empty?
|
3556
|
-
result
|
3557
|
-
end
|
3558
|
-
|
3559
|
-
def self.unsafe_load_file filename, **kwargs
|
3560
|
-
File.open(filename, 'r:bom|utf-8') { |f|
|
3561
|
-
self.unsafe_load f, filename: filename, **kwargs
|
3562
|
-
}
|
3563
|
-
end
|
3564
|
-
|
3565
|
-
def self.safe_load_file filename, **kwargs
|
3566
|
-
File.open(filename, 'r:bom|utf-8') { |f|
|
3567
|
-
self.safe_load f, filename: filename, **kwargs
|
3568
|
-
}
|
3569
|
-
end
|
3570
|
-
|
3571
|
-
def self.load_file filename, **kwargs
|
3572
|
-
File.open(filename, 'r:bom|utf-8') { |f|
|
3573
|
-
self.load f, filename: filename, **kwargs
|
3574
|
-
}
|
3575
|
-
end
|
3576
3705
|
end
|
3577
3706
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: psych-pure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-13 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: psych
|
@@ -97,7 +97,7 @@ licenses:
|
|
97
97
|
- MIT
|
98
98
|
metadata:
|
99
99
|
bug_tracker_uri: https://github.com/kddnewton/psych-pure/issues
|
100
|
-
changelog_uri: https://github.com/kddnewton/psych-pure/blob/v0.1.
|
100
|
+
changelog_uri: https://github.com/kddnewton/psych-pure/blob/v0.1.1/CHANGELOG.md
|
101
101
|
source_code_uri: https://github.com/kddnewton/psych-pure
|
102
102
|
rubygems_mfa_required: 'true'
|
103
103
|
rdoc_options: []
|