psych-pure 0.1.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 +7 -0
- data/CHANGELOG.md +16 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE +21 -0
- data/README.md +66 -0
- data/lib/psych/pure/version.rb +7 -0
- data/lib/psych/pure.rb +3577 -0
- data/psych-pure.gemspec +43 -0
- metadata +120 -0
data/lib/psych/pure.rb
ADDED
@@ -0,0 +1,3577 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delegate"
|
4
|
+
require "pp"
|
5
|
+
require "psych"
|
6
|
+
require "strscan"
|
7
|
+
require "stringio"
|
8
|
+
|
9
|
+
module Psych
|
10
|
+
# A YAML parser written in Ruby.
|
11
|
+
module Pure
|
12
|
+
# Represents a comment in the input.
|
13
|
+
class Comment
|
14
|
+
attr_reader :value, :start_line, :start_column, :end_line, :end_column
|
15
|
+
|
16
|
+
def initialize(value, start_line, start_column, end_line, end_column, inline)
|
17
|
+
@value = value
|
18
|
+
@start_line = start_line
|
19
|
+
@start_column = start_column
|
20
|
+
@end_line = end_line
|
21
|
+
@end_column = end_column
|
22
|
+
@inline = inline
|
23
|
+
end
|
24
|
+
|
25
|
+
def inline?
|
26
|
+
@inline
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Wraps a Ruby object with its leading and trailing YAML comments from the
|
31
|
+
# source input.
|
32
|
+
class YAMLCommentsDelegator < SimpleDelegator
|
33
|
+
attr_reader :yaml_leading_comments, :yaml_trailing_comments
|
34
|
+
|
35
|
+
def initialize(object, yaml_leading_comments, yaml_trailing_comments)
|
36
|
+
super(object)
|
37
|
+
@yaml_leading_comments = yaml_leading_comments
|
38
|
+
@yaml_trailing_comments = yaml_trailing_comments
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# This module contains all of the extensions to Psych that we need in order
|
43
|
+
# to support parsing comments.
|
44
|
+
module CommentExtensions
|
45
|
+
# Extend the Handler to be able to handle comments coming out of the
|
46
|
+
# parser.
|
47
|
+
module Handler
|
48
|
+
def comment(value, start_line, start_column, end_line, end_column, inline)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Extend the TreeBuilder to be able to attach comments to nodes.
|
53
|
+
module TreeBuilder
|
54
|
+
def comments
|
55
|
+
@comments ||= []
|
56
|
+
end
|
57
|
+
|
58
|
+
def comment(value, start_line, start_column, end_line, end_column, inline)
|
59
|
+
comments << Comment.new(value, start_line, start_column, end_line, end_column, inline)
|
60
|
+
end
|
61
|
+
|
62
|
+
def end_stream
|
63
|
+
attach_comments(super)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def attach_comments(node)
|
69
|
+
comments.each do |comment|
|
70
|
+
preceding, enclosing, following = nearest_nodes(node, comment)
|
71
|
+
|
72
|
+
if comment.inline?
|
73
|
+
if preceding
|
74
|
+
preceding.trailing_comment(comment)
|
75
|
+
else
|
76
|
+
(following || enclosing || node).leading_comment(comment)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# If a comment exists on its own line, prefer a leading comment.
|
80
|
+
if following
|
81
|
+
following.leading_comment(comment)
|
82
|
+
elsif preceding
|
83
|
+
preceding.trailing_comment(comment)
|
84
|
+
else
|
85
|
+
(enclosing || node).leading_comment(comment)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
comments.clear
|
91
|
+
node
|
92
|
+
end
|
93
|
+
|
94
|
+
def nearest_nodes(node, comment)
|
95
|
+
candidates = (node.children || []).sort_by { |child| [child.start_line, child.start_column] }
|
96
|
+
preceding = nil
|
97
|
+
following = nil
|
98
|
+
|
99
|
+
comment_start_line = comment.start_line
|
100
|
+
comment_start_column = comment.start_column
|
101
|
+
comment_end_line = comment.end_line
|
102
|
+
comment_end_column = comment.end_column
|
103
|
+
|
104
|
+
left = 0
|
105
|
+
right = candidates.length
|
106
|
+
|
107
|
+
# This is a custom binary search that finds the nearest nodes to the
|
108
|
+
# given comment. When it finds a node that completely encapsulates the
|
109
|
+
# comment, it recurses downward into the tree.
|
110
|
+
while left < right
|
111
|
+
middle = (left + right) / 2
|
112
|
+
candidate = candidates[middle]
|
113
|
+
|
114
|
+
if ((comment_start_line > candidate.start_line) || (comment_start_line == candidate.start_line && comment_start_column >= candidate.start_column)) &&
|
115
|
+
((comment_end_line < candidate.end_line) || (comment_end_line == candidate.end_line && comment_end_column <= candidate.end_column))
|
116
|
+
# The comment is completely contained by this candidate node.
|
117
|
+
# Abandon the binary search at this level.
|
118
|
+
return nearest_nodes(candidate, comment)
|
119
|
+
end
|
120
|
+
|
121
|
+
if (candidate.end_line < comment_start_line) ||
|
122
|
+
(candidate.end_line == comment_start_line && candidate.end_column <= comment_start_column)
|
123
|
+
# This candidate falls completely before the comment. Because we
|
124
|
+
# will never consider this candidate or any candidates before it
|
125
|
+
# again, this candidate must be the closest preceding candidate we
|
126
|
+
# have encountered so far.
|
127
|
+
preceding = candidate
|
128
|
+
left = middle + 1
|
129
|
+
next
|
130
|
+
end
|
131
|
+
|
132
|
+
if (candidate.start_line > comment_end_line) ||
|
133
|
+
(candidate.start_line == comment_end_line && candidate.start_column >= comment_end_column)
|
134
|
+
# This candidate falls completely after the comment. Because we
|
135
|
+
# will never consider this candidate or any candidates after it
|
136
|
+
# again, this candidate must be the closest following candidate we
|
137
|
+
# have encountered so far.
|
138
|
+
following = candidate
|
139
|
+
right = middle
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
# This should only happen if there is a bug in this parser.
|
144
|
+
raise InternalException, "Comment location overlaps with a target location"
|
145
|
+
end
|
146
|
+
|
147
|
+
[preceding, node, following]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Extend the document stream to be able to attach comments to the
|
152
|
+
# document.
|
153
|
+
module DocumentStream
|
154
|
+
def end_document(implicit_end = !streaming?)
|
155
|
+
@last.implicit_end = implicit_end
|
156
|
+
@block.call(attach_comments(pop))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Extend the nodes to be able to store comments.
|
161
|
+
module Node
|
162
|
+
def leading_comments
|
163
|
+
@leading_comments ||= []
|
164
|
+
end
|
165
|
+
|
166
|
+
def leading_comment(comment)
|
167
|
+
leading_comments << comment
|
168
|
+
end
|
169
|
+
|
170
|
+
def trailing_comments
|
171
|
+
@trailing_comments ||= []
|
172
|
+
end
|
173
|
+
|
174
|
+
def trailing_comment(comment)
|
175
|
+
trailing_comments << comment
|
176
|
+
end
|
177
|
+
|
178
|
+
def comments?
|
179
|
+
(defined?(@leading_comments) && @leading_comments.any?) ||
|
180
|
+
(defined?(@trailing_comments) && @trailing_comments.any?)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Extend the ToRuby visitor to be able to attach comments to the resulting
|
185
|
+
# Ruby objects.
|
186
|
+
module ToRuby
|
187
|
+
def accept(node)
|
188
|
+
result = super
|
189
|
+
|
190
|
+
if node.comments?
|
191
|
+
result =
|
192
|
+
YAMLCommentsDelegator.new(
|
193
|
+
result,
|
194
|
+
node.leading_comments,
|
195
|
+
node.trailing_comments
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
result
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
::Psych::Handler.prepend(CommentExtensions::Handler)
|
205
|
+
::Psych::TreeBuilder.prepend(CommentExtensions::TreeBuilder)
|
206
|
+
::Psych::Handlers::DocumentStream.prepend(CommentExtensions::DocumentStream)
|
207
|
+
::Psych::Nodes::Node.prepend(CommentExtensions::Node)
|
208
|
+
::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
|
271
|
+
|
272
|
+
# An alias event represents a reference to a previously defined anchor.
|
273
|
+
class Alias
|
274
|
+
attr_reader :location, :name
|
275
|
+
|
276
|
+
def initialize(location, name)
|
277
|
+
@location = location
|
278
|
+
@name = name
|
279
|
+
end
|
280
|
+
|
281
|
+
def accept(handler)
|
282
|
+
handler.event_location(*@location)
|
283
|
+
handler.alias(@name)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# A document start event represents the beginning of a new document, of
|
288
|
+
# which there can be multiple in a single YAML stream.
|
289
|
+
class DocumentStart
|
290
|
+
attr_reader :location, :tag_directives
|
291
|
+
attr_accessor :version
|
292
|
+
attr_writer :implicit
|
293
|
+
|
294
|
+
def initialize(location)
|
295
|
+
@location = location
|
296
|
+
@version = nil
|
297
|
+
@tag_directives = {}
|
298
|
+
@implicit = true
|
299
|
+
end
|
300
|
+
|
301
|
+
def accept(handler)
|
302
|
+
handler.event_location(*@location)
|
303
|
+
handler.start_document(@version, @tag_directives.to_a, @implicit)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# A document end event represents the end of a document, which may be
|
308
|
+
# implicit at the end of the stream or explicit with the ... delimiter.
|
309
|
+
class DocumentEnd
|
310
|
+
attr_reader :location
|
311
|
+
attr_writer :implicit
|
312
|
+
|
313
|
+
def initialize(location)
|
314
|
+
@location = location
|
315
|
+
@implicit = true
|
316
|
+
end
|
317
|
+
|
318
|
+
def accept(handler)
|
319
|
+
handler.event_location(*@location)
|
320
|
+
handler.end_document(@implicit)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# A mapping start event represents the beginning of a new mapping, which is
|
325
|
+
# a set of key-value pairs.
|
326
|
+
class MappingStart
|
327
|
+
attr_reader :location, :style
|
328
|
+
attr_accessor :anchor, :tag
|
329
|
+
|
330
|
+
def initialize(location, style)
|
331
|
+
@location = location
|
332
|
+
@anchor = nil
|
333
|
+
@tag = nil
|
334
|
+
@style = style
|
335
|
+
end
|
336
|
+
|
337
|
+
def accept(handler)
|
338
|
+
handler.event_location(*@location)
|
339
|
+
handler.start_mapping(@anchor, @tag, @style == Nodes::Mapping::BLOCK, @style)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# A mapping end event represents the end of a mapping.
|
344
|
+
class MappingEnd
|
345
|
+
attr_reader :location
|
346
|
+
|
347
|
+
def initialize(location)
|
348
|
+
@location = location
|
349
|
+
end
|
350
|
+
|
351
|
+
def accept(handler)
|
352
|
+
handler.event_location(*@location)
|
353
|
+
handler.end_mapping
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# A scalar event represents a single value in the YAML document. It can be
|
358
|
+
# many different types.
|
359
|
+
class Scalar
|
360
|
+
attr_reader :location, :value, :style
|
361
|
+
attr_accessor :anchor, :tag
|
362
|
+
|
363
|
+
def initialize(location, value, style)
|
364
|
+
@location = location
|
365
|
+
@value = value
|
366
|
+
@anchor = nil
|
367
|
+
@tag = nil
|
368
|
+
@style = style
|
369
|
+
end
|
370
|
+
|
371
|
+
def accept(handler)
|
372
|
+
handler.event_location(*@location)
|
373
|
+
handler.scalar(
|
374
|
+
@value,
|
375
|
+
@anchor,
|
376
|
+
@tag,
|
377
|
+
(!@tag || @tag == "!") && (@style == Nodes::Scalar::PLAIN),
|
378
|
+
(!@tag || @tag == "!") && (@style != Nodes::Scalar::PLAIN),
|
379
|
+
@style
|
380
|
+
)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# A sequence start event represents the beginning of a new sequence, which
|
385
|
+
# is a list of values.
|
386
|
+
class SequenceStart
|
387
|
+
attr_reader :location, :style
|
388
|
+
attr_accessor :anchor, :tag
|
389
|
+
|
390
|
+
def initialize(location, style)
|
391
|
+
@location = location
|
392
|
+
@anchor = nil
|
393
|
+
@tag = nil
|
394
|
+
@style = style
|
395
|
+
end
|
396
|
+
|
397
|
+
def accept(handler)
|
398
|
+
handler.event_location(*@location)
|
399
|
+
handler.start_sequence(@anchor, @tag, @style == Nodes::Sequence::BLOCK, @style)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# A sequence end event represents the end of a sequence.
|
404
|
+
class SequenceEnd
|
405
|
+
attr_reader :location
|
406
|
+
|
407
|
+
def initialize(location)
|
408
|
+
@location = location
|
409
|
+
end
|
410
|
+
|
411
|
+
def accept(handler)
|
412
|
+
handler.event_location(*@location)
|
413
|
+
handler.end_sequence
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# A stream start event represents the beginning of a new stream. There
|
418
|
+
# should only be one of these in a YAML stream.
|
419
|
+
class StreamStart
|
420
|
+
attr_reader :location
|
421
|
+
|
422
|
+
def initialize(location)
|
423
|
+
@location = location
|
424
|
+
end
|
425
|
+
|
426
|
+
def accept(handler)
|
427
|
+
handler.event_location(*@location)
|
428
|
+
handler.start_stream(Psych::Parser::UTF8)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# A stream end event represents the end of a stream. There should only be
|
433
|
+
# one of these in a YAML stream.
|
434
|
+
class StreamEnd
|
435
|
+
attr_reader :location
|
436
|
+
|
437
|
+
def initialize(location)
|
438
|
+
@location = location
|
439
|
+
end
|
440
|
+
|
441
|
+
def accept(handler)
|
442
|
+
handler.event_location(*@location)
|
443
|
+
handler.end_stream
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# The parser is responsible for taking a YAML string and converting it into
|
448
|
+
# a series of events that can be used by the consumer.
|
449
|
+
class Parser
|
450
|
+
# Initialize a new parser with the given source string.
|
451
|
+
def initialize(handler)
|
452
|
+
# These are used to track the current state of the parser.
|
453
|
+
@scanner = nil
|
454
|
+
@filename = nil
|
455
|
+
@source = nil
|
456
|
+
|
457
|
+
# The handler is the consumer of the events generated by the parser.
|
458
|
+
@handler = handler
|
459
|
+
|
460
|
+
# This functions as a list of temporary lists of events that may be
|
461
|
+
# flushed into the handler if current context is matched.
|
462
|
+
@events_cache = []
|
463
|
+
|
464
|
+
# These events are used to track the start and end of a document. They
|
465
|
+
# are flushed into the main events list when a new document is started.
|
466
|
+
@document_start_event = nil
|
467
|
+
@document_end_event = nil
|
468
|
+
|
469
|
+
# Each document gets its own set of tags. This is a mapping of tag
|
470
|
+
# handles to tag prefixes.
|
471
|
+
@tag_directives = nil
|
472
|
+
|
473
|
+
# When a tag property is parsed, it is stored here until it can be
|
474
|
+
# flushed into the next event.
|
475
|
+
@tag = nil
|
476
|
+
|
477
|
+
# When an anchor is parsed, it is stored here until it can be flushed
|
478
|
+
# into the next event.
|
479
|
+
@anchor = nil
|
480
|
+
|
481
|
+
# In a bare document, explicit document starts (---) and ends (...) are
|
482
|
+
# disallowed. In that case we need to check for those delimiters.
|
483
|
+
@in_bare_document = false
|
484
|
+
|
485
|
+
# In a literal or folded scalar, we need to track that state in order to
|
486
|
+
# insert the correct plain text prefix.
|
487
|
+
@in_scalar = false
|
488
|
+
@text_prefix = +""
|
489
|
+
|
490
|
+
# This parser can optionally parse comments and attach them to the
|
491
|
+
# resulting tree, if the option is passed.
|
492
|
+
@comments = false
|
493
|
+
end
|
494
|
+
|
495
|
+
# Top-level parse function that starts the parsing process.
|
496
|
+
def parse(yaml, filename = yaml.respond_to?(:path) ? yaml.path : "<unknown>", comments: false)
|
497
|
+
if yaml.respond_to?(:read)
|
498
|
+
yaml = yaml.read
|
499
|
+
elsif !yaml.is_a?(String)
|
500
|
+
raise TypeError, "Expected an IO or a String, got #{yaml.class}"
|
501
|
+
end
|
502
|
+
|
503
|
+
# This parser only supports UTF-8 encoding at the moment. This is
|
504
|
+
# largely due to the performance impact of having to convert all of the
|
505
|
+
# strings and regular expressions into compatible encodings. We do not
|
506
|
+
# attempt to transcode, as the locations would all be off at that point.
|
507
|
+
if yaml.encoding != Encoding::UTF_8
|
508
|
+
raise ArgumentError, "Expected UTF-8 encoding, got #{yaml.encoding}"
|
509
|
+
end
|
510
|
+
|
511
|
+
yaml += "\n" if !yaml.empty? && !yaml.end_with?("\n")
|
512
|
+
@scanner = StringScanner.new(yaml)
|
513
|
+
@filename = filename
|
514
|
+
@source = Source.new(yaml)
|
515
|
+
@comments = comments
|
516
|
+
|
517
|
+
raise_syntax_error("Parser failed to complete") unless parse_l_yaml_stream
|
518
|
+
raise_syntax_error("Parser finished before end of input") unless @scanner.eos?
|
519
|
+
true
|
520
|
+
end
|
521
|
+
|
522
|
+
private
|
523
|
+
|
524
|
+
# Raise a syntax error with the given message.
|
525
|
+
def raise_syntax_error(message)
|
526
|
+
line = @source.line(@scanner.pos)
|
527
|
+
column = @source.column(@scanner.pos)
|
528
|
+
raise SyntaxError.new(@filename, line, column, @scanner.pos, message, nil)
|
529
|
+
end
|
530
|
+
|
531
|
+
# ------------------------------------------------------------------------
|
532
|
+
# :section: Parsing helpers
|
533
|
+
# ------------------------------------------------------------------------
|
534
|
+
|
535
|
+
# In certain cirumstances, we need to determine the indent based on the
|
536
|
+
# content that follows the current position. This method implements that
|
537
|
+
# logic.
|
538
|
+
def detect_indent(n)
|
539
|
+
pos = @scanner.pos
|
540
|
+
in_seq = pos > 0 && @scanner.string.byteslice(pos - 1).match?(/[\-\?\:]/)
|
541
|
+
|
542
|
+
match = @scanner.check(%r{((?:\ *(?:\#.*)?\n)*)(\ *)}) or raise InternalException
|
543
|
+
pre = @scanner[1]
|
544
|
+
m = @scanner[2].length
|
545
|
+
|
546
|
+
if in_seq && pre.empty?
|
547
|
+
m += 1 if n == -1
|
548
|
+
else
|
549
|
+
m -= n
|
550
|
+
end
|
551
|
+
|
552
|
+
m = 0 if m < 0
|
553
|
+
m
|
554
|
+
end
|
555
|
+
|
556
|
+
# This is a convenience method used to retrieve a segment of the string
|
557
|
+
# that was just matched by the scanner. It takes a position and returns
|
558
|
+
# the input string from that position to the current scanner position.
|
559
|
+
def from(pos)
|
560
|
+
@scanner.string.byteslice(pos...@scanner.pos)
|
561
|
+
end
|
562
|
+
|
563
|
+
# This is the only way that the scanner is advanced. It checks if the
|
564
|
+
# given value matches the current position (either with a string or
|
565
|
+
# regular expression). If it does, it advances the scanner and returns
|
566
|
+
# true. If it does not, it returns false.
|
567
|
+
def match(value)
|
568
|
+
if @in_bare_document
|
569
|
+
return false if @scanner.eos?
|
570
|
+
return false if ((pos = @scanner.pos) == 0 || (@scanner.string.byteslice(pos - 1) == "\n")) && @scanner.check(/(?:---|\.\.\.)(?=\s|$)/)
|
571
|
+
end
|
572
|
+
|
573
|
+
@scanner.skip(value)
|
574
|
+
end
|
575
|
+
|
576
|
+
# This is effectively the same as match, except that it does not advance
|
577
|
+
# the scanner if the given match is found.
|
578
|
+
def peek
|
579
|
+
pos_start = @scanner.pos
|
580
|
+
result = try { yield }
|
581
|
+
@scanner.pos = pos_start
|
582
|
+
result
|
583
|
+
end
|
584
|
+
|
585
|
+
# In the grammar when a rule has rule+, it means it should match one or
|
586
|
+
# more times. This is a convenience method that implements that logic by
|
587
|
+
# attempting to match the given block one or more times.
|
588
|
+
def plus
|
589
|
+
return false unless yield
|
590
|
+
pos_current = @scanner.pos
|
591
|
+
pos_current = @scanner.pos while yield && (@scanner.pos != pos_current)
|
592
|
+
true
|
593
|
+
end
|
594
|
+
|
595
|
+
# In the grammar when a rule has rule*, it means it should match zero or
|
596
|
+
# more times. This is a convenience method that implements that logic by
|
597
|
+
# attempting to match the given block zero or more times.
|
598
|
+
def star
|
599
|
+
pos_current = @scanner.pos
|
600
|
+
pos_current = @scanner.pos while yield && (@scanner.pos != pos_current)
|
601
|
+
true
|
602
|
+
end
|
603
|
+
|
604
|
+
# True if the scanner it at the beginning of the string, the end of the
|
605
|
+
# string, or the previous character was a newline.
|
606
|
+
def start_of_line?
|
607
|
+
(pos = @scanner.pos) == 0 ||
|
608
|
+
@scanner.eos? ||
|
609
|
+
(@scanner.string.byteslice(pos - 1) == "\n")
|
610
|
+
end
|
611
|
+
|
612
|
+
# This is our main backtracking mechanism. It attempts to parse forward
|
613
|
+
# using the given block and return true. If it fails, it backtracks to the
|
614
|
+
# original position and returns false.
|
615
|
+
def try
|
616
|
+
pos_start = @scanner.pos
|
617
|
+
yield || (@scanner.pos = pos_start; false)
|
618
|
+
end
|
619
|
+
|
620
|
+
# ------------------------------------------------------------------------
|
621
|
+
# :section: Event handling
|
622
|
+
# ------------------------------------------------------------------------
|
623
|
+
|
624
|
+
# If there is a document end event, then flush it to the list of events
|
625
|
+
# and reset back to the starting state to parse the next document.
|
626
|
+
def document_end_event_flush
|
627
|
+
if @document_end_event
|
628
|
+
@document_end_event.accept(@handler)
|
629
|
+
@document_start_event = DocumentStart.new(Location.point(@source, @scanner.pos))
|
630
|
+
@tag_directives = @document_start_event.tag_directives
|
631
|
+
@document_end_event = nil
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
# Push a new temporary list onto the events cache.
|
636
|
+
def events_cache_push
|
637
|
+
@events_cache << []
|
638
|
+
end
|
639
|
+
|
640
|
+
# Pop a temporary list from the events cache.
|
641
|
+
def events_cache_pop
|
642
|
+
@events_cache.pop or raise InternalException
|
643
|
+
end
|
644
|
+
|
645
|
+
# Pop a temporary list from the events cache and flush it to the next
|
646
|
+
# level down in the cache or directly to the handler.
|
647
|
+
def events_cache_flush
|
648
|
+
events_cache_pop.each { |event| events_push(event) }
|
649
|
+
end
|
650
|
+
|
651
|
+
# Push an event into the events list. This could be pushing into the most
|
652
|
+
# recent temporary list if there is one, or flushed directly to the
|
653
|
+
# handler.
|
654
|
+
def events_push(event)
|
655
|
+
if @events_cache.empty?
|
656
|
+
if @document_start_event
|
657
|
+
case event
|
658
|
+
when MappingStart, SequenceStart, Scalar
|
659
|
+
@document_start_event.accept(@handler)
|
660
|
+
@document_start_event = nil
|
661
|
+
@document_end_event = DocumentEnd.new(Location.point(@source, @scanner.pos))
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
event.accept(@handler)
|
666
|
+
else
|
667
|
+
@events_cache.last << event
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# Push an event into the events list and flush the anchor and tag
|
672
|
+
# properties if they are set.
|
673
|
+
def events_push_flush_properties(event)
|
674
|
+
if @anchor
|
675
|
+
event.anchor = @anchor
|
676
|
+
@anchor = nil
|
677
|
+
end
|
678
|
+
|
679
|
+
if @tag
|
680
|
+
event.tag = @tag
|
681
|
+
@tag = nil
|
682
|
+
end
|
683
|
+
|
684
|
+
events_push(event)
|
685
|
+
end
|
686
|
+
|
687
|
+
# ------------------------------------------------------------------------
|
688
|
+
# :section: Grammar rules
|
689
|
+
# ------------------------------------------------------------------------
|
690
|
+
|
691
|
+
# [002]
|
692
|
+
# nb-json ::=
|
693
|
+
# x:9 | [x:20-x:10FFFF]
|
694
|
+
def parse_nb_json
|
695
|
+
match(/[\u{09}\u{20}-\u{10FFFF}]/)
|
696
|
+
end
|
697
|
+
|
698
|
+
# [023]
|
699
|
+
# c-flow-indicator ::=
|
700
|
+
# ',' | '[' | ']' | '{' | '}'
|
701
|
+
def parse_c_flow_indicator
|
702
|
+
match(/[,\[\]{}]/)
|
703
|
+
end
|
704
|
+
|
705
|
+
# [027]
|
706
|
+
# nb-char ::=
|
707
|
+
# c-printable - b-char - c-byte-order-mark
|
708
|
+
def parse_nb_char
|
709
|
+
pos_start = @scanner.pos
|
710
|
+
|
711
|
+
if match(/[\u{09}\u{0A}\u{0D}\u{20}-\u{7E}\u{85}\u{A0}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}]/)
|
712
|
+
pos_end = @scanner.pos
|
713
|
+
@scanner.pos = pos_start
|
714
|
+
|
715
|
+
if match(/[\u{0A}\u{0D}\u{FEFF}]/)
|
716
|
+
@scanner.pos = pos_start
|
717
|
+
false
|
718
|
+
else
|
719
|
+
@scanner.pos = pos_end
|
720
|
+
true
|
721
|
+
end
|
722
|
+
else
|
723
|
+
@scanner.pos = pos_start
|
724
|
+
false
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
# [028]
|
729
|
+
# b-break ::=
|
730
|
+
# ( b-carriage-return b-line-feed )
|
731
|
+
# | b-carriage-return
|
732
|
+
# | b-line-feed
|
733
|
+
def parse_b_break
|
734
|
+
match(/\u{0A}|\u{0D}\u{0A}?/)
|
735
|
+
end
|
736
|
+
|
737
|
+
# [029]
|
738
|
+
# b-as-line-feed ::=
|
739
|
+
# b-break
|
740
|
+
alias parse_b_as_line_feed parse_b_break
|
741
|
+
|
742
|
+
# [030]
|
743
|
+
# b-non-content ::=
|
744
|
+
# b-break
|
745
|
+
alias parse_b_non_content parse_b_break
|
746
|
+
|
747
|
+
# [033]
|
748
|
+
# s-white ::=
|
749
|
+
# s-space | s-tab
|
750
|
+
def parse_s_white
|
751
|
+
pos_start = @scanner.pos
|
752
|
+
|
753
|
+
if match(/[\u{20}\u{09}]/)
|
754
|
+
@text_prefix = from(pos_start) if @in_scalar
|
755
|
+
true
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
# Effectively star { parse_s_white }
|
760
|
+
def parse_s_white_star
|
761
|
+
match(/[\u{20}\u{09}]*/)
|
762
|
+
true
|
763
|
+
end
|
764
|
+
|
765
|
+
# [034]
|
766
|
+
# ns-char ::=
|
767
|
+
# nb-char - s-white
|
768
|
+
def parse_ns_char
|
769
|
+
pos_start = @scanner.pos
|
770
|
+
|
771
|
+
if begin
|
772
|
+
if parse_nb_char
|
773
|
+
pos_end = @scanner.pos
|
774
|
+
@scanner.pos = pos_start
|
775
|
+
|
776
|
+
if parse_s_white
|
777
|
+
@scanner.pos = pos_start
|
778
|
+
false
|
779
|
+
else
|
780
|
+
@scanner.pos = pos_end
|
781
|
+
true
|
782
|
+
end
|
783
|
+
end
|
784
|
+
end then
|
785
|
+
@text_prefix = from(pos_start) if @in_scalar
|
786
|
+
true
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
# [036]
|
791
|
+
# ns-hex-digit ::=
|
792
|
+
# ns-dec-digit
|
793
|
+
# | [x:41-x:46] | [x:61-x:66]
|
794
|
+
def parse_ns_hex_digit
|
795
|
+
match(/[\u{30}-\u{39}\u{41}-\u{46}\u{61}-\u{66}]/)
|
796
|
+
end
|
797
|
+
|
798
|
+
# [039]
|
799
|
+
# ns-uri-char ::=
|
800
|
+
# '%' ns-hex-digit ns-hex-digit | ns-word-char | '#'
|
801
|
+
# | ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ','
|
802
|
+
# | '_' | '.' | '!' | '~' | '*' | ''' | '(' | ')' | '[' | ']'
|
803
|
+
def parse_ns_uri_char
|
804
|
+
try { match("%") && parse_ns_hex_digit && parse_ns_hex_digit } ||
|
805
|
+
match(/[\u{30}-\u{39}\u{41}-\u{5A}\u{61}-\u{7A}\-#;\/?:@&=+$,_.!~*'\(\)\[\]]/)
|
806
|
+
end
|
807
|
+
|
808
|
+
# [040]
|
809
|
+
# ns-tag-char ::=
|
810
|
+
# ns-uri-char - '!' - c-flow-indicator
|
811
|
+
def parse_ns_tag_char
|
812
|
+
pos_start = @scanner.pos
|
813
|
+
|
814
|
+
if parse_ns_uri_char
|
815
|
+
pos_end = @scanner.pos
|
816
|
+
@scanner.pos = pos_start
|
817
|
+
|
818
|
+
if match("!") || parse_c_flow_indicator
|
819
|
+
@scanner.pos = pos_start
|
820
|
+
false
|
821
|
+
else
|
822
|
+
@scanner.pos = pos_end
|
823
|
+
true
|
824
|
+
end
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
# [062]
|
829
|
+
# c-ns-esc-char ::=
|
830
|
+
# c-escape
|
831
|
+
# ( ns-esc-null | ns-esc-bell | ns-esc-backspace
|
832
|
+
# | ns-esc-horizontal-tab | ns-esc-line-feed
|
833
|
+
# | ns-esc-vertical-tab | ns-esc-form-feed
|
834
|
+
# | ns-esc-carriage-return | ns-esc-escape | ns-esc-space
|
835
|
+
# | ns-esc-double-quote | ns-esc-slash | ns-esc-backslash
|
836
|
+
# | ns-esc-next-line | ns-esc-non-breaking-space
|
837
|
+
# | ns-esc-line-separator | ns-esc-paragraph-separator
|
838
|
+
# | ns-esc-8-bit | ns-esc-16-bit | ns-esc-32-bit )
|
839
|
+
def parse_c_ns_esc_char
|
840
|
+
match(/\\[0abt\u{09}nvfre\u{20}"\/\\N_LP]/) ||
|
841
|
+
try { match("\\x") && parse_ns_hex_digit && parse_ns_hex_digit } ||
|
842
|
+
try { match("\\u") && 4.times.all? { parse_ns_hex_digit } } ||
|
843
|
+
try { match("\\U") && 8.times.all? { parse_ns_hex_digit } }
|
844
|
+
end
|
845
|
+
|
846
|
+
# [063]
|
847
|
+
# s-indent(n) ::=
|
848
|
+
# s-space{n}
|
849
|
+
def parse_s_indent(n)
|
850
|
+
match(/\u{20}{#{n}}/)
|
851
|
+
end
|
852
|
+
|
853
|
+
# [031]
|
854
|
+
# s-space ::=
|
855
|
+
# x:20
|
856
|
+
#
|
857
|
+
# [064]
|
858
|
+
# s-indent(<n) ::=
|
859
|
+
# s-space{m} <where_m_<_n>
|
860
|
+
def parse_s_indent_lt(n)
|
861
|
+
pos_start = @scanner.pos
|
862
|
+
match(/\u{20}*/)
|
863
|
+
|
864
|
+
if (@scanner.pos - pos_start) < n
|
865
|
+
true
|
866
|
+
else
|
867
|
+
@scanner.pos = pos_start
|
868
|
+
false
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
# [031]
|
873
|
+
# s-space ::=
|
874
|
+
# x:20
|
875
|
+
#
|
876
|
+
# [065]
|
877
|
+
# s-indent(<=n) ::=
|
878
|
+
# s-space{m} <where_m_<=_n>
|
879
|
+
def parse_s_indent_le(n)
|
880
|
+
pos_start = @scanner.pos
|
881
|
+
match(/\u{20}*/)
|
882
|
+
|
883
|
+
if (@scanner.pos - pos_start) <= n
|
884
|
+
true
|
885
|
+
else
|
886
|
+
@scanner.pos = pos_start
|
887
|
+
false
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
# [066]
|
892
|
+
# s-separate-in-line ::=
|
893
|
+
# s-white+ | <start_of_line>
|
894
|
+
def parse_s_separate_in_line
|
895
|
+
plus { parse_s_white } || start_of_line?
|
896
|
+
end
|
897
|
+
|
898
|
+
# [067]
|
899
|
+
# s-line-prefix(n,c) ::=
|
900
|
+
# ( c = block-out => s-block-line-prefix(n) )
|
901
|
+
# ( c = block-in => s-block-line-prefix(n) )
|
902
|
+
# ( c = flow-out => s-flow-line-prefix(n) )
|
903
|
+
# ( c = flow-in => s-flow-line-prefix(n) )
|
904
|
+
def parse_s_line_prefix(n, c)
|
905
|
+
case c
|
906
|
+
when :block_in then parse_s_block_line_prefix(n)
|
907
|
+
when :block_out then parse_s_block_line_prefix(n)
|
908
|
+
when :flow_in then parse_s_flow_line_prefix(n)
|
909
|
+
when :flow_out then parse_s_flow_line_prefix(n)
|
910
|
+
else raise InternalException, c.inspect
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
# [068]
|
915
|
+
# s-block-line-prefix(n) ::=
|
916
|
+
# s-indent(n)
|
917
|
+
def parse_s_block_line_prefix(n)
|
918
|
+
parse_s_indent(n)
|
919
|
+
end
|
920
|
+
|
921
|
+
# [069]
|
922
|
+
# s-flow-line-prefix(n) ::=
|
923
|
+
# s-indent(n)
|
924
|
+
# s-separate-in-line?
|
925
|
+
def parse_s_flow_line_prefix(n)
|
926
|
+
try do
|
927
|
+
if parse_s_indent(n)
|
928
|
+
parse_s_separate_in_line
|
929
|
+
true
|
930
|
+
end
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
# [070]
|
935
|
+
# l-empty(n,c) ::=
|
936
|
+
# ( s-line-prefix(n,c) | s-indent(<n) )
|
937
|
+
# b-as-line-feed
|
938
|
+
def parse_l_empty(n, c)
|
939
|
+
if try {
|
940
|
+
(parse_s_line_prefix(n, c) || parse_s_indent_lt(n)) &&
|
941
|
+
parse_b_as_line_feed
|
942
|
+
} then
|
943
|
+
events_push("") if @in_scalar
|
944
|
+
true
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
# [071]
|
949
|
+
# b-l-trimmed(n,c) ::=
|
950
|
+
# b-non-content l-empty(n,c)+
|
951
|
+
def parse_b_l_trimmed(n, c)
|
952
|
+
try { parse_b_non_content && plus { parse_l_empty(n, c) } }
|
953
|
+
end
|
954
|
+
|
955
|
+
# [072]
|
956
|
+
# b-as-space ::=
|
957
|
+
# b-break
|
958
|
+
alias parse_b_as_space parse_b_break
|
959
|
+
|
960
|
+
# [073]
|
961
|
+
# b-l-folded(n,c) ::=
|
962
|
+
# b-l-trimmed(n,c) | b-as-space
|
963
|
+
def parse_b_l_folded(n, c)
|
964
|
+
parse_b_l_trimmed(n, c) || parse_b_as_space
|
965
|
+
end
|
966
|
+
|
967
|
+
# [074]
|
968
|
+
# s-flow-folded(n) ::=
|
969
|
+
# s-separate-in-line?
|
970
|
+
# b-l-folded(n,flow-in)
|
971
|
+
# s-flow-line-prefix(n)
|
972
|
+
def parse_s_flow_folded(n)
|
973
|
+
try do
|
974
|
+
parse_s_separate_in_line
|
975
|
+
parse_b_l_folded(n, :flow_in) && parse_s_flow_line_prefix(n)
|
976
|
+
end
|
977
|
+
end
|
978
|
+
|
979
|
+
# [075]
|
980
|
+
# c-nb-comment-text ::=
|
981
|
+
# '#' nb-char*
|
982
|
+
def parse_c_nb_comment_text(inline)
|
983
|
+
return false unless match("#")
|
984
|
+
|
985
|
+
pos = @scanner.pos - 1
|
986
|
+
star { parse_nb_char }
|
987
|
+
|
988
|
+
@handler.comment(from(pos), *Location.new(@source, pos, @scanner.pos), inline) if @comments
|
989
|
+
true
|
990
|
+
end
|
991
|
+
|
992
|
+
# [076]
|
993
|
+
# b-comment ::=
|
994
|
+
# b-non-content | <end_of_file>
|
995
|
+
def parse_b_comment
|
996
|
+
parse_b_non_content || @scanner.eos?
|
997
|
+
end
|
998
|
+
|
999
|
+
# [077]
|
1000
|
+
# s-b-comment ::=
|
1001
|
+
# ( s-separate-in-line
|
1002
|
+
# c-nb-comment-text? )?
|
1003
|
+
# b-comment
|
1004
|
+
def parse_s_b_comment
|
1005
|
+
try do
|
1006
|
+
try do
|
1007
|
+
if parse_s_separate_in_line
|
1008
|
+
parse_c_nb_comment_text(true)
|
1009
|
+
true
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
parse_b_comment
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# [078]
|
1018
|
+
# l-comment ::=
|
1019
|
+
# s-separate-in-line c-nb-comment-text?
|
1020
|
+
# b-comment
|
1021
|
+
def parse_l_comment
|
1022
|
+
try do
|
1023
|
+
if parse_s_separate_in_line
|
1024
|
+
parse_c_nb_comment_text(false)
|
1025
|
+
parse_b_comment
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
# [079]
|
1031
|
+
# s-l-comments ::=
|
1032
|
+
# ( s-b-comment | <start_of_line> )
|
1033
|
+
# l-comment*
|
1034
|
+
def parse_s_l_comments
|
1035
|
+
try { (parse_s_b_comment || start_of_line?) && star { parse_l_comment } }
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# [080]
|
1039
|
+
# s-separate(n,c) ::=
|
1040
|
+
# ( c = block-out => s-separate-lines(n) )
|
1041
|
+
# ( c = block-in => s-separate-lines(n) )
|
1042
|
+
# ( c = flow-out => s-separate-lines(n) )
|
1043
|
+
# ( c = flow-in => s-separate-lines(n) )
|
1044
|
+
# ( c = block-key => s-separate-in-line )
|
1045
|
+
# ( c = flow-key => s-separate-in-line )
|
1046
|
+
def parse_s_separate(n, c)
|
1047
|
+
case c
|
1048
|
+
when :block_in then parse_s_separate_lines(n)
|
1049
|
+
when :block_key then parse_s_separate_in_line
|
1050
|
+
when :block_out then parse_s_separate_lines(n)
|
1051
|
+
when :flow_in then parse_s_separate_lines(n)
|
1052
|
+
when :flow_key then parse_s_separate_in_line
|
1053
|
+
when :flow_out then parse_s_separate_lines(n)
|
1054
|
+
else raise InternalException, c.inspect
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
# [081]
|
1059
|
+
# s-separate-lines(n) ::=
|
1060
|
+
# ( s-l-comments
|
1061
|
+
# s-flow-line-prefix(n) )
|
1062
|
+
# | s-separate-in-line
|
1063
|
+
def parse_s_separate_lines(n)
|
1064
|
+
try { parse_s_l_comments && parse_s_flow_line_prefix(n) } ||
|
1065
|
+
parse_s_separate_in_line
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
# [082]
|
1069
|
+
# l-directive ::=
|
1070
|
+
# '%'
|
1071
|
+
# ( ns-yaml-directive
|
1072
|
+
# | ns-tag-directive
|
1073
|
+
# | ns-reserved-directive )
|
1074
|
+
# s-l-comments
|
1075
|
+
def parse_l_directive
|
1076
|
+
try do
|
1077
|
+
match("%") &&
|
1078
|
+
(parse_ns_yaml_directive || parse_ns_tag_directive || parse_ns_reserved_directive) &&
|
1079
|
+
parse_s_l_comments
|
1080
|
+
end
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# [083]
|
1084
|
+
# ns-reserved-directive ::=
|
1085
|
+
# ns-directive-name
|
1086
|
+
# ( s-separate-in-line ns-directive-parameter )*
|
1087
|
+
def parse_ns_reserved_directive
|
1088
|
+
try do
|
1089
|
+
parse_ns_directive_name &&
|
1090
|
+
star { try { parse_s_separate_in_line && parse_ns_directive_parameter } }
|
1091
|
+
end
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
# [084]
|
1095
|
+
# ns-directive-name ::=
|
1096
|
+
# ns-char+
|
1097
|
+
def parse_ns_directive_name
|
1098
|
+
plus { parse_ns_char }
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
# [085]
|
1102
|
+
# ns-directive-parameter ::=
|
1103
|
+
# ns-char+
|
1104
|
+
def parse_ns_directive_parameter
|
1105
|
+
plus { parse_ns_char }
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
# [086]
|
1109
|
+
# ns-yaml-directive ::=
|
1110
|
+
# 'Y' 'A' 'M' 'L'
|
1111
|
+
# s-separate-in-line ns-yaml-version
|
1112
|
+
def parse_ns_yaml_directive
|
1113
|
+
try { match("YAML") && parse_s_separate_in_line && parse_ns_yaml_version }
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# [035]
|
1117
|
+
# ns-dec-digit ::=
|
1118
|
+
# [x:30-x:39]
|
1119
|
+
#
|
1120
|
+
# [087]
|
1121
|
+
# ns-yaml-version ::=
|
1122
|
+
# ns-dec-digit+ '.' ns-dec-digit+
|
1123
|
+
def parse_ns_yaml_version
|
1124
|
+
pos_start = @scanner.pos
|
1125
|
+
|
1126
|
+
if try {
|
1127
|
+
plus { match(/[\u{30}-\u{39}]/) } &&
|
1128
|
+
match(".") &&
|
1129
|
+
plus { match(/[\u{30}-\u{39}]/) }
|
1130
|
+
} then
|
1131
|
+
raise_syntax_error("Multiple %YAML directives not allowed") if @document_start_event.version
|
1132
|
+
@document_start_event.version = from(pos_start).split(".").map { |digits| digits.to_i(10) }
|
1133
|
+
true
|
1134
|
+
end
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
# [088]
|
1138
|
+
# ns-tag-directive ::=
|
1139
|
+
# 'T' 'A' 'G'
|
1140
|
+
# s-separate-in-line c-tag-handle
|
1141
|
+
# s-separate-in-line ns-tag-prefix
|
1142
|
+
def parse_ns_tag_directive
|
1143
|
+
try do
|
1144
|
+
match("TAG") &&
|
1145
|
+
parse_s_separate_in_line &&
|
1146
|
+
parse_c_tag_handle &&
|
1147
|
+
parse_s_separate_in_line &&
|
1148
|
+
parse_ns_tag_prefix
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
# [092]
|
1153
|
+
# c-named-tag-handle ::=
|
1154
|
+
# '!' ns-word-char+ '!'
|
1155
|
+
def parse_c_tag_handle
|
1156
|
+
pos_start = @scanner.pos
|
1157
|
+
|
1158
|
+
if begin
|
1159
|
+
try do
|
1160
|
+
match("!") &&
|
1161
|
+
plus { match(/[\u{30}-\u{39}\u{41}-\u{5A}\u{61}-\u{7A}-]/) } &&
|
1162
|
+
match("!")
|
1163
|
+
end || match(/!!?/)
|
1164
|
+
end then
|
1165
|
+
@tag_handle = from(pos_start)
|
1166
|
+
true
|
1167
|
+
end
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
# [093]
|
1171
|
+
# ns-tag-prefix ::=
|
1172
|
+
# c-ns-local-tag-prefix | ns-global-tag-prefix
|
1173
|
+
def parse_ns_tag_prefix
|
1174
|
+
pos_start = @scanner.pos
|
1175
|
+
|
1176
|
+
if parse_c_ns_local_tag_prefix || parse_ns_global_tag_prefix
|
1177
|
+
@tag_directives[@tag_handle] = from(pos_start)
|
1178
|
+
true
|
1179
|
+
end
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# [094]
|
1183
|
+
# c-ns-local-tag-prefix ::=
|
1184
|
+
# '!' ns-uri-char*
|
1185
|
+
def parse_c_ns_local_tag_prefix
|
1186
|
+
try { match("!") && star { parse_ns_uri_char } }
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
# [095]
|
1190
|
+
# ns-global-tag-prefix ::=
|
1191
|
+
# ns-tag-char ns-uri-char*
|
1192
|
+
def parse_ns_global_tag_prefix
|
1193
|
+
try { parse_ns_tag_char && star { parse_ns_uri_char } }
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
# [096]
|
1197
|
+
# c-ns-properties(n,c) ::=
|
1198
|
+
# ( c-ns-tag-property
|
1199
|
+
# ( s-separate(n,c) c-ns-anchor-property )? )
|
1200
|
+
# | ( c-ns-anchor-property
|
1201
|
+
# ( s-separate(n,c) c-ns-tag-property )? )
|
1202
|
+
def parse_c_ns_properties(n, c)
|
1203
|
+
try do
|
1204
|
+
if parse_c_ns_tag_property
|
1205
|
+
try { parse_s_separate(n, c) && parse_c_ns_anchor_property }
|
1206
|
+
true
|
1207
|
+
end
|
1208
|
+
end ||
|
1209
|
+
try do
|
1210
|
+
if parse_c_ns_anchor_property
|
1211
|
+
try { parse_s_separate(n, c) && parse_c_ns_tag_property }
|
1212
|
+
true
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
# [097]
|
1218
|
+
# c-ns-tag-property ::=
|
1219
|
+
# c-verbatim-tag
|
1220
|
+
# | c-ns-shorthand-tag
|
1221
|
+
# | c-non-specific-tag
|
1222
|
+
#
|
1223
|
+
# [098]
|
1224
|
+
# c-verbatim-tag ::=
|
1225
|
+
# '!' '<' ns-uri-char+ '>'
|
1226
|
+
#
|
1227
|
+
# [099]
|
1228
|
+
# c-ns-shorthand-tag ::=
|
1229
|
+
# c-tag-handle ns-tag-char+
|
1230
|
+
#
|
1231
|
+
# [100]
|
1232
|
+
# c-non-specific-tag ::=
|
1233
|
+
# '!'
|
1234
|
+
def parse_c_ns_tag_property
|
1235
|
+
pos_start = @scanner.pos
|
1236
|
+
|
1237
|
+
if try { match("!<") && plus { parse_ns_uri_char } && match(">") }
|
1238
|
+
@tag = from(pos_start)[/\A!<(.*)>\z/, 1].gsub(/%([0-9a-fA-F]{2})/) { $1.to_i(16).chr(Encoding::UTF_8) }
|
1239
|
+
true
|
1240
|
+
elsif try { parse_c_tag_handle && plus { parse_ns_tag_char } }
|
1241
|
+
tag = from(pos_start)
|
1242
|
+
@tag =
|
1243
|
+
if (m = tag.match(/\A!!(.*)/))
|
1244
|
+
(prefix = @tag_directives["!!"]) ? (prefix + tag[2..]) : "tag:yaml.org,2002:#{m[1]}"
|
1245
|
+
elsif (m = tag.match(/\A(!.*?!)/))
|
1246
|
+
raise_syntax_error("No %TAG entry for '#{prefix}'") if !(prefix = @tag_directives[m[1]])
|
1247
|
+
prefix + tag[m[1].length..]
|
1248
|
+
elsif (prefix = @tag_directives["!"])
|
1249
|
+
prefix + tag[1..]
|
1250
|
+
else
|
1251
|
+
tag
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
@tag.gsub!(/%([0-9a-fA-F]{2})/) { $1.to_i(16).chr(Encoding::UTF_8) }
|
1255
|
+
true
|
1256
|
+
elsif match("!")
|
1257
|
+
@tag = @tag_directives.fetch("!", "!")
|
1258
|
+
true
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
# [101]
|
1263
|
+
# c-ns-anchor-property ::=
|
1264
|
+
# '&' ns-anchor-name
|
1265
|
+
def parse_c_ns_anchor_property
|
1266
|
+
pos_start = @scanner.pos
|
1267
|
+
|
1268
|
+
if try { match("&") && plus { parse_ns_anchor_char } }
|
1269
|
+
@anchor = from(pos_start).byteslice(1..)
|
1270
|
+
true
|
1271
|
+
end
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
# [102]
|
1275
|
+
# ns-anchor-char ::=
|
1276
|
+
# ns-char - c-flow-indicator
|
1277
|
+
def parse_ns_anchor_char
|
1278
|
+
pos_start = @scanner.pos
|
1279
|
+
|
1280
|
+
if parse_ns_char
|
1281
|
+
pos_end = @scanner.pos
|
1282
|
+
@scanner.pos = pos_start
|
1283
|
+
|
1284
|
+
if parse_c_flow_indicator
|
1285
|
+
@scanner.pos = pos_start
|
1286
|
+
false
|
1287
|
+
else
|
1288
|
+
@scanner.pos = pos_end
|
1289
|
+
true
|
1290
|
+
end
|
1291
|
+
end
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
# [104]
|
1295
|
+
# c-ns-alias-node ::=
|
1296
|
+
# '*' ns-anchor-name
|
1297
|
+
def parse_c_ns_alias_node
|
1298
|
+
pos_start = @scanner.pos
|
1299
|
+
|
1300
|
+
if try { match("*") && plus { parse_ns_anchor_char } }
|
1301
|
+
events_push_flush_properties(Alias.new(Location.new(@source, pos_start, @scanner.pos), from(pos_start).byteslice(1..)))
|
1302
|
+
true
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
# [105]
|
1307
|
+
# e-scalar ::=
|
1308
|
+
# <empty>
|
1309
|
+
def parse_e_scalar
|
1310
|
+
events_push_flush_properties(Scalar.new(Location.point(@source, @scanner.pos), "", Nodes::Scalar::PLAIN))
|
1311
|
+
true
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
# [106]
|
1315
|
+
# e-node ::=
|
1316
|
+
# e-scalar
|
1317
|
+
alias parse_e_node parse_e_scalar
|
1318
|
+
|
1319
|
+
# [107]
|
1320
|
+
# nb-double-char ::=
|
1321
|
+
# c-ns-esc-char | ( nb-json - '\' - '"' )
|
1322
|
+
def parse_nb_double_char
|
1323
|
+
return true if parse_c_ns_esc_char
|
1324
|
+
pos_start = @scanner.pos
|
1325
|
+
|
1326
|
+
if parse_nb_json
|
1327
|
+
pos_end = @scanner.pos
|
1328
|
+
@scanner.pos = pos_start
|
1329
|
+
|
1330
|
+
if match(/[\\"]/)
|
1331
|
+
@scanner.pos = pos_start
|
1332
|
+
false
|
1333
|
+
else
|
1334
|
+
@scanner.pos = pos_end
|
1335
|
+
true
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
# [108]
|
1341
|
+
# ns-double-char ::=
|
1342
|
+
# nb-double-char - s-white
|
1343
|
+
def parse_ns_double_char
|
1344
|
+
pos_start = @scanner.pos
|
1345
|
+
|
1346
|
+
if parse_nb_double_char
|
1347
|
+
pos_end = @scanner.pos
|
1348
|
+
@scanner.pos = pos_start
|
1349
|
+
|
1350
|
+
if parse_s_white
|
1351
|
+
@scanner.pos = pos_start
|
1352
|
+
false
|
1353
|
+
else
|
1354
|
+
@scanner.pos = pos_end
|
1355
|
+
true
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
# The following unescape sequences are supported in double quoted scalars.
|
1361
|
+
C_DOUBLE_QUOTED_UNESCAPES = {
|
1362
|
+
"\\\\" => "\\",
|
1363
|
+
"\\r\\n" => "\n",
|
1364
|
+
"\\ " => " ",
|
1365
|
+
'\\"' => '"',
|
1366
|
+
"\\/" => "/",
|
1367
|
+
"\\_" => "\u{a0}",
|
1368
|
+
"\\0" => "\x00",
|
1369
|
+
"\\a" => "\x07",
|
1370
|
+
"\\b" => "\x08",
|
1371
|
+
"\\e" => "\x1b",
|
1372
|
+
"\\f" => "\x0c",
|
1373
|
+
"\\n" => "\x0a",
|
1374
|
+
"\\r" => "\x0d",
|
1375
|
+
"\\t" => "\x09",
|
1376
|
+
"\\\t" => "\x09",
|
1377
|
+
"\\v" => "\x0b",
|
1378
|
+
"\\L" => "\u{2028}",
|
1379
|
+
"\\N" => "\u{85}",
|
1380
|
+
"\\P" => "\u{2029}"
|
1381
|
+
}.freeze
|
1382
|
+
|
1383
|
+
private_constant :C_DOUBLE_QUOTED_UNESCAPES
|
1384
|
+
|
1385
|
+
# [109]
|
1386
|
+
# c-double-quoted(n,c) ::=
|
1387
|
+
# '"' nb-double-text(n,c)
|
1388
|
+
# '"'
|
1389
|
+
def parse_c_double_quoted(n, c)
|
1390
|
+
pos_start = @scanner.pos
|
1391
|
+
|
1392
|
+
if try { match("\"") && parse_nb_double_text(n, c) && match("\"") }
|
1393
|
+
end1 = "(?:\\\\\\r?\\n[ \\t]*)"
|
1394
|
+
end2 = "(?:[ \\t]*\\r?\\n[ \\t]*)"
|
1395
|
+
hex = "[0-9a-fA-F]"
|
1396
|
+
hex2 = "(?:\\\\x(#{hex}{2}))"
|
1397
|
+
hex4 = "(?:\\\\u(#{hex}{4}))"
|
1398
|
+
hex8 = "(?:\\\\U(#{hex}{8}))"
|
1399
|
+
|
1400
|
+
value = from(pos_start).byteslice(1...-1)
|
1401
|
+
value.gsub!(%r{(?:\r\n|#{end1}|#{end2}+|#{hex2}|#{hex4}|#{hex8}|\\[\\ "/_0abefnrt\tvLNP])}) do |m|
|
1402
|
+
case m
|
1403
|
+
when /\A(?:#{hex2}|#{hex4}|#{hex8})\z/o
|
1404
|
+
m[2..].to_i(16).chr(Encoding::UTF_8)
|
1405
|
+
when /\A#{end1}\z/o
|
1406
|
+
""
|
1407
|
+
when /\A#{end2}+\z/o
|
1408
|
+
m.sub(/#{end2}/, "").gsub(/#{end2}/, "\n").then { |r| r.empty? ? " " : r }
|
1409
|
+
else
|
1410
|
+
C_DOUBLE_QUOTED_UNESCAPES.fetch(m, m)
|
1411
|
+
end
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), value, Nodes::Scalar::DOUBLE_QUOTED))
|
1415
|
+
true
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
|
1419
|
+
# [110]
|
1420
|
+
# nb-double-text(n,c) ::=
|
1421
|
+
# ( c = flow-out => nb-double-multi-line(n) )
|
1422
|
+
# ( c = flow-in => nb-double-multi-line(n) )
|
1423
|
+
# ( c = block-key => nb-double-one-line )
|
1424
|
+
# ( c = flow-key => nb-double-one-line )
|
1425
|
+
def parse_nb_double_text(n, c)
|
1426
|
+
case c
|
1427
|
+
when :block_key then parse_nb_double_one_line
|
1428
|
+
when :flow_in then parse_nb_double_multi_line(n)
|
1429
|
+
when :flow_key then parse_nb_double_one_line
|
1430
|
+
when :flow_out then parse_nb_double_multi_line(n)
|
1431
|
+
else raise InternalException, c.inspect
|
1432
|
+
end
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
# [111]
|
1436
|
+
# nb-double-one-line ::=
|
1437
|
+
# nb-double-char*
|
1438
|
+
def parse_nb_double_one_line
|
1439
|
+
star { parse_nb_double_char }
|
1440
|
+
end
|
1441
|
+
|
1442
|
+
# [112]
|
1443
|
+
# s-double-escaped(n) ::=
|
1444
|
+
# s-white* '\'
|
1445
|
+
# b-non-content
|
1446
|
+
# l-empty(n,flow-in)* s-flow-line-prefix(n)
|
1447
|
+
def parse_s_double_escaped(n)
|
1448
|
+
try do
|
1449
|
+
parse_s_white_star &&
|
1450
|
+
match("\\") &&
|
1451
|
+
parse_b_non_content &&
|
1452
|
+
star { parse_l_empty(n, :flow_in) } &&
|
1453
|
+
parse_s_flow_line_prefix(n)
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
# [113]
|
1458
|
+
# s-double-break(n) ::=
|
1459
|
+
# s-double-escaped(n) | s-flow-folded(n)
|
1460
|
+
def parse_s_double_break(n)
|
1461
|
+
parse_s_double_escaped(n) || parse_s_flow_folded(n)
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
# [114]
|
1465
|
+
# nb-ns-double-in-line ::=
|
1466
|
+
# ( s-white* ns-double-char )*
|
1467
|
+
def parse_nb_ns_double_in_line
|
1468
|
+
star { try { parse_s_white_star && parse_ns_double_char } }
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
# [115]
|
1472
|
+
# s-double-next-line(n) ::=
|
1473
|
+
# s-double-break(n)
|
1474
|
+
# ( ns-double-char nb-ns-double-in-line
|
1475
|
+
# ( s-double-next-line(n) | s-white* ) )?
|
1476
|
+
def parse_s_double_next_line(n)
|
1477
|
+
try do
|
1478
|
+
if parse_s_double_break(n)
|
1479
|
+
try do
|
1480
|
+
parse_ns_double_char &&
|
1481
|
+
parse_nb_ns_double_in_line &&
|
1482
|
+
(parse_s_double_next_line(n) || parse_s_white_star)
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
true
|
1486
|
+
end
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
# [116]
|
1491
|
+
# nb-double-multi-line(n) ::=
|
1492
|
+
# nb-ns-double-in-line
|
1493
|
+
# ( s-double-next-line(n) | s-white* )
|
1494
|
+
def parse_nb_double_multi_line(n)
|
1495
|
+
try do
|
1496
|
+
parse_nb_ns_double_in_line &&
|
1497
|
+
(parse_s_double_next_line(n) || parse_s_white_star)
|
1498
|
+
end
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
# [117]
|
1502
|
+
# c-quoted-quote ::=
|
1503
|
+
# ''' '''
|
1504
|
+
#
|
1505
|
+
# [118]
|
1506
|
+
# nb-single-char ::=
|
1507
|
+
# c-quoted-quote | ( nb-json - ''' )
|
1508
|
+
def parse_nb_single_char
|
1509
|
+
return true if match("''")
|
1510
|
+
pos_start = @scanner.pos
|
1511
|
+
|
1512
|
+
if parse_nb_json
|
1513
|
+
pos_end = @scanner.pos
|
1514
|
+
@scanner.pos = pos_start
|
1515
|
+
|
1516
|
+
if match("'")
|
1517
|
+
@scanner.pos = pos_start
|
1518
|
+
false
|
1519
|
+
else
|
1520
|
+
@scanner.pos = pos_end
|
1521
|
+
true
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
# [119]
|
1527
|
+
# ns-single-char ::=
|
1528
|
+
# nb-single-char - s-white
|
1529
|
+
def parse_ns_single_char
|
1530
|
+
pos_start = @scanner.pos
|
1531
|
+
|
1532
|
+
if parse_nb_single_char
|
1533
|
+
pos_end = @scanner.pos
|
1534
|
+
@scanner.pos = pos_start
|
1535
|
+
|
1536
|
+
if parse_s_white
|
1537
|
+
@scanner.pos = pos_start
|
1538
|
+
false
|
1539
|
+
else
|
1540
|
+
@scanner.pos = pos_end
|
1541
|
+
true
|
1542
|
+
end
|
1543
|
+
end
|
1544
|
+
end
|
1545
|
+
|
1546
|
+
# [120]
|
1547
|
+
# c-single-quoted(n,c) ::=
|
1548
|
+
# ''' nb-single-text(n,c)
|
1549
|
+
# '''
|
1550
|
+
def parse_c_single_quoted(n, c)
|
1551
|
+
pos_start = @scanner.pos
|
1552
|
+
|
1553
|
+
if try { match("'") && parse_nb_single_text(n, c) && match("'") }
|
1554
|
+
value = from(pos_start).byteslice(1...-1)
|
1555
|
+
value.gsub!(/(?:[\ \t]*\r?\n[\ \t]*)/, "\n")
|
1556
|
+
value.gsub!(/\n(\n*)/) { $1.empty? ? " " : $1 }
|
1557
|
+
value.gsub!("''", "'")
|
1558
|
+
events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), value, Nodes::Scalar::SINGLE_QUOTED))
|
1559
|
+
true
|
1560
|
+
end
|
1561
|
+
end
|
1562
|
+
|
1563
|
+
# [121]
|
1564
|
+
# nb-single-text(n,c) ::=
|
1565
|
+
# ( c = flow-out => nb-single-multi-line(n) )
|
1566
|
+
# ( c = flow-in => nb-single-multi-line(n) )
|
1567
|
+
# ( c = block-key => nb-single-one-line )
|
1568
|
+
# ( c = flow-key => nb-single-one-line )
|
1569
|
+
def parse_nb_single_text(n, c)
|
1570
|
+
case c
|
1571
|
+
when :block_key then parse_nb_single_one_line
|
1572
|
+
when :flow_in then parse_nb_single_multi_line(n)
|
1573
|
+
when :flow_key then parse_nb_single_one_line
|
1574
|
+
when :flow_out then parse_nb_single_multi_line(n)
|
1575
|
+
else raise InternalException, c.inspect
|
1576
|
+
end
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
# [122]
|
1580
|
+
# nb-single-one-line ::=
|
1581
|
+
# nb-single-char*
|
1582
|
+
def parse_nb_single_one_line
|
1583
|
+
star { parse_nb_single_char }
|
1584
|
+
end
|
1585
|
+
|
1586
|
+
# [123]
|
1587
|
+
# nb-ns-single-in-line ::=
|
1588
|
+
# ( s-white* ns-single-char )*
|
1589
|
+
def parse_nb_ns_single_in_line
|
1590
|
+
star { try { parse_s_white_star && parse_ns_single_char } }
|
1591
|
+
end
|
1592
|
+
|
1593
|
+
# [124]
|
1594
|
+
# s-single-next-line(n) ::=
|
1595
|
+
# s-flow-folded(n)
|
1596
|
+
# ( ns-single-char nb-ns-single-in-line
|
1597
|
+
# ( s-single-next-line(n) | s-white* ) )?
|
1598
|
+
def parse_s_single_next_line(n)
|
1599
|
+
try do
|
1600
|
+
if parse_s_flow_folded(n)
|
1601
|
+
try do
|
1602
|
+
parse_ns_single_char &&
|
1603
|
+
parse_nb_ns_single_in_line &&
|
1604
|
+
(parse_s_single_next_line(n) || parse_s_white_star)
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
true
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
# [125]
|
1613
|
+
# nb-single-multi-line(n) ::=
|
1614
|
+
# nb-ns-single-in-line
|
1615
|
+
# ( s-single-next-line(n) | s-white* )
|
1616
|
+
def parse_nb_single_multi_line(n)
|
1617
|
+
try do
|
1618
|
+
parse_nb_ns_single_in_line &&
|
1619
|
+
(parse_s_single_next_line(n) || parse_s_white_star)
|
1620
|
+
end
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
# [126]
|
1624
|
+
# ns-plain-first(c) ::=
|
1625
|
+
# ( ns-char - c-indicator )
|
1626
|
+
# | ( ( '?' | ':' | '-' )
|
1627
|
+
# <followed_by_an_ns-plain-safe(c)> )
|
1628
|
+
def parse_ns_plain_first(c)
|
1629
|
+
begin
|
1630
|
+
pos_start = @scanner.pos
|
1631
|
+
|
1632
|
+
if parse_ns_char
|
1633
|
+
pos_end = @scanner.pos
|
1634
|
+
@scanner.pos = pos_start
|
1635
|
+
|
1636
|
+
if match(/[-?:,\[\]{}#&*!|>'"%@`]/)
|
1637
|
+
@scanner.pos = pos_start
|
1638
|
+
false
|
1639
|
+
else
|
1640
|
+
@scanner.pos = pos_end
|
1641
|
+
true
|
1642
|
+
end
|
1643
|
+
end
|
1644
|
+
end || try { match(/[?:-]/) && peek { parse_ns_plain_safe(c) } }
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
# [127]
|
1648
|
+
# ns-plain-safe(c) ::=
|
1649
|
+
# ( c = flow-out => ns-plain-safe-out )
|
1650
|
+
# ( c = flow-in => ns-plain-safe-in )
|
1651
|
+
# ( c = block-key => ns-plain-safe-out )
|
1652
|
+
# ( c = flow-key => ns-plain-safe-in )
|
1653
|
+
def parse_ns_plain_safe(c)
|
1654
|
+
case c
|
1655
|
+
when :block_key then parse_ns_plain_safe_out
|
1656
|
+
when :flow_in then parse_ns_plain_safe_in
|
1657
|
+
when :flow_key then parse_ns_plain_safe_in
|
1658
|
+
when :flow_out then parse_ns_plain_safe_out
|
1659
|
+
else raise InternalException, c.inspect
|
1660
|
+
end
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
# [128]
|
1664
|
+
# ns-plain-safe-out ::=
|
1665
|
+
# ns-char
|
1666
|
+
alias parse_ns_plain_safe_out parse_ns_char
|
1667
|
+
|
1668
|
+
# [129]
|
1669
|
+
# ns-plain-safe-in ::=
|
1670
|
+
# ns-char - c-flow-indicator
|
1671
|
+
def parse_ns_plain_safe_in
|
1672
|
+
pos_start = @scanner.pos
|
1673
|
+
|
1674
|
+
if parse_ns_char
|
1675
|
+
pos_end = @scanner.pos
|
1676
|
+
@scanner.pos = pos_start
|
1677
|
+
|
1678
|
+
if parse_c_flow_indicator
|
1679
|
+
@scanner.pos = pos_start
|
1680
|
+
false
|
1681
|
+
else
|
1682
|
+
@scanner.pos = pos_end
|
1683
|
+
true
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
# [130]
|
1689
|
+
# ns-plain-char(c) ::=
|
1690
|
+
# ( ns-plain-safe(c) - ':' - '#' )
|
1691
|
+
# | ( <an_ns-char_preceding> '#' )
|
1692
|
+
# | ( ':' <followed_by_an_ns-plain-safe(c)> )
|
1693
|
+
def parse_ns_plain_char(c)
|
1694
|
+
try do
|
1695
|
+
pos_start = @scanner.pos
|
1696
|
+
|
1697
|
+
if parse_ns_plain_safe(c)
|
1698
|
+
pos_end = @scanner.pos
|
1699
|
+
@scanner.pos = pos_start
|
1700
|
+
|
1701
|
+
if match(/[:#]/)
|
1702
|
+
false
|
1703
|
+
else
|
1704
|
+
@scanner.pos = pos_end
|
1705
|
+
true
|
1706
|
+
end
|
1707
|
+
end
|
1708
|
+
end ||
|
1709
|
+
try do
|
1710
|
+
pos_start = @scanner.pos
|
1711
|
+
@scanner.pos -= 1
|
1712
|
+
|
1713
|
+
was_ns_char = parse_ns_char
|
1714
|
+
@scanner.pos = pos_start
|
1715
|
+
|
1716
|
+
was_ns_char && match("#")
|
1717
|
+
end ||
|
1718
|
+
try do
|
1719
|
+
match(":") && peek { parse_ns_plain_safe(c) }
|
1720
|
+
end
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
# [132]
|
1724
|
+
# nb-ns-plain-in-line(c) ::=
|
1725
|
+
# ( s-white*
|
1726
|
+
# ns-plain-char(c) )*
|
1727
|
+
def parse_nb_ns_plain_in_line(c)
|
1728
|
+
star { try { parse_s_white_star && parse_ns_plain_char(c) } }
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
# [133]
|
1732
|
+
# ns-plain-one-line(c) ::=
|
1733
|
+
# ns-plain-first(c)
|
1734
|
+
# nb-ns-plain-in-line(c)
|
1735
|
+
def parse_ns_plain_one_line(c)
|
1736
|
+
try { parse_ns_plain_first(c) && parse_nb_ns_plain_in_line(c) }
|
1737
|
+
end
|
1738
|
+
|
1739
|
+
# [134]
|
1740
|
+
# s-ns-plain-next-line(n,c) ::=
|
1741
|
+
# s-flow-folded(n)
|
1742
|
+
# ns-plain-char(c) nb-ns-plain-in-line(c)
|
1743
|
+
def parse_s_ns_plain_next_line(n, c)
|
1744
|
+
try do
|
1745
|
+
parse_s_flow_folded(n) &&
|
1746
|
+
parse_ns_plain_char(c) &&
|
1747
|
+
parse_nb_ns_plain_in_line(c)
|
1748
|
+
end
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
# [135]
|
1752
|
+
# ns-plain-multi-line(n,c) ::=
|
1753
|
+
# ns-plain-one-line(c)
|
1754
|
+
# s-ns-plain-next-line(n,c)*
|
1755
|
+
def parse_ns_plain_multi_line(n, c)
|
1756
|
+
try do
|
1757
|
+
parse_ns_plain_one_line(c) &&
|
1758
|
+
star { parse_s_ns_plain_next_line(n, c) }
|
1759
|
+
end
|
1760
|
+
end
|
1761
|
+
|
1762
|
+
# [136]
|
1763
|
+
# in-flow(c) ::=
|
1764
|
+
# ( c = flow-out => flow-in )
|
1765
|
+
# ( c = flow-in => flow-in )
|
1766
|
+
# ( c = block-key => flow-key )
|
1767
|
+
# ( c = flow-key => flow-key )
|
1768
|
+
def parse_in_flow(c)
|
1769
|
+
case c
|
1770
|
+
when :block_key then :flow_key
|
1771
|
+
when :flow_in then :flow_in
|
1772
|
+
when :flow_key then :flow_key
|
1773
|
+
when :flow_out then :flow_in
|
1774
|
+
else raise InternalException, c.inspect
|
1775
|
+
end
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
# [137]
|
1779
|
+
# c-flow-sequence(n,c) ::=
|
1780
|
+
# '[' s-separate(n,c)?
|
1781
|
+
# ns-s-flow-seq-entries(n,in-flow(c))? ']'
|
1782
|
+
def parse_c_flow_sequence(n, c)
|
1783
|
+
try do
|
1784
|
+
if match("[")
|
1785
|
+
events_push_flush_properties(SequenceStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Sequence::FLOW))
|
1786
|
+
|
1787
|
+
parse_s_separate(n, c)
|
1788
|
+
parse_ns_s_flow_seq_entries(n, parse_in_flow(c))
|
1789
|
+
|
1790
|
+
if match("]")
|
1791
|
+
events_push_flush_properties(SequenceEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
|
1792
|
+
true
|
1793
|
+
end
|
1794
|
+
end
|
1795
|
+
end
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
# [138]
|
1799
|
+
# ns-s-flow-seq-entries(n,c) ::=
|
1800
|
+
# ns-flow-seq-entry(n,c)
|
1801
|
+
# s-separate(n,c)?
|
1802
|
+
# ( ',' s-separate(n,c)?
|
1803
|
+
# ns-s-flow-seq-entries(n,c)? )?
|
1804
|
+
def parse_ns_s_flow_seq_entries(n, c)
|
1805
|
+
try do
|
1806
|
+
if parse_ns_flow_seq_entry(n, c)
|
1807
|
+
parse_s_separate(n, c)
|
1808
|
+
|
1809
|
+
try do
|
1810
|
+
if match(",")
|
1811
|
+
parse_s_separate(n, c)
|
1812
|
+
parse_ns_s_flow_seq_entries(n, c)
|
1813
|
+
true
|
1814
|
+
end
|
1815
|
+
end
|
1816
|
+
|
1817
|
+
true
|
1818
|
+
end
|
1819
|
+
end
|
1820
|
+
end
|
1821
|
+
|
1822
|
+
# [139]
|
1823
|
+
# ns-flow-seq-entry(n,c) ::=
|
1824
|
+
# ns-flow-pair(n,c) | ns-flow-node(n,c)
|
1825
|
+
def parse_ns_flow_seq_entry(n, c)
|
1826
|
+
parse_ns_flow_pair(n, c) || parse_ns_flow_node(n, c)
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
# [140]
|
1830
|
+
# c-flow-mapping(n,c) ::=
|
1831
|
+
# '{' s-separate(n,c)?
|
1832
|
+
# ns-s-flow-map-entries(n,in-flow(c))? '}'
|
1833
|
+
def parse_c_flow_mapping(n, c)
|
1834
|
+
try do
|
1835
|
+
if match("{")
|
1836
|
+
events_push_flush_properties(MappingStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Mapping::FLOW))
|
1837
|
+
|
1838
|
+
parse_s_separate(n, c)
|
1839
|
+
parse_ns_s_flow_map_entries(n, parse_in_flow(c))
|
1840
|
+
|
1841
|
+
if match("}")
|
1842
|
+
events_push_flush_properties(MappingEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
|
1843
|
+
true
|
1844
|
+
end
|
1845
|
+
end
|
1846
|
+
end
|
1847
|
+
end
|
1848
|
+
|
1849
|
+
# [141]
|
1850
|
+
# ns-s-flow-map-entries(n,c) ::=
|
1851
|
+
# ns-flow-map-entry(n,c)
|
1852
|
+
# s-separate(n,c)?
|
1853
|
+
# ( ',' s-separate(n,c)?
|
1854
|
+
# ns-s-flow-map-entries(n,c)? )?
|
1855
|
+
def parse_ns_s_flow_map_entries(n, c)
|
1856
|
+
try do
|
1857
|
+
if parse_ns_flow_map_entry(n, c)
|
1858
|
+
parse_s_separate(n, c)
|
1859
|
+
|
1860
|
+
try do
|
1861
|
+
if match(",")
|
1862
|
+
parse_s_separate(n, c)
|
1863
|
+
parse_ns_s_flow_map_entries(n, c)
|
1864
|
+
true
|
1865
|
+
end
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
true
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
# [142]
|
1874
|
+
# ns-flow-map-entry(n,c) ::=
|
1875
|
+
# ( '?' s-separate(n,c)
|
1876
|
+
# ns-flow-map-explicit-entry(n,c) )
|
1877
|
+
# | ns-flow-map-implicit-entry(n,c)
|
1878
|
+
def parse_ns_flow_map_entry(n, c)
|
1879
|
+
try do
|
1880
|
+
match("?") &&
|
1881
|
+
peek { @scanner.eos? || parse_s_white || parse_b_break } &&
|
1882
|
+
parse_s_separate(n, c) && parse_ns_flow_map_explicit_entry(n, c)
|
1883
|
+
end || parse_ns_flow_map_implicit_entry(n, c)
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
# [143]
|
1887
|
+
# ns-flow-map-explicit-entry(n,c) ::=
|
1888
|
+
# ns-flow-map-implicit-entry(n,c)
|
1889
|
+
# | ( e-node
|
1890
|
+
# e-node )
|
1891
|
+
def parse_ns_flow_map_explicit_entry(n, c)
|
1892
|
+
parse_ns_flow_map_implicit_entry(n, c) ||
|
1893
|
+
try { parse_e_node && parse_e_node }
|
1894
|
+
end
|
1895
|
+
|
1896
|
+
# [144]
|
1897
|
+
# ns-flow-map-implicit-entry(n,c) ::=
|
1898
|
+
# ns-flow-map-yaml-key-entry(n,c)
|
1899
|
+
# | c-ns-flow-map-empty-key-entry(n,c)
|
1900
|
+
# | c-ns-flow-map-json-key-entry(n,c)
|
1901
|
+
def parse_ns_flow_map_implicit_entry(n, c)
|
1902
|
+
parse_ns_flow_map_yaml_key_entry(n, c) ||
|
1903
|
+
parse_c_ns_flow_map_empty_key_entry(n, c) ||
|
1904
|
+
parse_c_ns_flow_map_json_key_entry(n, c)
|
1905
|
+
end
|
1906
|
+
|
1907
|
+
# [145]
|
1908
|
+
# ns-flow-map-yaml-key-entry(n,c) ::=
|
1909
|
+
# ns-flow-yaml-node(n,c)
|
1910
|
+
# ( ( s-separate(n,c)?
|
1911
|
+
# c-ns-flow-map-separate-value(n,c) )
|
1912
|
+
# | e-node )
|
1913
|
+
def parse_ns_flow_map_yaml_key_entry(n, c)
|
1914
|
+
try do
|
1915
|
+
parse_ns_flow_yaml_node(n, c) && (
|
1916
|
+
try do
|
1917
|
+
parse_s_separate(n, c)
|
1918
|
+
parse_c_ns_flow_map_separate_value(n, c)
|
1919
|
+
end || parse_e_node
|
1920
|
+
)
|
1921
|
+
end
|
1922
|
+
end
|
1923
|
+
|
1924
|
+
# [146]
|
1925
|
+
# c-ns-flow-map-empty-key-entry(n,c) ::=
|
1926
|
+
# e-node
|
1927
|
+
# c-ns-flow-map-separate-value(n,c)
|
1928
|
+
def parse_c_ns_flow_map_empty_key_entry(n, c)
|
1929
|
+
events_cache_push
|
1930
|
+
|
1931
|
+
if try { parse_e_node && parse_c_ns_flow_map_separate_value(n, c) }
|
1932
|
+
events_cache_flush
|
1933
|
+
true
|
1934
|
+
else
|
1935
|
+
events_cache_pop
|
1936
|
+
false
|
1937
|
+
end
|
1938
|
+
end
|
1939
|
+
|
1940
|
+
# [147]
|
1941
|
+
# c-ns-flow-map-separate-value(n,c) ::=
|
1942
|
+
# ':' <not_followed_by_an_ns-plain-safe(c)>
|
1943
|
+
# ( ( s-separate(n,c) ns-flow-node(n,c) )
|
1944
|
+
# | e-node )
|
1945
|
+
def parse_c_ns_flow_map_separate_value(n, c)
|
1946
|
+
try do
|
1947
|
+
match(":") &&
|
1948
|
+
!peek { parse_ns_plain_safe(c) } &&
|
1949
|
+
(try { parse_s_separate(n, c) && parse_ns_flow_node(n, c) } || parse_e_node)
|
1950
|
+
end
|
1951
|
+
end
|
1952
|
+
|
1953
|
+
# [148]
|
1954
|
+
# c-ns-flow-map-json-key-entry(n,c) ::=
|
1955
|
+
# c-flow-json-node(n,c)
|
1956
|
+
# ( ( s-separate(n,c)?
|
1957
|
+
# c-ns-flow-map-adjacent-value(n,c) )
|
1958
|
+
# | e-node )
|
1959
|
+
def parse_c_ns_flow_map_json_key_entry(n, c)
|
1960
|
+
try do
|
1961
|
+
parse_c_flow_json_node(n, c) && (
|
1962
|
+
try do
|
1963
|
+
parse_s_separate(n, c)
|
1964
|
+
parse_c_ns_flow_map_adjacent_value(n, c)
|
1965
|
+
end || parse_e_node
|
1966
|
+
)
|
1967
|
+
end
|
1968
|
+
end
|
1969
|
+
|
1970
|
+
# [149]
|
1971
|
+
# c-ns-flow-map-adjacent-value(n,c) ::=
|
1972
|
+
# ':' ( (
|
1973
|
+
# s-separate(n,c)?
|
1974
|
+
# ns-flow-node(n,c) )
|
1975
|
+
# | e-node )
|
1976
|
+
def parse_c_ns_flow_map_adjacent_value(n, c)
|
1977
|
+
try do
|
1978
|
+
match(":") && (
|
1979
|
+
try do
|
1980
|
+
parse_s_separate(n, c)
|
1981
|
+
parse_ns_flow_node(n, c)
|
1982
|
+
end || parse_e_node
|
1983
|
+
)
|
1984
|
+
end
|
1985
|
+
end
|
1986
|
+
|
1987
|
+
# [150]
|
1988
|
+
# ns-flow-pair(n,c) ::=
|
1989
|
+
# ( '?' s-separate(n,c)
|
1990
|
+
# ns-flow-map-explicit-entry(n,c) )
|
1991
|
+
# | ns-flow-pair-entry(n,c)
|
1992
|
+
def parse_ns_flow_pair(n, c)
|
1993
|
+
events_cache_push
|
1994
|
+
events_push_flush_properties(MappingStart.new(Location.point(@source, @scanner.pos), Nodes::Mapping::FLOW))
|
1995
|
+
|
1996
|
+
if begin
|
1997
|
+
try do
|
1998
|
+
match("?") &&
|
1999
|
+
peek { @scanner.eos? || parse_s_white || parse_b_break } &&
|
2000
|
+
parse_s_separate(n, c) &&
|
2001
|
+
parse_ns_flow_map_explicit_entry(n, c)
|
2002
|
+
end || parse_ns_flow_pair_entry(n, c)
|
2003
|
+
end then
|
2004
|
+
events_cache_flush
|
2005
|
+
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
|
2006
|
+
true
|
2007
|
+
else
|
2008
|
+
events_cache_pop
|
2009
|
+
false
|
2010
|
+
end
|
2011
|
+
end
|
2012
|
+
|
2013
|
+
# [151]
|
2014
|
+
# ns-flow-pair-entry(n,c) ::=
|
2015
|
+
# ns-flow-pair-yaml-key-entry(n,c)
|
2016
|
+
# | c-ns-flow-map-empty-key-entry(n,c)
|
2017
|
+
# | c-ns-flow-pair-json-key-entry(n,c)
|
2018
|
+
def parse_ns_flow_pair_entry(n, c)
|
2019
|
+
parse_ns_flow_pair_yaml_key_entry(n, c) ||
|
2020
|
+
parse_c_ns_flow_map_empty_key_entry(n, c) ||
|
2021
|
+
parse_c_ns_flow_pair_json_key_entry(n, c)
|
2022
|
+
end
|
2023
|
+
|
2024
|
+
# [152]
|
2025
|
+
# ns-flow-pair-yaml-key-entry(n,c) ::=
|
2026
|
+
# ns-s-implicit-yaml-key(flow-key)
|
2027
|
+
# c-ns-flow-map-separate-value(n,c)
|
2028
|
+
def parse_ns_flow_pair_yaml_key_entry(n, c)
|
2029
|
+
try do
|
2030
|
+
parse_ns_s_implicit_yaml_key(:flow_key) &&
|
2031
|
+
parse_c_ns_flow_map_separate_value(n, c)
|
2032
|
+
end
|
2033
|
+
end
|
2034
|
+
|
2035
|
+
# [153]
|
2036
|
+
# c-ns-flow-pair-json-key-entry(n,c) ::=
|
2037
|
+
# c-s-implicit-json-key(flow-key)
|
2038
|
+
# c-ns-flow-map-adjacent-value(n,c)
|
2039
|
+
def parse_c_ns_flow_pair_json_key_entry(n, c)
|
2040
|
+
try do
|
2041
|
+
parse_c_s_implicit_json_key(:flow_key) &&
|
2042
|
+
parse_c_ns_flow_map_adjacent_value(n, c)
|
2043
|
+
end
|
2044
|
+
end
|
2045
|
+
|
2046
|
+
# [154]
|
2047
|
+
# ns-s-implicit-yaml-key(c) ::=
|
2048
|
+
# ns-flow-yaml-node(n/a,c)
|
2049
|
+
# s-separate-in-line?
|
2050
|
+
# <at_most_1024_characters_altogether>
|
2051
|
+
def parse_ns_s_implicit_yaml_key(c)
|
2052
|
+
pos_start = @scanner.pos
|
2053
|
+
try do
|
2054
|
+
if parse_ns_flow_yaml_node(nil, c)
|
2055
|
+
parse_s_separate_in_line
|
2056
|
+
(@scanner.pos - pos_start) <= 1024
|
2057
|
+
end
|
2058
|
+
end
|
2059
|
+
end
|
2060
|
+
|
2061
|
+
# [155]
|
2062
|
+
# c-s-implicit-json-key(c) ::=
|
2063
|
+
# c-flow-json-node(n/a,c)
|
2064
|
+
# s-separate-in-line?
|
2065
|
+
# <at_most_1024_characters_altogether>
|
2066
|
+
def parse_c_s_implicit_json_key(c)
|
2067
|
+
pos_start = @scanner.pos
|
2068
|
+
try do
|
2069
|
+
if parse_c_flow_json_node(nil, c)
|
2070
|
+
parse_s_separate_in_line
|
2071
|
+
(@scanner.pos - pos_start) <= 1024
|
2072
|
+
end
|
2073
|
+
end
|
2074
|
+
end
|
2075
|
+
|
2076
|
+
# [131]
|
2077
|
+
# ns-plain(n,c) ::=
|
2078
|
+
# ( c = flow-out => ns-plain-multi-line(n,c) )
|
2079
|
+
# ( c = flow-in => ns-plain-multi-line(n,c) )
|
2080
|
+
# ( c = block-key => ns-plain-one-line(c) )
|
2081
|
+
# ( c = flow-key => ns-plain-one-line(c) )
|
2082
|
+
#
|
2083
|
+
# [156]
|
2084
|
+
# ns-flow-yaml-content(n,c) ::=
|
2085
|
+
# ns-plain(n,c)
|
2086
|
+
def parse_ns_flow_yaml_content(n, c)
|
2087
|
+
pos_start = @scanner.pos
|
2088
|
+
result =
|
2089
|
+
case c
|
2090
|
+
when :block_key then parse_ns_plain_one_line(c)
|
2091
|
+
when :flow_in then parse_ns_plain_multi_line(n, c)
|
2092
|
+
when :flow_key then parse_ns_plain_one_line(c)
|
2093
|
+
when :flow_out then parse_ns_plain_multi_line(n, c)
|
2094
|
+
else raise InternalException, c.inspect
|
2095
|
+
end
|
2096
|
+
|
2097
|
+
if result
|
2098
|
+
value = from(pos_start)
|
2099
|
+
value.gsub!(/(?:[\ \t]*\r?\n[\ \t]*)/, "\n")
|
2100
|
+
value.gsub!(/\n(\n*)/) { $1.empty? ? " " : $1 }
|
2101
|
+
events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), value, Nodes::Scalar::PLAIN))
|
2102
|
+
end
|
2103
|
+
|
2104
|
+
result
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
# [157]
|
2108
|
+
# c-flow-json-content(n,c) ::=
|
2109
|
+
# c-flow-sequence(n,c) | c-flow-mapping(n,c)
|
2110
|
+
# | c-single-quoted(n,c) | c-double-quoted(n,c)
|
2111
|
+
def parse_c_flow_json_content(n, c)
|
2112
|
+
parse_c_flow_sequence(n, c) ||
|
2113
|
+
parse_c_flow_mapping(n, c) ||
|
2114
|
+
parse_c_single_quoted(n, c) ||
|
2115
|
+
parse_c_double_quoted(n, c)
|
2116
|
+
end
|
2117
|
+
|
2118
|
+
# [158]
|
2119
|
+
# ns-flow-content(n,c) ::=
|
2120
|
+
# ns-flow-yaml-content(n,c) | c-flow-json-content(n,c)
|
2121
|
+
def parse_ns_flow_content(n, c)
|
2122
|
+
parse_ns_flow_yaml_content(n, c) ||
|
2123
|
+
parse_c_flow_json_content(n, c)
|
2124
|
+
end
|
2125
|
+
|
2126
|
+
# [159]
|
2127
|
+
# ns-flow-yaml-node(n,c) ::=
|
2128
|
+
# c-ns-alias-node
|
2129
|
+
# | ns-flow-yaml-content(n,c)
|
2130
|
+
# | ( c-ns-properties(n,c)
|
2131
|
+
# ( ( s-separate(n,c)
|
2132
|
+
# ns-flow-yaml-content(n,c) )
|
2133
|
+
# | e-scalar ) )
|
2134
|
+
def parse_ns_flow_yaml_node(n, c)
|
2135
|
+
parse_c_ns_alias_node ||
|
2136
|
+
parse_ns_flow_yaml_content(n, c) ||
|
2137
|
+
try do
|
2138
|
+
parse_c_ns_properties(n, c) &&
|
2139
|
+
(try { parse_s_separate(n, c) && parse_ns_flow_content(n, c) } || parse_e_scalar)
|
2140
|
+
end
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
# [160]
|
2144
|
+
# c-flow-json-node(n,c) ::=
|
2145
|
+
# ( c-ns-properties(n,c)
|
2146
|
+
# s-separate(n,c) )?
|
2147
|
+
# c-flow-json-content(n,c)
|
2148
|
+
def parse_c_flow_json_node(n, c)
|
2149
|
+
try do
|
2150
|
+
try { parse_c_ns_properties(n, c) && parse_s_separate(n, c) }
|
2151
|
+
parse_c_flow_json_content(n, c)
|
2152
|
+
end
|
2153
|
+
end
|
2154
|
+
|
2155
|
+
# [161]
|
2156
|
+
# ns-flow-node(n,c) ::=
|
2157
|
+
# c-ns-alias-node
|
2158
|
+
# | ns-flow-content(n,c)
|
2159
|
+
# | ( c-ns-properties(n,c)
|
2160
|
+
# ( ( s-separate(n,c)
|
2161
|
+
# ns-flow-content(n,c) )
|
2162
|
+
# | e-scalar ) )
|
2163
|
+
def parse_ns_flow_node(n, c)
|
2164
|
+
parse_c_ns_alias_node ||
|
2165
|
+
parse_ns_flow_content(n, c) ||
|
2166
|
+
try do
|
2167
|
+
parse_c_ns_properties(n, c) &&
|
2168
|
+
(try { parse_s_separate(n, c) && parse_ns_flow_content(n, c) } || parse_e_scalar)
|
2169
|
+
end
|
2170
|
+
end
|
2171
|
+
|
2172
|
+
# [162]
|
2173
|
+
# c-b-block-header(m,t) ::=
|
2174
|
+
# ( ( c-indentation-indicator(m)
|
2175
|
+
# c-chomping-indicator(t) )
|
2176
|
+
# | ( c-chomping-indicator(t)
|
2177
|
+
# c-indentation-indicator(m) ) )
|
2178
|
+
# s-b-comment
|
2179
|
+
def parse_c_b_block_header(n)
|
2180
|
+
m = nil
|
2181
|
+
t = nil
|
2182
|
+
|
2183
|
+
result =
|
2184
|
+
try do
|
2185
|
+
(
|
2186
|
+
try do
|
2187
|
+
(m = parse_c_indentation_indicator(n)) &&
|
2188
|
+
(t = parse_c_chomping_indicator) &&
|
2189
|
+
peek { @scanner.eos? || parse_s_white || parse_b_break }
|
2190
|
+
end ||
|
2191
|
+
try do
|
2192
|
+
(t = parse_c_chomping_indicator) &&
|
2193
|
+
(m = parse_c_indentation_indicator(n)) &&
|
2194
|
+
peek { @scanner.eos? || parse_s_white || parse_b_break }
|
2195
|
+
end
|
2196
|
+
) && parse_s_b_comment
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
result ? [m, t] : false
|
2200
|
+
end
|
2201
|
+
|
2202
|
+
# [163]
|
2203
|
+
# c-indentation-indicator(m) ::=
|
2204
|
+
# ( ns-dec-digit => m = ns-dec-digit - x:30 )
|
2205
|
+
# ( <empty> => m = auto-detect() )
|
2206
|
+
def parse_c_indentation_indicator(n)
|
2207
|
+
pos_start = @scanner.pos
|
2208
|
+
|
2209
|
+
if match(/[\u{31}-\u{39}]/)
|
2210
|
+
Integer(from(pos_start))
|
2211
|
+
else
|
2212
|
+
@scanner.check(/.*\n((?:\ *\n)*)(\ *)(.?)/)
|
2213
|
+
|
2214
|
+
pre = @scanner[1]
|
2215
|
+
if !@scanner[3].empty?
|
2216
|
+
m = @scanner[2].length - n
|
2217
|
+
else
|
2218
|
+
m = 0
|
2219
|
+
while pre.match?(/\ {#{m}}/)
|
2220
|
+
m += 1
|
2221
|
+
end
|
2222
|
+
m = m - n - 1
|
2223
|
+
end
|
2224
|
+
|
2225
|
+
if m > 0 && pre.match?(/^.{#{m + n}}\ /)
|
2226
|
+
raise_syntax_error("Invalid indentation indicator")
|
2227
|
+
end
|
2228
|
+
|
2229
|
+
m == 0 ? 1 : m
|
2230
|
+
end
|
2231
|
+
end
|
2232
|
+
|
2233
|
+
# [164]
|
2234
|
+
# c-chomping-indicator(t) ::=
|
2235
|
+
# ( '-' => t = strip )
|
2236
|
+
# ( '+' => t = keep )
|
2237
|
+
# ( <empty> => t = clip )
|
2238
|
+
def parse_c_chomping_indicator
|
2239
|
+
if match("-") then :strip
|
2240
|
+
elsif match("+") then :keep
|
2241
|
+
else :clip
|
2242
|
+
end
|
2243
|
+
end
|
2244
|
+
|
2245
|
+
# [165]
|
2246
|
+
# b-chomped-last(t) ::=
|
2247
|
+
# ( t = strip => b-non-content | <end_of_file> )
|
2248
|
+
# ( t = clip => b-as-line-feed | <end_of_file> )
|
2249
|
+
# ( t = keep => b-as-line-feed | <end_of_file> )
|
2250
|
+
def parse_b_chomped_last(t)
|
2251
|
+
case t
|
2252
|
+
when :clip then parse_b_as_line_feed || @scanner.eos?
|
2253
|
+
when :keep then parse_b_as_line_feed || @scanner.eos?
|
2254
|
+
when :strip then parse_b_non_content || @scanner.eos?
|
2255
|
+
else raise InternalException, t.inspect
|
2256
|
+
end
|
2257
|
+
end
|
2258
|
+
|
2259
|
+
# [166]
|
2260
|
+
# l-chomped-empty(n,t) ::=
|
2261
|
+
# ( t = strip => l-strip-empty(n) )
|
2262
|
+
# ( t = clip => l-strip-empty(n) )
|
2263
|
+
# ( t = keep => l-keep-empty(n) )
|
2264
|
+
#
|
2265
|
+
# [167]
|
2266
|
+
# l-strip-empty(n) ::=
|
2267
|
+
# ( s-indent(<=n) b-non-content )*
|
2268
|
+
# l-trail-comments(n)?
|
2269
|
+
#
|
2270
|
+
# [168]
|
2271
|
+
# l-keep-empty(n) ::=
|
2272
|
+
# l-empty(n,block-in)*
|
2273
|
+
# l-trail-comments(n)?
|
2274
|
+
def parse_l_chomped_empty(n, t)
|
2275
|
+
case t
|
2276
|
+
when :clip, :strip
|
2277
|
+
try do
|
2278
|
+
if star { try { parse_s_indent_le(n) && parse_b_non_content } }
|
2279
|
+
parse_l_trail_comments(n)
|
2280
|
+
true
|
2281
|
+
end
|
2282
|
+
end
|
2283
|
+
when :keep
|
2284
|
+
try do
|
2285
|
+
if star { parse_l_empty(n, :block_in) }
|
2286
|
+
parse_l_trail_comments(n)
|
2287
|
+
true
|
2288
|
+
end
|
2289
|
+
end
|
2290
|
+
else
|
2291
|
+
raise InternalException, t.inspect
|
2292
|
+
end
|
2293
|
+
end
|
2294
|
+
|
2295
|
+
# [169]
|
2296
|
+
# l-trail-comments(n) ::=
|
2297
|
+
# s-indent(<n)
|
2298
|
+
# c-nb-comment-text b-comment
|
2299
|
+
# l-comment*
|
2300
|
+
def parse_l_trail_comments(n)
|
2301
|
+
try do
|
2302
|
+
parse_s_indent_lt(n) &&
|
2303
|
+
parse_c_nb_comment_text(false) &&
|
2304
|
+
parse_b_comment &&
|
2305
|
+
star { parse_l_comment }
|
2306
|
+
end
|
2307
|
+
end
|
2308
|
+
|
2309
|
+
# [170]
|
2310
|
+
# c-l+literal(n) ::=
|
2311
|
+
# '|' c-b-block-header(m,t)
|
2312
|
+
# l-literal-content(n+m,t)
|
2313
|
+
def parse_c_l_literal(n)
|
2314
|
+
@in_scalar = true
|
2315
|
+
events_cache_push
|
2316
|
+
|
2317
|
+
m = nil
|
2318
|
+
t = nil
|
2319
|
+
pos_start = @scanner.pos
|
2320
|
+
|
2321
|
+
if try {
|
2322
|
+
match("|") &&
|
2323
|
+
(m, t = parse_c_b_block_header(n)) &&
|
2324
|
+
parse_l_literal_content(n + m, t)
|
2325
|
+
} then
|
2326
|
+
@in_scalar = false
|
2327
|
+
lines = events_cache_pop
|
2328
|
+
lines.pop if lines.length > 0 && lines.last.empty?
|
2329
|
+
value = lines.map { |line| "#{line}\n" }.join
|
2330
|
+
|
2331
|
+
case t
|
2332
|
+
when :clip
|
2333
|
+
value.sub!(/\n+\z/, "\n")
|
2334
|
+
when :strip
|
2335
|
+
value.sub!(/\n+\z/, "")
|
2336
|
+
when :keep
|
2337
|
+
value.sub!(/\n(\n+)\z/) { $1 } if !value.match?(/\S/)
|
2338
|
+
else
|
2339
|
+
raise InternalException, t.inspect
|
2340
|
+
end
|
2341
|
+
|
2342
|
+
events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), value, Nodes::Scalar::LITERAL))
|
2343
|
+
true
|
2344
|
+
else
|
2345
|
+
@in_scalar = false
|
2346
|
+
events_cache_pop
|
2347
|
+
false
|
2348
|
+
end
|
2349
|
+
end
|
2350
|
+
|
2351
|
+
# [171]
|
2352
|
+
# l-nb-literal-text(n) ::=
|
2353
|
+
# l-empty(n,block-in)*
|
2354
|
+
# s-indent(n) nb-char+
|
2355
|
+
def parse_l_nb_literal_text(n)
|
2356
|
+
try do
|
2357
|
+
if star { parse_l_empty(n, :block_in) } && parse_s_indent(n)
|
2358
|
+
pos_start = @scanner.pos
|
2359
|
+
|
2360
|
+
if plus { parse_nb_char }
|
2361
|
+
events_push(from(pos_start))
|
2362
|
+
true
|
2363
|
+
end
|
2364
|
+
end
|
2365
|
+
end
|
2366
|
+
end
|
2367
|
+
|
2368
|
+
# [172]
|
2369
|
+
# b-nb-literal-next(n) ::=
|
2370
|
+
# b-as-line-feed
|
2371
|
+
# l-nb-literal-text(n)
|
2372
|
+
def parse_b_nb_literal_next(n)
|
2373
|
+
try { parse_b_as_line_feed && parse_l_nb_literal_text(n) }
|
2374
|
+
end
|
2375
|
+
|
2376
|
+
# [173]
|
2377
|
+
# l-literal-content(n,t) ::=
|
2378
|
+
# ( l-nb-literal-text(n)
|
2379
|
+
# b-nb-literal-next(n)*
|
2380
|
+
# b-chomped-last(t) )?
|
2381
|
+
# l-chomped-empty(n,t)
|
2382
|
+
def parse_l_literal_content(n, t)
|
2383
|
+
try do
|
2384
|
+
try do
|
2385
|
+
parse_l_nb_literal_text(n) &&
|
2386
|
+
star { parse_b_nb_literal_next(n) } &&
|
2387
|
+
parse_b_chomped_last(t)
|
2388
|
+
end
|
2389
|
+
|
2390
|
+
parse_l_chomped_empty(n, t)
|
2391
|
+
end
|
2392
|
+
end
|
2393
|
+
|
2394
|
+
# [174]
|
2395
|
+
# c-l+folded(n) ::=
|
2396
|
+
# '>' c-b-block-header(m,t)
|
2397
|
+
# l-folded-content(n+m,t)
|
2398
|
+
def parse_c_l_folded(n)
|
2399
|
+
@in_scalar = true
|
2400
|
+
@text_prefix.clear
|
2401
|
+
events_cache_push
|
2402
|
+
|
2403
|
+
m = nil
|
2404
|
+
t = nil
|
2405
|
+
pos_start = @scanner.pos
|
2406
|
+
|
2407
|
+
if try {
|
2408
|
+
match(">") &&
|
2409
|
+
(m, t = parse_c_b_block_header(n)) &&
|
2410
|
+
parse_l_folded_content(n + m, t)
|
2411
|
+
} then
|
2412
|
+
@in_scalar = false
|
2413
|
+
|
2414
|
+
value = events_cache_pop.join("\n")
|
2415
|
+
value.gsub!(/^(\S.*)\n(?=\S)/) { "#{$1} " }
|
2416
|
+
value.gsub!(/^(\S.*)\n(\n+)/) { "#{$1}#{$2}" }
|
2417
|
+
value.gsub!(/^([\ \t]+\S.*)\n(\n+)(?=\S)/) { "#{$1}#{$2}" }
|
2418
|
+
value << "\n"
|
2419
|
+
|
2420
|
+
case t
|
2421
|
+
when :clip
|
2422
|
+
value.sub!(/\n+\z/, "\n")
|
2423
|
+
value.clear if value == "\n"
|
2424
|
+
when :strip
|
2425
|
+
value.sub!(/\n+\z/, "")
|
2426
|
+
when :keep
|
2427
|
+
# nothing
|
2428
|
+
else
|
2429
|
+
raise InternalException, t.inspect
|
2430
|
+
end
|
2431
|
+
|
2432
|
+
events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), value, Nodes::Scalar::FOLDED))
|
2433
|
+
true
|
2434
|
+
else
|
2435
|
+
@in_scalar = false
|
2436
|
+
events_cache_pop
|
2437
|
+
false
|
2438
|
+
end
|
2439
|
+
end
|
2440
|
+
|
2441
|
+
# [175]
|
2442
|
+
# s-nb-folded-text(n) ::=
|
2443
|
+
# s-indent(n) ns-char
|
2444
|
+
# nb-char*
|
2445
|
+
def parse_s_nb_folded_text(n)
|
2446
|
+
try do
|
2447
|
+
if parse_s_indent(n) && parse_ns_char
|
2448
|
+
pos_start = @scanner.pos
|
2449
|
+
|
2450
|
+
if star { parse_nb_char }
|
2451
|
+
events_push("#{@text_prefix}#{from(pos_start)}")
|
2452
|
+
true
|
2453
|
+
end
|
2454
|
+
end
|
2455
|
+
end
|
2456
|
+
end
|
2457
|
+
|
2458
|
+
# [176]
|
2459
|
+
# l-nb-folded-lines(n) ::=
|
2460
|
+
# s-nb-folded-text(n)
|
2461
|
+
# ( b-l-folded(n,block-in) s-nb-folded-text(n) )*
|
2462
|
+
def parse_l_nb_folded_lines(n)
|
2463
|
+
try do
|
2464
|
+
parse_s_nb_folded_text(n) &&
|
2465
|
+
star { try { parse_b_l_folded(n, :block_in) && parse_s_nb_folded_text(n) } }
|
2466
|
+
end
|
2467
|
+
end
|
2468
|
+
|
2469
|
+
# [177]
|
2470
|
+
# s-nb-spaced-text(n) ::=
|
2471
|
+
# s-indent(n) s-white
|
2472
|
+
# nb-char*
|
2473
|
+
def parse_s_nb_spaced_text(n)
|
2474
|
+
try do
|
2475
|
+
if parse_s_indent(n) && parse_s_white
|
2476
|
+
pos_start = @scanner.pos
|
2477
|
+
star { parse_nb_char }
|
2478
|
+
events_push("#{@text_prefix}#{from(pos_start)}")
|
2479
|
+
true
|
2480
|
+
end
|
2481
|
+
end
|
2482
|
+
end
|
2483
|
+
|
2484
|
+
# [178]
|
2485
|
+
# b-l-spaced(n) ::=
|
2486
|
+
# b-as-line-feed
|
2487
|
+
# l-empty(n,block-in)*
|
2488
|
+
def parse_b_l_spaced(n)
|
2489
|
+
try { parse_b_as_line_feed && star { parse_l_empty(n, :block_in) } }
|
2490
|
+
end
|
2491
|
+
|
2492
|
+
# [179]
|
2493
|
+
# l-nb-spaced-lines(n) ::=
|
2494
|
+
# s-nb-spaced-text(n)
|
2495
|
+
# ( b-l-spaced(n) s-nb-spaced-text(n) )*
|
2496
|
+
def parse_l_nb_spaced_lines(n)
|
2497
|
+
try do
|
2498
|
+
parse_s_nb_spaced_text(n) &&
|
2499
|
+
star { try { parse_b_l_spaced(n) && parse_s_nb_spaced_text(n) } }
|
2500
|
+
end
|
2501
|
+
end
|
2502
|
+
|
2503
|
+
# [180]
|
2504
|
+
# l-nb-same-lines(n) ::=
|
2505
|
+
# l-empty(n,block-in)*
|
2506
|
+
# ( l-nb-folded-lines(n) | l-nb-spaced-lines(n) )
|
2507
|
+
def parse_l_nb_same_lines(n)
|
2508
|
+
try do
|
2509
|
+
star { parse_l_empty(n, :block_in) }
|
2510
|
+
parse_l_nb_folded_lines(n) || parse_l_nb_spaced_lines(n)
|
2511
|
+
end
|
2512
|
+
end
|
2513
|
+
|
2514
|
+
# [181]
|
2515
|
+
# l-nb-diff-lines(n) ::=
|
2516
|
+
# l-nb-same-lines(n)
|
2517
|
+
# ( b-as-line-feed l-nb-same-lines(n) )*
|
2518
|
+
def parse_l_nb_diff_lines(n)
|
2519
|
+
try do
|
2520
|
+
parse_l_nb_same_lines(n) &&
|
2521
|
+
star { try { parse_b_as_line_feed && parse_l_nb_same_lines(n) } }
|
2522
|
+
end
|
2523
|
+
end
|
2524
|
+
|
2525
|
+
# [182]
|
2526
|
+
# l-folded-content(n,t) ::=
|
2527
|
+
# ( l-nb-diff-lines(n)
|
2528
|
+
# b-chomped-last(t) )?
|
2529
|
+
# l-chomped-empty(n,t)
|
2530
|
+
def parse_l_folded_content(n, t)
|
2531
|
+
try do
|
2532
|
+
try { parse_l_nb_diff_lines(n) && parse_b_chomped_last(t) }
|
2533
|
+
parse_l_chomped_empty(n, t)
|
2534
|
+
end
|
2535
|
+
end
|
2536
|
+
|
2537
|
+
# [183]
|
2538
|
+
# l+block-sequence(n) ::=
|
2539
|
+
# ( s-indent(n+m)
|
2540
|
+
# c-l-block-seq-entry(n+m) )+
|
2541
|
+
# <for_some_fixed_auto-detected_m_>_0>
|
2542
|
+
def parse_l_block_sequence(n)
|
2543
|
+
return false if (m = detect_indent(n)) == 0
|
2544
|
+
|
2545
|
+
events_cache_push
|
2546
|
+
events_push_flush_properties(SequenceStart.new(Location.point(@source, @scanner.pos), Nodes::Sequence::BLOCK))
|
2547
|
+
|
2548
|
+
if try { plus { try { parse_s_indent(n + m) && parse_c_l_block_seq_entry(n + m) } } }
|
2549
|
+
events_cache_flush
|
2550
|
+
events_push_flush_properties(SequenceEnd.new(Location.point(@source, @scanner.pos)))
|
2551
|
+
true
|
2552
|
+
else
|
2553
|
+
event = events_cache_pop[0]
|
2554
|
+
@anchor = event.anchor
|
2555
|
+
@tag = event.tag
|
2556
|
+
false
|
2557
|
+
end
|
2558
|
+
end
|
2559
|
+
|
2560
|
+
# [004]
|
2561
|
+
# c-sequence-entry ::=
|
2562
|
+
# '-'
|
2563
|
+
#
|
2564
|
+
# [184]
|
2565
|
+
# c-l-block-seq-entry(n) ::=
|
2566
|
+
# '-' <not_followed_by_an_ns-char>
|
2567
|
+
# s-l+block-indented(n,block-in)
|
2568
|
+
def parse_c_l_block_seq_entry(n)
|
2569
|
+
try do
|
2570
|
+
match("-") &&
|
2571
|
+
!peek { parse_ns_char } &&
|
2572
|
+
parse_s_l_block_indented(n, :block_in)
|
2573
|
+
end
|
2574
|
+
end
|
2575
|
+
|
2576
|
+
# [185]
|
2577
|
+
# s-l+block-indented(n,c) ::=
|
2578
|
+
# ( s-indent(m)
|
2579
|
+
# ( ns-l-compact-sequence(n+1+m)
|
2580
|
+
# | ns-l-compact-mapping(n+1+m) ) )
|
2581
|
+
# | s-l+block-node(n,c)
|
2582
|
+
# | ( e-node s-l-comments )
|
2583
|
+
def parse_s_l_block_indented(n, c)
|
2584
|
+
m = detect_indent(n)
|
2585
|
+
|
2586
|
+
try do
|
2587
|
+
parse_s_indent(m) &&
|
2588
|
+
(parse_ns_l_compact_sequence(n + 1 + m) || parse_ns_l_compact_mapping(n + 1 + m))
|
2589
|
+
end || parse_s_l_block_node(n, c) || try { parse_e_node && parse_s_l_comments }
|
2590
|
+
end
|
2591
|
+
|
2592
|
+
# [186]
|
2593
|
+
# ns-l-compact-sequence(n) ::=
|
2594
|
+
# c-l-block-seq-entry(n)
|
2595
|
+
# ( s-indent(n) c-l-block-seq-entry(n) )*
|
2596
|
+
def parse_ns_l_compact_sequence(n)
|
2597
|
+
events_cache_push
|
2598
|
+
events_push_flush_properties(SequenceStart.new(Location.point(@source, @scanner.pos), Nodes::Sequence::BLOCK))
|
2599
|
+
|
2600
|
+
if try {
|
2601
|
+
parse_c_l_block_seq_entry(n) &&
|
2602
|
+
star { try { parse_s_indent(n) && parse_c_l_block_seq_entry(n) } }
|
2603
|
+
} then
|
2604
|
+
events_cache_flush
|
2605
|
+
events_push_flush_properties(SequenceEnd.new(Location.point(@source, @scanner.pos)))
|
2606
|
+
true
|
2607
|
+
else
|
2608
|
+
events_cache_pop
|
2609
|
+
false
|
2610
|
+
end
|
2611
|
+
end
|
2612
|
+
|
2613
|
+
# [187]
|
2614
|
+
# l+block-mapping(n) ::=
|
2615
|
+
# ( s-indent(n+m)
|
2616
|
+
# ns-l-block-map-entry(n+m) )+
|
2617
|
+
# <for_some_fixed_auto-detected_m_>_0>
|
2618
|
+
def parse_l_block_mapping(n)
|
2619
|
+
return false if (m = detect_indent(n)) == 0
|
2620
|
+
|
2621
|
+
events_cache_push
|
2622
|
+
events_push_flush_properties(MappingStart.new(Location.point(@source, @scanner.pos), Nodes::Mapping::BLOCK))
|
2623
|
+
|
2624
|
+
if try { plus { try { parse_s_indent(n + m) && parse_ns_l_block_map_entry(n + m) } } }
|
2625
|
+
events_cache_flush
|
2626
|
+
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
|
2627
|
+
true
|
2628
|
+
else
|
2629
|
+
events_cache_pop
|
2630
|
+
false
|
2631
|
+
end
|
2632
|
+
end
|
2633
|
+
|
2634
|
+
# [188]
|
2635
|
+
# ns-l-block-map-entry(n) ::=
|
2636
|
+
# c-l-block-map-explicit-entry(n)
|
2637
|
+
# | ns-l-block-map-implicit-entry(n)
|
2638
|
+
def parse_ns_l_block_map_entry(n)
|
2639
|
+
parse_c_l_block_map_explicit_entry(n) ||
|
2640
|
+
parse_ns_l_block_map_implicit_entry(n)
|
2641
|
+
end
|
2642
|
+
|
2643
|
+
# [189]
|
2644
|
+
# c-l-block-map-explicit-entry(n) ::=
|
2645
|
+
# c-l-block-map-explicit-key(n)
|
2646
|
+
# ( l-block-map-explicit-value(n)
|
2647
|
+
# | e-node )
|
2648
|
+
def parse_c_l_block_map_explicit_entry(n)
|
2649
|
+
events_cache_push
|
2650
|
+
|
2651
|
+
if try {
|
2652
|
+
parse_c_l_block_map_explicit_key(n) &&
|
2653
|
+
(parse_l_block_map_explicit_value(n) || parse_e_node)
|
2654
|
+
} then
|
2655
|
+
events_cache_flush
|
2656
|
+
true
|
2657
|
+
else
|
2658
|
+
events_cache_pop
|
2659
|
+
false
|
2660
|
+
end
|
2661
|
+
end
|
2662
|
+
|
2663
|
+
# [190]
|
2664
|
+
# c-l-block-map-explicit-key(n) ::=
|
2665
|
+
# '?'
|
2666
|
+
# s-l+block-indented(n,block-out)
|
2667
|
+
def parse_c_l_block_map_explicit_key(n)
|
2668
|
+
try do
|
2669
|
+
match("?") &&
|
2670
|
+
peek { @scanner.eos? || parse_s_white || parse_b_break } &&
|
2671
|
+
parse_s_l_block_indented(n, :block_out)
|
2672
|
+
end
|
2673
|
+
end
|
2674
|
+
|
2675
|
+
# [191]
|
2676
|
+
# l-block-map-explicit-value(n) ::=
|
2677
|
+
# s-indent(n)
|
2678
|
+
# ':' s-l+block-indented(n,block-out)
|
2679
|
+
def parse_l_block_map_explicit_value(n)
|
2680
|
+
try do
|
2681
|
+
parse_s_indent(n) &&
|
2682
|
+
match(":") &&
|
2683
|
+
parse_s_l_block_indented(n, :block_out)
|
2684
|
+
end
|
2685
|
+
end
|
2686
|
+
|
2687
|
+
# [192]
|
2688
|
+
# ns-l-block-map-implicit-entry(n) ::=
|
2689
|
+
# (
|
2690
|
+
# ns-s-block-map-implicit-key
|
2691
|
+
# | e-node )
|
2692
|
+
# c-l-block-map-implicit-value(n)
|
2693
|
+
def parse_ns_l_block_map_implicit_entry(n)
|
2694
|
+
events_cache_push
|
2695
|
+
|
2696
|
+
if try {
|
2697
|
+
(parse_ns_s_block_map_implicit_key || parse_e_node) &&
|
2698
|
+
parse_c_l_block_map_implicit_value(n)
|
2699
|
+
} then
|
2700
|
+
events_cache_flush
|
2701
|
+
true
|
2702
|
+
else
|
2703
|
+
events_cache_pop
|
2704
|
+
false
|
2705
|
+
end
|
2706
|
+
end
|
2707
|
+
|
2708
|
+
# [193]
|
2709
|
+
# ns-s-block-map-implicit-key ::=
|
2710
|
+
# c-s-implicit-json-key(block-key)
|
2711
|
+
# | ns-s-implicit-yaml-key(block-key)
|
2712
|
+
def parse_ns_s_block_map_implicit_key
|
2713
|
+
parse_c_s_implicit_json_key(:block_key) ||
|
2714
|
+
parse_ns_s_implicit_yaml_key(:block_key)
|
2715
|
+
end
|
2716
|
+
|
2717
|
+
# [194]
|
2718
|
+
# c-l-block-map-implicit-value(n) ::=
|
2719
|
+
# ':' (
|
2720
|
+
# s-l+block-node(n,block-out)
|
2721
|
+
# | ( e-node s-l-comments ) )
|
2722
|
+
def parse_c_l_block_map_implicit_value(n)
|
2723
|
+
try do
|
2724
|
+
match(":") &&
|
2725
|
+
(parse_s_l_block_node(n, :block_out) || try { parse_e_node && parse_s_l_comments })
|
2726
|
+
end
|
2727
|
+
end
|
2728
|
+
|
2729
|
+
# [195]
|
2730
|
+
# ns-l-compact-mapping(n) ::=
|
2731
|
+
# ns-l-block-map-entry(n)
|
2732
|
+
# ( s-indent(n) ns-l-block-map-entry(n) )*
|
2733
|
+
def parse_ns_l_compact_mapping(n)
|
2734
|
+
events_cache_push
|
2735
|
+
events_push_flush_properties(MappingStart.new(Location.point(@source, @scanner.pos), Nodes::Mapping::BLOCK))
|
2736
|
+
|
2737
|
+
if try {
|
2738
|
+
parse_ns_l_block_map_entry(n) &&
|
2739
|
+
star { try { parse_s_indent(n) && parse_ns_l_block_map_entry(n) } }
|
2740
|
+
} then
|
2741
|
+
events_cache_flush
|
2742
|
+
events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
|
2743
|
+
true
|
2744
|
+
else
|
2745
|
+
events_cache_pop
|
2746
|
+
false
|
2747
|
+
end
|
2748
|
+
end
|
2749
|
+
|
2750
|
+
# [196]
|
2751
|
+
# s-l+block-node(n,c) ::=
|
2752
|
+
# s-l+block-in-block(n,c) | s-l+flow-in-block(n)
|
2753
|
+
def parse_s_l_block_node(n, c)
|
2754
|
+
parse_s_l_block_in_block(n, c) || parse_s_l_flow_in_block(n)
|
2755
|
+
end
|
2756
|
+
|
2757
|
+
# [197]
|
2758
|
+
# s-l+flow-in-block(n) ::=
|
2759
|
+
# s-separate(n+1,flow-out)
|
2760
|
+
# ns-flow-node(n+1,flow-out) s-l-comments
|
2761
|
+
def parse_s_l_flow_in_block(n)
|
2762
|
+
try do
|
2763
|
+
parse_s_separate(n + 1, :flow_out) &&
|
2764
|
+
parse_ns_flow_node(n + 1, :flow_out) &&
|
2765
|
+
parse_s_l_comments
|
2766
|
+
end
|
2767
|
+
end
|
2768
|
+
|
2769
|
+
# [198]
|
2770
|
+
# s-l+block-in-block(n,c) ::=
|
2771
|
+
# s-l+block-scalar(n,c) | s-l+block-collection(n,c)
|
2772
|
+
def parse_s_l_block_in_block(n, c)
|
2773
|
+
parse_s_l_block_scalar(n, c) || parse_s_l_block_collection(n, c)
|
2774
|
+
end
|
2775
|
+
|
2776
|
+
# [199]
|
2777
|
+
# s-l+block-scalar(n,c) ::=
|
2778
|
+
# s-separate(n+1,c)
|
2779
|
+
# ( c-ns-properties(n+1,c) s-separate(n+1,c) )?
|
2780
|
+
# ( c-l+literal(n) | c-l+folded(n) )
|
2781
|
+
def parse_s_l_block_scalar(n, c)
|
2782
|
+
try do
|
2783
|
+
if parse_s_separate(n + 1, c)
|
2784
|
+
try { parse_c_ns_properties(n + 1, c) && parse_s_separate(n + 1, c) }
|
2785
|
+
parse_c_l_literal(n) || parse_c_l_folded(n)
|
2786
|
+
end
|
2787
|
+
end
|
2788
|
+
end
|
2789
|
+
|
2790
|
+
# [200]
|
2791
|
+
# s-l+block-collection(n,c) ::=
|
2792
|
+
# ( s-separate(n+1,c)
|
2793
|
+
# c-ns-properties(n+1,c) )?
|
2794
|
+
# s-l-comments
|
2795
|
+
# ( l+block-sequence(seq-spaces(n,c))
|
2796
|
+
# | l+block-mapping(n) )
|
2797
|
+
def parse_s_l_block_collection(n, c)
|
2798
|
+
try do
|
2799
|
+
try do
|
2800
|
+
next false if !parse_s_separate(n + 1, c)
|
2801
|
+
|
2802
|
+
next true if try { parse_c_ns_properties(n + 1, c) && parse_s_l_comments }
|
2803
|
+
@tag = nil
|
2804
|
+
@anchor = nil
|
2805
|
+
|
2806
|
+
next true if try { parse_c_ns_tag_property && parse_s_l_comments }
|
2807
|
+
@tag = nil
|
2808
|
+
|
2809
|
+
next true if try { parse_c_ns_anchor_property && parse_s_l_comments }
|
2810
|
+
@anchor = nil
|
2811
|
+
|
2812
|
+
false
|
2813
|
+
end
|
2814
|
+
|
2815
|
+
parse_s_l_comments && (parse_l_block_sequence(parse_seq_spaces(n, c)) || parse_l_block_mapping(n))
|
2816
|
+
end
|
2817
|
+
end
|
2818
|
+
|
2819
|
+
# [201]
|
2820
|
+
# seq-spaces(n,c) ::=
|
2821
|
+
# ( c = block-out => n-1 )
|
2822
|
+
# ( c = block-in => n )
|
2823
|
+
def parse_seq_spaces(n, c)
|
2824
|
+
case c
|
2825
|
+
when :block_in then n
|
2826
|
+
when :block_out then n - 1
|
2827
|
+
else raise InternalException, c.inspect
|
2828
|
+
end
|
2829
|
+
end
|
2830
|
+
|
2831
|
+
# [003]
|
2832
|
+
# c-byte-order-mark ::=
|
2833
|
+
# x:FEFF
|
2834
|
+
#
|
2835
|
+
# [202]
|
2836
|
+
# l-document-prefix ::=
|
2837
|
+
# c-byte-order-mark? l-comment*
|
2838
|
+
def parse_l_document_prefix
|
2839
|
+
try do
|
2840
|
+
@scanner.skip("\u{FEFF}")
|
2841
|
+
star { parse_l_comment }
|
2842
|
+
end
|
2843
|
+
end
|
2844
|
+
|
2845
|
+
# [203]
|
2846
|
+
# c-directives-end ::=
|
2847
|
+
# '-' '-' '-'
|
2848
|
+
def parse_c_directives_end
|
2849
|
+
if try { match("---") && peek { @scanner.eos? || parse_s_white || parse_b_break } }
|
2850
|
+
document_end_event_flush
|
2851
|
+
@document_start_event.implicit = false
|
2852
|
+
true
|
2853
|
+
end
|
2854
|
+
end
|
2855
|
+
|
2856
|
+
# [204]
|
2857
|
+
# c-document-end ::=
|
2858
|
+
# '.' '.' '.'
|
2859
|
+
def parse_c_document_end
|
2860
|
+
if match("...")
|
2861
|
+
@document_end_event.implicit = false if @document_end_event
|
2862
|
+
document_end_event_flush
|
2863
|
+
true
|
2864
|
+
end
|
2865
|
+
end
|
2866
|
+
|
2867
|
+
# [205]
|
2868
|
+
# l-document-suffix ::=
|
2869
|
+
# c-document-end s-l-comments
|
2870
|
+
def parse_l_document_suffix
|
2871
|
+
try { parse_c_document_end && parse_s_l_comments }
|
2872
|
+
end
|
2873
|
+
|
2874
|
+
# [207]
|
2875
|
+
# l-bare-document ::=
|
2876
|
+
# s-l+block-node(-1,block-in)
|
2877
|
+
# <excluding_c-forbidden_content>
|
2878
|
+
def parse_l_bare_document
|
2879
|
+
previous = @in_bare_document
|
2880
|
+
@in_bare_document = true
|
2881
|
+
|
2882
|
+
result =
|
2883
|
+
try do
|
2884
|
+
!try { start_of_line? && (parse_c_directives_end || parse_c_document_end) && (match(/[\u{0A}\u{0D}]/) || parse_s_white || @scanner.eos?) } &&
|
2885
|
+
parse_s_l_block_node(-1, :block_in)
|
2886
|
+
end
|
2887
|
+
|
2888
|
+
@in_bare_document = previous
|
2889
|
+
result
|
2890
|
+
end
|
2891
|
+
|
2892
|
+
# [208]
|
2893
|
+
# l-explicit-document ::=
|
2894
|
+
# c-directives-end
|
2895
|
+
# ( l-bare-document
|
2896
|
+
# | ( e-node s-l-comments ) )
|
2897
|
+
def parse_l_explicit_document
|
2898
|
+
try do
|
2899
|
+
parse_c_directives_end &&
|
2900
|
+
(parse_l_bare_document || try { parse_e_node && parse_s_l_comments })
|
2901
|
+
end
|
2902
|
+
end
|
2903
|
+
|
2904
|
+
# [209]
|
2905
|
+
# l-directive-document ::=
|
2906
|
+
# l-directive+
|
2907
|
+
# l-explicit-document
|
2908
|
+
#
|
2909
|
+
# [210]
|
2910
|
+
# l-any-document ::=
|
2911
|
+
# l-directive-document
|
2912
|
+
# | l-explicit-document
|
2913
|
+
# | l-bare-document
|
2914
|
+
def parse_l_any_document
|
2915
|
+
try { plus { parse_l_directive } && parse_l_explicit_document } ||
|
2916
|
+
parse_l_explicit_document ||
|
2917
|
+
parse_l_bare_document
|
2918
|
+
end
|
2919
|
+
|
2920
|
+
# [211]
|
2921
|
+
# l-yaml-stream ::=
|
2922
|
+
# l-document-prefix* l-any-document?
|
2923
|
+
# ( ( l-document-suffix+ l-document-prefix*
|
2924
|
+
# l-any-document? )
|
2925
|
+
# | ( l-document-prefix* l-explicit-document? ) )*
|
2926
|
+
def parse_l_yaml_stream
|
2927
|
+
events_push_flush_properties(StreamStart.new(Location.point(@source, @scanner.pos)))
|
2928
|
+
|
2929
|
+
@document_start_event = DocumentStart.new(Location.point(@source, @scanner.pos))
|
2930
|
+
@tag_directives = @document_start_event.tag_directives
|
2931
|
+
@document_end_event = nil
|
2932
|
+
|
2933
|
+
if try {
|
2934
|
+
if parse_l_document_prefix
|
2935
|
+
parse_l_any_document
|
2936
|
+
star do
|
2937
|
+
try do
|
2938
|
+
if parse_l_document_suffix
|
2939
|
+
star { parse_l_document_prefix }
|
2940
|
+
parse_l_any_document
|
2941
|
+
true
|
2942
|
+
end
|
2943
|
+
end ||
|
2944
|
+
try do
|
2945
|
+
if parse_l_document_prefix
|
2946
|
+
parse_l_explicit_document
|
2947
|
+
true
|
2948
|
+
end
|
2949
|
+
end
|
2950
|
+
end
|
2951
|
+
end
|
2952
|
+
} then
|
2953
|
+
document_end_event_flush
|
2954
|
+
events_push_flush_properties(StreamEnd.new(Location.point(@source, @scanner.pos)))
|
2955
|
+
true
|
2956
|
+
end
|
2957
|
+
end
|
2958
|
+
|
2959
|
+
# ------------------------------------------------------------------------
|
2960
|
+
# :section: Debugging
|
2961
|
+
# ------------------------------------------------------------------------
|
2962
|
+
|
2963
|
+
# If the DEBUG environment variable is set, we'll decorate all of the
|
2964
|
+
# parse methods and print them out as they are encountered.
|
2965
|
+
if !ENV.fetch("DEBUG", "").empty?
|
2966
|
+
class Debug < Module
|
2967
|
+
def initialize(methods)
|
2968
|
+
methods.each do |method|
|
2969
|
+
prefix = method.name.delete_prefix("parse_")
|
2970
|
+
|
2971
|
+
define_method(method) do |*args|
|
2972
|
+
norm = args.map { |arg| arg.nil? ? "nil" : arg }.join(",")
|
2973
|
+
$stderr.puts(">>> #{prefix}(#{norm})")
|
2974
|
+
super(*args)
|
2975
|
+
end
|
2976
|
+
end
|
2977
|
+
end
|
2978
|
+
end
|
2979
|
+
|
2980
|
+
prepend Debug.new(private_instance_methods.grep(/\Aparse_/))
|
2981
|
+
end
|
2982
|
+
end
|
2983
|
+
|
2984
|
+
# The emitter is responsible for taking Ruby objects and converting them
|
2985
|
+
# into YAML documents.
|
2986
|
+
class Emitter
|
2987
|
+
# The base class for all emitter nodes. We need to build a tree of nodes
|
2988
|
+
# here in order to support dumping repeated objects as anchors and
|
2989
|
+
# aliases, since we may find that we need to add an anchor after the
|
2990
|
+
# object has already been flushed.
|
2991
|
+
class Node
|
2992
|
+
attr_reader :value, :leading_comments, :trailing_comments
|
2993
|
+
|
2994
|
+
def initialize(value, leading_comments, trailing_comments)
|
2995
|
+
@value = value
|
2996
|
+
@leading_comments = leading_comments
|
2997
|
+
@trailing_comments = trailing_comments
|
2998
|
+
@anchor = nil
|
2999
|
+
end
|
3000
|
+
|
3001
|
+
def accept(visitor)
|
3002
|
+
raise
|
3003
|
+
end
|
3004
|
+
end
|
3005
|
+
|
3006
|
+
# Represents an alias to another node in the tree.
|
3007
|
+
class AliasNode < Node
|
3008
|
+
def accept(visitor)
|
3009
|
+
visitor.visit_alias(self)
|
3010
|
+
end
|
3011
|
+
end
|
3012
|
+
|
3013
|
+
# Represents an array of nodes.
|
3014
|
+
class ArrayNode < Node
|
3015
|
+
attr_accessor :anchor
|
3016
|
+
|
3017
|
+
def accept(visitor)
|
3018
|
+
visitor.visit_array(self)
|
3019
|
+
end
|
3020
|
+
end
|
3021
|
+
|
3022
|
+
# Represents a hash of nodes.
|
3023
|
+
class HashNode < Node
|
3024
|
+
attr_accessor :anchor
|
3025
|
+
|
3026
|
+
def accept(visitor)
|
3027
|
+
visitor.visit_hash(self)
|
3028
|
+
end
|
3029
|
+
end
|
3030
|
+
|
3031
|
+
# Represents the nil value.
|
3032
|
+
class NilNode < Node
|
3033
|
+
end
|
3034
|
+
|
3035
|
+
# Represents a generic object that is not matched by any of the other node
|
3036
|
+
# types.
|
3037
|
+
class ObjectNode < Node
|
3038
|
+
def accept(visitor)
|
3039
|
+
visitor.visit_object(self)
|
3040
|
+
end
|
3041
|
+
end
|
3042
|
+
|
3043
|
+
# Represents a Psych::Omap object.
|
3044
|
+
class OmapNode < Node
|
3045
|
+
attr_accessor :anchor
|
3046
|
+
|
3047
|
+
def accept(visitor)
|
3048
|
+
visitor.visit_omap(self)
|
3049
|
+
end
|
3050
|
+
end
|
3051
|
+
|
3052
|
+
# Represents a Psych::Set object.
|
3053
|
+
class SetNode < Node
|
3054
|
+
attr_accessor :anchor
|
3055
|
+
|
3056
|
+
def accept(visitor)
|
3057
|
+
visitor.visit_set(self)
|
3058
|
+
end
|
3059
|
+
end
|
3060
|
+
|
3061
|
+
# Represents a string object.
|
3062
|
+
class StringNode < Node
|
3063
|
+
def accept(visitor)
|
3064
|
+
visitor.visit_string(self)
|
3065
|
+
end
|
3066
|
+
end
|
3067
|
+
|
3068
|
+
# The visitor is responsible for walking the tree and generating the YAML
|
3069
|
+
# output.
|
3070
|
+
class Visitor
|
3071
|
+
def initialize(q)
|
3072
|
+
@q = q
|
3073
|
+
end
|
3074
|
+
|
3075
|
+
# Visit an AliasNode.
|
3076
|
+
def visit_alias(node)
|
3077
|
+
with_comments(node) { |value| @q.text("*#{value}") }
|
3078
|
+
end
|
3079
|
+
|
3080
|
+
# Visit an ArrayNode.
|
3081
|
+
def visit_array(node)
|
3082
|
+
with_comments(node) do |value|
|
3083
|
+
if (anchor = node.anchor)
|
3084
|
+
@q.text("&#{anchor} ")
|
3085
|
+
end
|
3086
|
+
|
3087
|
+
if value.empty?
|
3088
|
+
@q.text("[]")
|
3089
|
+
else
|
3090
|
+
visit_array_contents(value)
|
3091
|
+
end
|
3092
|
+
end
|
3093
|
+
end
|
3094
|
+
|
3095
|
+
# Visit a HashNode.
|
3096
|
+
def visit_hash(node)
|
3097
|
+
with_comments(node) do |value|
|
3098
|
+
if (anchor = node.anchor)
|
3099
|
+
@q.text("&#{anchor}")
|
3100
|
+
end
|
3101
|
+
|
3102
|
+
if value.empty?
|
3103
|
+
@q.text(" ") if anchor
|
3104
|
+
@q.text("{}")
|
3105
|
+
else
|
3106
|
+
@q.breakable if anchor
|
3107
|
+
visit_hash_contents(value)
|
3108
|
+
end
|
3109
|
+
end
|
3110
|
+
end
|
3111
|
+
|
3112
|
+
# Visit an ObjectNode.
|
3113
|
+
def visit_object(node)
|
3114
|
+
with_comments(node) do |value|
|
3115
|
+
@q.text(Psych.dump(value, indentation: @q.indent)[/\A--- (.+)\n\z/m, 1]) # TODO
|
3116
|
+
end
|
3117
|
+
end
|
3118
|
+
|
3119
|
+
# Visit an OmapNode.
|
3120
|
+
def visit_omap(node)
|
3121
|
+
with_comments(node) do |value|
|
3122
|
+
if (anchor = node.anchor)
|
3123
|
+
@q.text("&#{anchor} ")
|
3124
|
+
end
|
3125
|
+
|
3126
|
+
@q.text("!!omap")
|
3127
|
+
@q.breakable
|
3128
|
+
|
3129
|
+
visit_array_contents(value)
|
3130
|
+
end
|
3131
|
+
end
|
3132
|
+
|
3133
|
+
# Visit a SetNode.
|
3134
|
+
def visit_set(node)
|
3135
|
+
with_comments(node) do |value|
|
3136
|
+
if (anchor = node.anchor)
|
3137
|
+
@q.text("&#{anchor} ")
|
3138
|
+
end
|
3139
|
+
|
3140
|
+
@q.text("!set")
|
3141
|
+
@q.breakable
|
3142
|
+
|
3143
|
+
visit_hash_contents(node.value)
|
3144
|
+
end
|
3145
|
+
end
|
3146
|
+
|
3147
|
+
# Visit a StringNode.
|
3148
|
+
alias visit_string visit_object
|
3149
|
+
|
3150
|
+
private
|
3151
|
+
|
3152
|
+
# Shortcut to visit a node by passing this visitor to the accept method.
|
3153
|
+
def visit(node)
|
3154
|
+
node.accept(self)
|
3155
|
+
end
|
3156
|
+
|
3157
|
+
# Visit the elements within an array.
|
3158
|
+
def visit_array_contents(contents)
|
3159
|
+
@q.seplist(contents, -> { @q.breakable }) do |element|
|
3160
|
+
@q.text("-")
|
3161
|
+
next if element.is_a?(NilNode)
|
3162
|
+
|
3163
|
+
@q.text(" ")
|
3164
|
+
@q.nest(2) { visit(element) }
|
3165
|
+
end
|
3166
|
+
end
|
3167
|
+
|
3168
|
+
# Visit the key/value pairs within a hash.
|
3169
|
+
def visit_hash_contents(contents)
|
3170
|
+
@q.seplist(contents, -> { @q.breakable }) do |key, value|
|
3171
|
+
inlined = false
|
3172
|
+
|
3173
|
+
case key
|
3174
|
+
when NilNode
|
3175
|
+
@q.text("! ''")
|
3176
|
+
when ArrayNode, HashNode, OmapNode, SetNode
|
3177
|
+
if key.anchor.nil?
|
3178
|
+
@q.text("? ")
|
3179
|
+
@q.nest(2) { visit(key) }
|
3180
|
+
@q.breakable
|
3181
|
+
inlined = true
|
3182
|
+
else
|
3183
|
+
visit(key)
|
3184
|
+
end
|
3185
|
+
when AliasNode, ObjectNode
|
3186
|
+
visit(key)
|
3187
|
+
when StringNode
|
3188
|
+
if key.value.include?("\n")
|
3189
|
+
@q.text("? ")
|
3190
|
+
visit(key)
|
3191
|
+
@q.breakable
|
3192
|
+
inlined = true
|
3193
|
+
else
|
3194
|
+
visit(key)
|
3195
|
+
end
|
3196
|
+
end
|
3197
|
+
|
3198
|
+
@q.text(":")
|
3199
|
+
|
3200
|
+
case value
|
3201
|
+
when NilNode
|
3202
|
+
# skip
|
3203
|
+
when OmapNode, SetNode
|
3204
|
+
@q.text(" ")
|
3205
|
+
@q.nest(2) { visit(value) }
|
3206
|
+
when ArrayNode
|
3207
|
+
if value.value.empty?
|
3208
|
+
@q.text(" []")
|
3209
|
+
elsif inlined || value.anchor
|
3210
|
+
@q.text(" ")
|
3211
|
+
@q.nest(2) { visit(value) }
|
3212
|
+
else
|
3213
|
+
@q.breakable
|
3214
|
+
visit(value)
|
3215
|
+
end
|
3216
|
+
when HashNode
|
3217
|
+
if value.value.empty?
|
3218
|
+
@q.text(" {}")
|
3219
|
+
elsif inlined || value.anchor
|
3220
|
+
@q.text(" ")
|
3221
|
+
@q.nest(2) { visit(value) }
|
3222
|
+
else
|
3223
|
+
@q.nest(2) do
|
3224
|
+
@q.breakable
|
3225
|
+
visit(value)
|
3226
|
+
end
|
3227
|
+
end
|
3228
|
+
when AliasNode, ObjectNode, StringNode
|
3229
|
+
@q.text(" ")
|
3230
|
+
@q.nest(2) { visit(value) }
|
3231
|
+
end
|
3232
|
+
end
|
3233
|
+
end
|
3234
|
+
|
3235
|
+
# Print out the leading and trailing comments of a node, as well as
|
3236
|
+
# yielding the value of the node to the block.
|
3237
|
+
def with_comments(node)
|
3238
|
+
node.leading_comments.each do |comment|
|
3239
|
+
@q.text(comment.value)
|
3240
|
+
@q.breakable
|
3241
|
+
end
|
3242
|
+
|
3243
|
+
yield node.value
|
3244
|
+
|
3245
|
+
if (trailing_comments = node.trailing_comments).any?
|
3246
|
+
if trailing_comments[0].inline?
|
3247
|
+
inline_comment = trailing_comments.shift
|
3248
|
+
@q.trailer { @q.text(" "); @q.text(inline_comment.value) }
|
3249
|
+
end
|
3250
|
+
|
3251
|
+
trailing_comments.each do |comment|
|
3252
|
+
@q.breakable
|
3253
|
+
@q.text(comment.value)
|
3254
|
+
end
|
3255
|
+
end
|
3256
|
+
end
|
3257
|
+
end
|
3258
|
+
|
3259
|
+
# This is a specialized pretty printer that knows how to format trailing
|
3260
|
+
# comment.
|
3261
|
+
class Formatter < PP
|
3262
|
+
def breakable(sep = " ", width = sep.length)
|
3263
|
+
(current_trailers = trailers).each(&:call)
|
3264
|
+
current_trailers.clear
|
3265
|
+
super(sep, width)
|
3266
|
+
end
|
3267
|
+
|
3268
|
+
# These are blocks in the doc tree that should be flushed whenever we
|
3269
|
+
# are about to flush a breakable.
|
3270
|
+
def trailers
|
3271
|
+
@trailers ||= []
|
3272
|
+
end
|
3273
|
+
|
3274
|
+
# Register a block to be called when the next breakable is flushed.
|
3275
|
+
def trailer(&block)
|
3276
|
+
trailers << block
|
3277
|
+
end
|
3278
|
+
end
|
3279
|
+
|
3280
|
+
# Initialize a new emitter with the given io and options.
|
3281
|
+
def initialize(io, options)
|
3282
|
+
@io = io || $stdout
|
3283
|
+
@options = options
|
3284
|
+
@started = false
|
3285
|
+
|
3286
|
+
# These three instance variables are used to support dumping repeated
|
3287
|
+
# objects. When the same object is found more than once, we switch to
|
3288
|
+
# using an anchor and an alias.
|
3289
|
+
@object_nodes = {}.compare_by_identity
|
3290
|
+
@object_anchors = {}.compare_by_identity
|
3291
|
+
@object_anchor = 0
|
3292
|
+
end
|
3293
|
+
|
3294
|
+
# This is the main entrypoint into this object. It is responsible for
|
3295
|
+
# pushing a new object onto the emitter, which is then represented as a
|
3296
|
+
# YAML document.
|
3297
|
+
def <<(object)
|
3298
|
+
if @started
|
3299
|
+
@io << "...\n---"
|
3300
|
+
else
|
3301
|
+
@io << "---"
|
3302
|
+
@started = true
|
3303
|
+
end
|
3304
|
+
|
3305
|
+
if (node = dump(object)).is_a?(NilNode)
|
3306
|
+
@io << "\n"
|
3307
|
+
else
|
3308
|
+
q = Formatter.new(+"", 79, "\n") { |n| " " * n }
|
3309
|
+
|
3310
|
+
if (node.is_a?(ArrayNode) || node.is_a?(HashNode)) && !node.value.empty?
|
3311
|
+
q.breakable
|
3312
|
+
else
|
3313
|
+
q.text(" ")
|
3314
|
+
end
|
3315
|
+
|
3316
|
+
node.accept(Visitor.new(q))
|
3317
|
+
q.breakable
|
3318
|
+
q.current_group.break
|
3319
|
+
q.flush
|
3320
|
+
|
3321
|
+
@io << q.output
|
3322
|
+
end
|
3323
|
+
end
|
3324
|
+
|
3325
|
+
private
|
3326
|
+
|
3327
|
+
# Walk through the given object and convert it into a tree of nodes.
|
3328
|
+
def dump(base_object)
|
3329
|
+
object = base_object
|
3330
|
+
leading_comments = []
|
3331
|
+
trailing_comments = []
|
3332
|
+
|
3333
|
+
if base_object.is_a?(YAMLCommentsDelegator)
|
3334
|
+
object = base_object.__getobj__
|
3335
|
+
leading_comments.concat(base_object.yaml_leading_comments)
|
3336
|
+
trailing_comments.concat(base_object.yaml_trailing_comments)
|
3337
|
+
end
|
3338
|
+
|
3339
|
+
if object.nil?
|
3340
|
+
NilNode.new(object, leading_comments, trailing_comments)
|
3341
|
+
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
|
+
)
|
3347
|
+
else
|
3348
|
+
case object
|
3349
|
+
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
|
+
)
|
3356
|
+
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
|
+
)
|
3363
|
+
when Array
|
3364
|
+
@object_nodes[object] =
|
3365
|
+
ArrayNode.new(
|
3366
|
+
object.map { |element| dump(element) },
|
3367
|
+
leading_comments,
|
3368
|
+
trailing_comments
|
3369
|
+
)
|
3370
|
+
when Hash
|
3371
|
+
@object_nodes[object] =
|
3372
|
+
HashNode.new(
|
3373
|
+
object.to_h { |key, value| [dump(key), dump(value)] },
|
3374
|
+
leading_comments,
|
3375
|
+
trailing_comments
|
3376
|
+
)
|
3377
|
+
when String
|
3378
|
+
StringNode.new(object, leading_comments, trailing_comments)
|
3379
|
+
else
|
3380
|
+
ObjectNode.new(object, leading_comments, trailing_comments)
|
3381
|
+
end
|
3382
|
+
end
|
3383
|
+
end
|
3384
|
+
end
|
3385
|
+
|
3386
|
+
# A safe emitter is a subclass of the emitter that restricts the types of
|
3387
|
+
# objects that can be serialized.
|
3388
|
+
class SafeEmitter < Emitter
|
3389
|
+
DEFAULT_PERMITTED_CLASSES = {
|
3390
|
+
TrueClass => true,
|
3391
|
+
FalseClass => true,
|
3392
|
+
NilClass => true,
|
3393
|
+
Integer => true,
|
3394
|
+
Float => true,
|
3395
|
+
String => true,
|
3396
|
+
Array => true,
|
3397
|
+
Hash => true,
|
3398
|
+
}.compare_by_identity.freeze
|
3399
|
+
|
3400
|
+
# Initialize a new safe emitter with the given io and options.
|
3401
|
+
def initialize(io, options)
|
3402
|
+
super(io, options)
|
3403
|
+
|
3404
|
+
@permitted_classes = DEFAULT_PERMITTED_CLASSES.dup
|
3405
|
+
Array(options[:permitted_classes]).each do |klass|
|
3406
|
+
@permitted_classes[klass] = true
|
3407
|
+
end
|
3408
|
+
|
3409
|
+
@permitted_symbols = {}.compare_by_identity
|
3410
|
+
Array(options[:permitted_symbols]).each do |symbol|
|
3411
|
+
@permitted_symbols[symbol] = true
|
3412
|
+
end
|
3413
|
+
|
3414
|
+
@aliases = options.fetch(:aliases, false)
|
3415
|
+
end
|
3416
|
+
|
3417
|
+
private
|
3418
|
+
|
3419
|
+
# Dump the given object, ensuring that it is a permitted object.
|
3420
|
+
def dump(object)
|
3421
|
+
if !@aliases && @object_nodes.key?(object)
|
3422
|
+
raise BadAlias, "Tried to dump an aliased object"
|
3423
|
+
end
|
3424
|
+
|
3425
|
+
if Symbol === object
|
3426
|
+
if !@permitted_classes[Symbol] || !@permitted_symbols[object]
|
3427
|
+
raise DisallowedClass.new("dump", "Symbol(#{object.inspect})")
|
3428
|
+
end
|
3429
|
+
elsif !@permitted_classes[object.class]
|
3430
|
+
raise DisallowedClass.new("dump", object.class.name || object.class.inspect)
|
3431
|
+
end
|
3432
|
+
|
3433
|
+
super
|
3434
|
+
end
|
3435
|
+
end
|
3436
|
+
|
3437
|
+
# --------------------------------------------------------------------------
|
3438
|
+
# :section: Public API specific to Psych::Pure
|
3439
|
+
# --------------------------------------------------------------------------
|
3440
|
+
|
3441
|
+
# Create a new default parser.
|
3442
|
+
def self.parser
|
3443
|
+
Pure::Parser.new(TreeBuilder.new)
|
3444
|
+
end
|
3445
|
+
|
3446
|
+
# Parse a YAML stream and return the root node.
|
3447
|
+
def self.parse_stream(yaml, filename: nil, comments: false, &block)
|
3448
|
+
if block_given?
|
3449
|
+
parser = Pure::Parser.new(Handlers::DocumentStream.new(&block))
|
3450
|
+
parser.parse(yaml, filename, comments: comments)
|
3451
|
+
else
|
3452
|
+
parser = self.parser
|
3453
|
+
parser.parse(yaml, filename, comments: comments)
|
3454
|
+
parser.handler.root
|
3455
|
+
end
|
3456
|
+
end
|
3457
|
+
|
3458
|
+
# Dump an object to a YAML string.
|
3459
|
+
def self.dump(o, io = nil, options = {})
|
3460
|
+
if Hash === io
|
3461
|
+
options = io
|
3462
|
+
io = nil
|
3463
|
+
end
|
3464
|
+
|
3465
|
+
real_io = io || StringIO.new
|
3466
|
+
emitter = Emitter.new(real_io, options)
|
3467
|
+
emitter << o
|
3468
|
+
io || real_io.string
|
3469
|
+
end
|
3470
|
+
|
3471
|
+
# Dump an object to a YAML string, with restricted classes, symbols, and
|
3472
|
+
# aliases.
|
3473
|
+
def self.safe_dump(o, io = nil, options = {})
|
3474
|
+
if Hash === io
|
3475
|
+
options = io
|
3476
|
+
io = nil
|
3477
|
+
end
|
3478
|
+
|
3479
|
+
real_io = io || StringIO.new
|
3480
|
+
emitter = SafeEmitter.new(real_io, options)
|
3481
|
+
emitter << o
|
3482
|
+
io || real_io.string
|
3483
|
+
end
|
3484
|
+
|
3485
|
+
# Dump a stream of objects to a YAML string.
|
3486
|
+
def self.dump_stream(*objects)
|
3487
|
+
real_io = io || StringIO.new
|
3488
|
+
emitter = Emitter.new(real_io, {})
|
3489
|
+
objects.each { |object| emitter << object }
|
3490
|
+
io || real_io.string
|
3491
|
+
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
|
+
end
|
3577
|
+
end
|