RbYAML 0.0.1

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.
Files changed (66) hide show
  1. data/LICENSE +19 -0
  2. data/README +31 -0
  3. data/lib/rbyaml.rb +378 -0
  4. data/lib/rbyaml/composer.rb +189 -0
  5. data/lib/rbyaml/constructor.rb +374 -0
  6. data/lib/rbyaml/detector.rb +44 -0
  7. data/lib/rbyaml/dumper.rb +40 -0
  8. data/lib/rbyaml/emitter.rb +1116 -0
  9. data/lib/rbyaml/error.rb +81 -0
  10. data/lib/rbyaml/events.rb +92 -0
  11. data/lib/rbyaml/loader.rb +49 -0
  12. data/lib/rbyaml/nodes.rb +69 -0
  13. data/lib/rbyaml/parser.rb +488 -0
  14. data/lib/rbyaml/reader.rb +127 -0
  15. data/lib/rbyaml/representer.rb +183 -0
  16. data/lib/rbyaml/scanner.rb +1258 -0
  17. data/lib/rbyaml/serializer.rb +120 -0
  18. data/lib/rbyaml/test.rb +56 -0
  19. data/lib/rbyaml/tokens.rb +163 -0
  20. data/lib/rbyaml/yaml.rb +143 -0
  21. data/test/test_rbyaml.rb +18 -0
  22. data/test/yaml/gems.yml +130951 -0
  23. data/test/yaml/gems2.yml +113 -0
  24. data/test/yaml/test1.yml +3 -0
  25. data/test/yaml/test10.yml +8 -0
  26. data/test/yaml/test12.yml +8 -0
  27. data/test/yaml/test13.yml +4 -0
  28. data/test/yaml/test14.yml +4 -0
  29. data/test/yaml/test15.yml +8 -0
  30. data/test/yaml/test16.yml +7 -0
  31. data/test/yaml/test18.yml +6 -0
  32. data/test/yaml/test19.yml +5 -0
  33. data/test/yaml/test2.yml +3 -0
  34. data/test/yaml/test20.yml +6 -0
  35. data/test/yaml/test21.yml +4 -0
  36. data/test/yaml/test22.yml +4 -0
  37. data/test/yaml/test23.yml +13 -0
  38. data/test/yaml/test24.yml +14 -0
  39. data/test/yaml/test25.yml +7 -0
  40. data/test/yaml/test26.yml +7 -0
  41. data/test/yaml/test27.yml +29 -0
  42. data/test/yaml/test28.yml +26 -0
  43. data/test/yaml/test29.yml +13 -0
  44. data/test/yaml/test3.yml +8 -0
  45. data/test/yaml/test30.yml +7 -0
  46. data/test/yaml/test31.yml +2 -0
  47. data/test/yaml/test32.yml +13 -0
  48. data/test/yaml/test33.yml +2 -0
  49. data/test/yaml/test34.yml +8 -0
  50. data/test/yaml/test35.yml +4 -0
  51. data/test/yaml/test36.yml +8 -0
  52. data/test/yaml/test37.yml +2 -0
  53. data/test/yaml/test38.yml +8 -0
  54. data/test/yaml/test39.yml +2 -0
  55. data/test/yaml/test4.yml +8 -0
  56. data/test/yaml/test40.yml +3 -0
  57. data/test/yaml/test41.yml +5 -0
  58. data/test/yaml/test42.yml +12 -0
  59. data/test/yaml/test43.yml +15 -0
  60. data/test/yaml/test44.yml +23 -0
  61. data/test/yaml/test5.yml +3 -0
  62. data/test/yaml/test6.yml +5 -0
  63. data/test/yaml/test7.yml +10 -0
  64. data/test/yaml/test8.yml +10 -0
  65. data/test/yaml/test9.yml +8 -0
  66. metadata +111 -0
@@ -0,0 +1,1116 @@
1
+ # Emitter expects events obeying the following grammar:
2
+ # stream ::= STREAM-START document* STREAM-END
3
+ # document ::= DOCUMENT-START node DOCUMENT-END
4
+ # node ::= SCALAR | sequence | mapping
5
+ # sequence ::= SEQUENCE-START node* SEQUENCE-END
6
+ # mapping ::= MAPPING-START (node node)* MAPPING-END
7
+
8
+ require 'rbyaml/error'
9
+ require 'rbyaml/events'
10
+
11
+ module RbYAML
12
+ class EmitterError < YAMLError
13
+ end
14
+
15
+ class ScalarAnalysis
16
+ attr_accessor :scalar,:empty,:multiline,:allow_flow_plain,:allow_block_plain,:allow_single_quoted,:allow_double_quoted,:allow_block
17
+ def initialize(scalar,empty,multiline,allow_flow_plain, allow_block_plain,allow_single_quoted, allow_double_quoted,allow_block)
18
+ @scalar = scalar
19
+ @empty = empty
20
+ @multiline = multiline
21
+ @allow_flow_plain = allow_flow_plain
22
+ @allow_block_plain = allow_block_plain
23
+ @allow_single_quoted = allow_single_quoted
24
+ @allow_double_quoted = allow_double_quoted
25
+ @allow_block = allow_block
26
+ end
27
+ end
28
+
29
+ module Emitter
30
+ DEFAULT_TAG_PREFIXES = {
31
+ "!" => "!",
32
+ "tag:yaml.org,2002:" => "!!"
33
+ }
34
+
35
+ def initialize_emitter(stream, canonical=nil, indent=nil, width=nil,line_break=nil)
36
+ # The stream should have the methods `write` and possibly `flush`.
37
+ @stream = stream
38
+
39
+ # Emitter is a state machine with a stack of states to handle nested
40
+ # structures.
41
+ @states = []
42
+ @state = :expect_stream_start
43
+
44
+ # Current event and the event queue.
45
+ @events = []
46
+ @event = nil
47
+
48
+ # The current indentation level and the stack of previous indents.
49
+ @indents = []
50
+ @indent = nil
51
+
52
+ # Flow level.
53
+ @flow_level = 0
54
+
55
+ # Contexts.
56
+ @root_context = false
57
+ @sequence_context = false
58
+ @mapping_context = false
59
+ @simple_key_context = false
60
+
61
+ # Characteristics of the last emitted character:
62
+ # - current position.
63
+ # - is it a whitespace?
64
+ # - is it an indention character
65
+ # (indentation space, '-', '?', or ':')?
66
+ @line = 0
67
+ @column = 0
68
+ @whitespace = true
69
+ @indention = true
70
+
71
+ # Formatting details.
72
+ @canonical = canonical
73
+ @best_indent = 2
74
+ @best_indent = indent if indent && indent!=0 && 1 < indent && indent < 10
75
+
76
+ @best_width = 80
77
+ @best_width = width if width && width != 0 && width > @best_indent*2
78
+
79
+ @best_line_break = "\n"
80
+ @best_line_break = line_break if ["\r", "\n", "\r\n"].include?(line_break)
81
+
82
+ # Tag prefixes.
83
+ @tag_prefixes = nil
84
+
85
+ # Prepared anchor and tag.
86
+ @prepared_anchor = nil
87
+ @prepared_tag = nil
88
+
89
+ # Scalar analysis and style.
90
+ @analysis = nil
91
+ @style = nil
92
+ end
93
+
94
+ def emit(event)
95
+ @events << event
96
+ while !need_more_events
97
+ @event = @events.shift
98
+ send(@state)
99
+ @event = nil
100
+ end
101
+ end
102
+
103
+ # In some cases, we wait for a few next events before emitting.
104
+
105
+ def need_more_events
106
+ return true if @events.empty?
107
+ event = @events.first
108
+ if DocumentStartEvent === event
109
+ need_events(1)
110
+ elsif SequenceStartEvent === event
111
+ need_events(2)
112
+ elsif MappingStartEvent === event
113
+ need_events(3)
114
+ else
115
+ false
116
+ end
117
+ end
118
+
119
+ def need_events(count)
120
+ level = 0
121
+ for event in @events[1..-1]
122
+ if DocumentStartEvent === event || CollectionStartEvent === event
123
+ level += 1
124
+ elsif DocumentEndEvent === event || CollectionEndEvent === event
125
+ level -= 1
126
+ elsif StreamEndEvent === event
127
+ level = -1
128
+ end
129
+ if level < 0
130
+ return false
131
+ end
132
+ end
133
+ @events.length < count+1
134
+ end
135
+
136
+ def increase_indent(flow=false, indentless=false)
137
+ @indents << @indent
138
+ if @indent.nil?
139
+ if flow
140
+ @indent = @best_indent
141
+ else
142
+ @indent = 0
143
+ end
144
+ elsif !indentless
145
+ @indent += @best_indent
146
+ end
147
+ end
148
+
149
+ # States.
150
+
151
+ # Stream handlers.
152
+
153
+ def expect_stream_start
154
+ if StreamStartEvent === @event
155
+ write_stream_start
156
+ @state = :expect_first_document_start
157
+ else
158
+ raise EmitterError.new("expected StreamStartEvent, but got #{@event}")
159
+ end
160
+ end
161
+
162
+ def expect_nothing
163
+ raise EmitterError.new("expected nothing, but got #{@event}")
164
+ end
165
+
166
+ # Document handlers.
167
+
168
+ def expect_first_document_start
169
+ expect_document_start(true)
170
+ end
171
+
172
+ def expect_document_start(first=false)
173
+ if DocumentStartEvent === @event
174
+ if @event.version
175
+ version_text = prepare_version(@event.version)
176
+ write_version_directive(version_text)
177
+ end
178
+ @tag_prefixes = Emitter::DEFAULT_TAG_PREFIXES.dup
179
+ if @event.tags
180
+ handles = @event.tags.keys
181
+ handles.sort!
182
+ for handle in handles
183
+ prefix = @event.tags[handle]
184
+ @tag_prefixes[prefix] = handle
185
+ handle_text = prepare_tag_handle(handle)
186
+ prefix_text = prepare_tag_prefix(prefix)
187
+ write_tag_directive(handle_text, prefix_text)
188
+ end
189
+ end
190
+ implicit = first && !@event.explicit && !@canonical && !@event.version && !@event.tags && !check_empty_document
191
+ if !implicit
192
+ write_indent
193
+ write_indicator("---",true)
194
+ if @canonical
195
+ write_indent
196
+ end
197
+ end
198
+ @state = :expect_document_root
199
+ elsif StreamEndEvent === @event
200
+ write_stream_end
201
+ @state = :expect_nothing
202
+ else
203
+ raise EmitterError.new("expected DocumentStartEvent, but got #{@event}")
204
+ end
205
+ end
206
+
207
+ def expect_document_end
208
+ if DocumentEndEvent === @event
209
+ write_indent
210
+ if @event.explicit
211
+ write_indicator("...", true)
212
+ write_indent
213
+ end
214
+ flush_stream
215
+ @state = :expect_document_start
216
+ else
217
+ raise EmitterError.new("expected DocumentEndEvent, but got #{@event}")
218
+ end
219
+ end
220
+
221
+ def expect_document_root
222
+ @states << :expect_document_end
223
+ expect_node(true)
224
+ end
225
+
226
+ # Node handlers.
227
+
228
+ def expect_node(root=false, sequence=false, mapping=false, simple_key=false)
229
+ @root_context = root
230
+ @sequence_context = sequence
231
+ @mapping_context = mapping
232
+ @simple_key_context = simple_key
233
+ if AliasEvent === @event
234
+ expect_alias
235
+ elsif ScalarEvent === @event || CollectionStartEvent === @event
236
+ process_anchor("&")
237
+ process_tag
238
+ if ScalarEvent === @event
239
+ expect_scalar
240
+ elsif SequenceStartEvent === @event
241
+ if @flow_level!=0 || @canonical || @event.flow_style || check_empty_sequence
242
+ expect_flow_sequence
243
+ else
244
+ expect_block_sequence
245
+ end
246
+ elsif MappingStartEvent === @event
247
+ if @flow_level!=0 || @canonical || @event.flow_style || check_empty_mapping
248
+ expect_flow_mapping
249
+ else
250
+ expect_block_mapping
251
+ end
252
+ end
253
+ else
254
+ raise EmitterError.new("expected NodeEvent, but got #{@event}")
255
+ end
256
+ end
257
+
258
+ def expect_alias
259
+ raise EmitterError.new("anchor is not specified for alias") if @event.anchor.nil?
260
+ process_anchor("*")
261
+ @state = @states.pop
262
+ end
263
+
264
+ def expect_scalar
265
+ increase_indent(true)
266
+ process_scalar
267
+ @indent = @indents.pop
268
+ @state = @states.pop
269
+ end
270
+
271
+ # Flow sequence handlers.
272
+
273
+ def expect_flow_sequence
274
+ write_indicator("[", true, true)
275
+ @flow_level += 1
276
+ increase_indent(true)
277
+ @state = :expect_first_flow_sequence_item
278
+ end
279
+
280
+ def expect_first_flow_sequence_item
281
+ if SequenceEndEvent === @event
282
+ @indent = @indents.pop
283
+ @flow_level -= 1
284
+ write_indicator("]", false)
285
+ @state = @states.pop
286
+ else
287
+ write_indent if @canonical || @column > @best_width
288
+ @states << :expect_flow_sequence_item
289
+ expect_node(false,true)
290
+ end
291
+ end
292
+
293
+ def expect_flow_sequence_item
294
+ if SequenceEndEvent === @event
295
+ @indent = @indents.pop
296
+ @flow_level -= 1
297
+ if @canonical
298
+ write_indicator(",",false)
299
+ write_indent
300
+ end
301
+ write_indicator("]",false)
302
+ @state = @states.pop
303
+ else
304
+ write_indicator(",", false)
305
+ write_indent if @canonical or @column > @best_width
306
+ @states << :expect_flow_sequence_item
307
+ expect_node(false,true)
308
+ end
309
+ end
310
+
311
+ # Flow mapping handlers.
312
+
313
+ def expect_flow_mapping
314
+ write_indicator("{", true, true)
315
+ @flow_level += 1
316
+ increase_indent(true)
317
+ @state = :expect_first_flow_mapping_key
318
+ end
319
+
320
+ def expect_first_flow_mapping_key
321
+ if MappingEndEvent === @event
322
+ @indent = @indents.pop
323
+ @flow_level -= 1
324
+ write_indicator("}", false)
325
+ @state = @states.pop
326
+ else
327
+ write_indent if @canonical || @column > @best_width
328
+ if !@canonical && check_simple_key
329
+ @states << :expect_flow_mapping_simple_value
330
+ expect_node(false,false,true,true)
331
+ else
332
+ write_indicator("?", true)
333
+ @states << :expect_flow_mapping_value
334
+ expect_node(false,false,true)
335
+ end
336
+ end
337
+ end
338
+
339
+ def expect_flow_mapping_key
340
+ if MappingEndEvent === @event
341
+ @indent = @indents.pop
342
+ @flow_level -= 1
343
+ if @canonical
344
+ write_indicator(",", false)
345
+ write_indent
346
+ end
347
+ write_indicator("}", false)
348
+ @state = @states.pop
349
+ else
350
+ write_indicator(",", false)
351
+ write_indent if @canonical || @column > @best_width
352
+ if !@canonical && check_simple_key
353
+ @states << :expect_flow_mapping_simple_value
354
+ expect_node(false,false,true,true)
355
+ else
356
+ write_indicator("?", true)
357
+ @states << :expect_flow_mapping_value
358
+ expect_node(false,false,true)
359
+ end
360
+ end
361
+ end
362
+
363
+ def expect_flow_mapping_simple_value
364
+ write_indicator(":", false)
365
+ @states << :expect_flow_mapping_key
366
+ expect_node(false,false,true)
367
+ end
368
+
369
+ def expect_flow_mapping_value
370
+ write_indent if @canonical || @column > @best_width
371
+ write_indicator(":", true)
372
+ @states << :expect_flow_mapping_key
373
+ expect_node(false,false,true)
374
+ end
375
+
376
+ # Block sequence handlers.
377
+
378
+ def expect_block_sequence
379
+ indentless = @mapping_context && !@indention
380
+ increase_indent(false,indentless)
381
+ @state = :expect_first_block_sequence_item
382
+ end
383
+
384
+ def expect_first_block_sequence_item
385
+ expect_block_sequence_item(true)
386
+ end
387
+
388
+ def expect_block_sequence_item(first=false)
389
+ if !first && SequenceEndEvent === @event
390
+ @indent = @indents.pop
391
+ @state = @states.pop
392
+ else
393
+ write_indent
394
+ write_indicator("-", true, false, true)
395
+ @states << :expect_block_sequence_item
396
+ expect_node(false,true)
397
+ end
398
+ end
399
+
400
+ # Block mapping handlers.
401
+
402
+ def expect_block_mapping
403
+ increase_indent(false)
404
+ @state = :expect_first_block_mapping_key
405
+ end
406
+
407
+ def expect_first_block_mapping_key
408
+ expect_block_mapping_key(true)
409
+ end
410
+
411
+ def expect_block_mapping_key(first=false)
412
+ if !first && MappingEndEvent === @event
413
+ @indent = @indents.pop
414
+ @state = @states.pop
415
+ else
416
+ write_indent
417
+ if check_simple_key
418
+ @states << :expect_block_mapping_simple_value
419
+ expect_node(false,false,true,true)
420
+ else
421
+ write_indicator("?", true, false, true)
422
+ @states << :expect_block_mapping_value
423
+ expect_node(false,false,true)
424
+ end
425
+ end
426
+ end
427
+
428
+ def expect_block_mapping_simple_value
429
+ write_indicator(":", false)
430
+ @states << :expect_block_mapping_key
431
+ expect_node(false,false,true)
432
+ end
433
+
434
+ def expect_block_mapping_value
435
+ write_indent
436
+ write_indicator(":",true,false,true)
437
+ @states << :expect_block_mapping_key
438
+ expect_node(false,false,true)
439
+ end
440
+
441
+ # Checkers.
442
+
443
+ def check_empty_sequence
444
+ SequenceStartEvent === @event && !@events.empty? && SequenceEndEvent === @events.first
445
+ end
446
+
447
+ def check_empty_mapping
448
+ MappingStartEvent === @event && !@events.empty? && MappingEndEvent === @events.first
449
+ end
450
+
451
+ def check_empty_document
452
+ return false if !DocumentStartEvent === @event || @events.empty?
453
+ event = @events.first
454
+ ScalarEvent === event && event.anchor.nil? && event.tag.nil? && event.implicit && event.value == ""
455
+ end
456
+
457
+ def check_simple_key
458
+ length = 0
459
+ if NodeEvent === @event && !@event.anchor.nil?
460
+ @prepared_anchor = prepare_anchor(@event.anchor) if @prepared_anchor.nil?
461
+ length += @prepared_anchor.length
462
+ end
463
+ if (ScalarEvent === @event || CollectionStartEvent === @event) && !@event.tag.nil?
464
+ @prepared_tag = prepare_tag(@event.tag) if @prepared_tag.nil?
465
+ length += @prepared_tag.length
466
+ end
467
+ if ScalarEvent === @event
468
+ @analysis = analyze_scalar(@event.value) if @analysis.nil?
469
+ length += @analysis.scalar.length
470
+ end
471
+
472
+ (length < 128 && (AliasEvent === @event ||
473
+ (ScalarEvent === @event && !@analysis.empty && !@analysis.multiline) ||
474
+ check_empty_sequence || check_empty_mapping))
475
+ end
476
+
477
+
478
+ # Anchor, Tag, and Scalar processors.
479
+
480
+ def process_anchor(indicator)
481
+ if @event.anchor.nil?
482
+ @prepared_anchor = nil
483
+ return nil
484
+ end
485
+ @prepared_anchor = prepare_anchor(@event.anchor) if @prepared_anchor.nil?
486
+ write_indicator(indicator+@prepared_anchor, true) if @prepared_anchor && !@prepared_anchor.empty?
487
+ @prepared_anchor = nil
488
+ end
489
+
490
+ def process_tag
491
+ tag = @event.tag
492
+ if ScalarEvent === @event
493
+ @style = choose_scalar_style if @style.nil?
494
+ if @style == ""
495
+ @prepared_tag = nil
496
+ return nil
497
+ end
498
+ if @event.implicit && !tag
499
+ tag = "!"
500
+ @prepared_tag = nil
501
+ end
502
+ end
503
+ if !tag
504
+ @prepared_tag = nil
505
+ return nil
506
+ end
507
+ @prepared_tag = prepare_tag(tag) if @prepared_tag.nil?
508
+ write_indicator(@prepared_tag, true) if @prepared_tag && !@prepared_tag.empty?
509
+ @prepared_tag = nil
510
+ end
511
+
512
+ def choose_scalar_style
513
+ @analysis = analyze_scalar(@event.value) if @analysis.nil?
514
+ return '"' if @event.style == '"' || @canonical
515
+ if !@event.style && @event.implicit
516
+ if !(@simple_key_context && (@analysis.empty || @analysis.multiline)) && ((@flow_level!=0 && @analysis.allow_flow_plain) || (@flow_level == 0 && @analysis.allow_block_plain))
517
+ return ""
518
+ end
519
+ end
520
+
521
+
522
+
523
+ if !@event.style && @event.implicit && (!(@simple_key_context && (@analysis.empty || @analysis.multiline)) &&
524
+ (@flow_level!=0 && @analysis.allow_flow_plain || (@flow_level==0 && @analysis.allow_block_plain)))
525
+ return ""
526
+ end
527
+ return @event.style if @event.style && /^[|>]$/ =~ @event.style && @flow_level==0 && @analysis.allow_block
528
+ return "'" if (!@event.style || @event.style == "'") && (@analysis.allow_single_quoted && !(@simple_key_context && @analysis.multiline))
529
+ return '"'
530
+ end
531
+
532
+ def process_scalar
533
+ @analysis = analyze_scalar(@event.value) if @analysis.nil?
534
+ @style = choose_scalar_style if @style.nil?
535
+ split = !@simple_key_context
536
+ if @style == '"'
537
+ write_double_quoted(@analysis.scalar, split)
538
+ elsif @style == "'"
539
+ write_single_quoted(@analysis.scalar, split)
540
+ elsif @style == ">"
541
+ write_folded(@analysis.scalar)
542
+ elsif @style == "|"
543
+ write_literal(@analysis.scalar)
544
+ else
545
+ write_plain(@analysis.scalar, split)
546
+ end
547
+ @analysis = nil
548
+ @style = nil
549
+ end
550
+
551
+ # Analyzers.
552
+
553
+ def prepare_version(version)
554
+ major, minor = version
555
+ raise EmitterError.new("unsupported YAML version: #{major}.#{minor}") if major != 1
556
+ "#{major}.#{minor}"
557
+ end
558
+
559
+ def prepare_tag_handle(handle)
560
+ raise EmitterError.new("tag handle must not be empty") if handle.nil? || handle.empty?
561
+ raise EmitterError("tag handle must start and end with '!': #{handle}") if handle[0] != ?! || handle[-1] != ?!
562
+ raise EmitterError.new("invalid character #{$&} in the tag handle: #{handle}") if /[^-\w]/ =~ handle[1...-1]
563
+ handle
564
+ end
565
+
566
+ def prepare_tag_prefix(prefix)
567
+ raise EmitterError.new("tag prefix must not be empty") if prefix.nil? || prefix.empty?
568
+ chunks = []
569
+ start = ending = 0
570
+ ending = 1 if prefix[0] == ?!
571
+ ending += 1 while ending < prefix.length
572
+ chunks << prefix[start...ending] if start < ending
573
+ chunks.join("")
574
+ end
575
+
576
+ def prepare_tag(tag)
577
+ raise EmitterError.new("tag must not be empty") if tag.nil? || tag.empty?
578
+ return tag if tag == "!"
579
+ handle = nil
580
+ suffix = tag
581
+ for prefix in @tag_prefixes.keys
582
+ if Regexp.new("^"+Regexp.escape(prefix)) =~ tag && (prefix == "!" || prefix.length < tag.length)
583
+ handle = @tag_prefixes[prefix]
584
+ suffix = tag[prefix.length..-1]
585
+ end
586
+ end
587
+ chunks = []
588
+ start = ending = 0
589
+ ending += 1 while ending < suffix.length
590
+ chunks << suffix[start...ending] if start < ending
591
+ suffix_text = chunks.join("")
592
+ if handle
593
+ "#{handle}#{suffix_text}"
594
+ else
595
+ "!<#{suffix_text}>"
596
+ end
597
+ end
598
+
599
+ def prepare_anchor(anchor)
600
+ raise EmitterError.new("anchor must not be empty") if anchor.nil? || anchor.empty?
601
+ raise EmitterError.new("invalid character #{$&} in the anchor: #{anchor}") if /[^-\w]/ =~ anchor
602
+ anchor
603
+ end
604
+
605
+ def analyze_scalar(scalar)
606
+ # Empty scalar is a special case.
607
+ return ScalarAnalysis.new(scalar,true,false,false,true,true,true,false) if scalar.nil? || scalar.empty?
608
+ # Indicators and special characters.
609
+ block_indicators = false
610
+ flow_indicators = false
611
+ line_breaks = false
612
+ special_characters = false
613
+
614
+ # Whitespaces.
615
+ inline_spaces = false # non-space space+ non-space
616
+ inline_breaks = false # non-space break+ non-space
617
+ leading_spaces = false # ^ space+ (non-space | $)
618
+ leading_breaks = false # ^ break+ (non-space | $)
619
+ trailing_spaces = false # (^ | non-space) space+ $
620
+ trailing_breaks = false # (^ | non-space) break+ $
621
+ inline_breaks_spaces = false # non-space break+ space+ non-space
622
+ mixed_breaks_spaces = false # anything else
623
+
624
+ # Check document indicators.
625
+ if /^(---|\.\.\.)/ =~ scalar
626
+ block_indicators = true
627
+ flow_indicators = true
628
+ end
629
+
630
+ # First character or preceded by a whitespace.
631
+ preceeded_by_space = true
632
+
633
+ # Last character or followed by a whitespace.
634
+ followed_by_space = scalar.length == 1 || "\0 \t\r\n\x85".include?(scalar[1])
635
+
636
+ # The current series of whitespaces contain plain spaces.
637
+ spaces = false
638
+
639
+ # The current series of whitespaces contain line breaks.
640
+ breaks = false
641
+
642
+ # The current series of whitespaces contain a space followed by a
643
+ # break.
644
+ mixed = false
645
+
646
+ # The current series of whitespaces start at the beginning of the
647
+ # scalar.
648
+ leading = false
649
+
650
+ index = 0
651
+ while index < scalar.length
652
+ ch = scalar[index]
653
+
654
+ # Check for indicators.
655
+
656
+ if index == 0
657
+ # Leading indicators are special characters.
658
+ if "#,[]{}#&*!|>'\"%@`".include?(ch)
659
+ flow_indicators = true
660
+ block_indicators = true
661
+ end
662
+ if "?:".include?(ch)
663
+ flow_indicators = true
664
+ if followed_by_space
665
+ block_indicators = true
666
+ end
667
+ end
668
+ if ch == ?- && followed_by_space
669
+ flow_indicators = true
670
+ block_indicators = true
671
+ end
672
+ else
673
+ # Some indicators cannot appear within a scalar as well.
674
+ flow_indicators = true if ",?[]{}".include?(ch)
675
+ if ch == ?:
676
+ flow_indicators = true
677
+ block_indicators = true if followed_by_space
678
+ end
679
+ if ch == ?# && preceeded_by_space
680
+ flow_indicators = true
681
+ block_indicators = true
682
+ end
683
+ end
684
+ # Check for line breaks, special, and unicode characters.
685
+ line_breaks = true if "\n\x85".include?(ch)
686
+ if !(ch == ?\n || (?\x20 <= ch && ch <= ?\x7E))
687
+ special_characters = true
688
+ end
689
+ # Spaces, line breaks, and how they are mixed. State machine.
690
+
691
+ # Start or continue series of whitespaces.
692
+ if " \n\x85".include?(ch)
693
+ if spaces && breaks
694
+ mixed = true if ch != 32 # break+ (space+ break+) => mixed
695
+ elsif spaces
696
+ if ch != 32 # (space+ break+) => mixed
697
+ breaks = true
698
+ mixed = true
699
+ end
700
+ elsif breaks
701
+ spaces = true if ch == 32 # break+ space+
702
+ else
703
+ leading = (index == 0)
704
+ if ch == 32 # space+
705
+ spaces = true
706
+ else # break+
707
+ breaks = true
708
+ end
709
+ end
710
+ # Series of whitespaces ended with a non-space.
711
+ elsif spaces || breaks
712
+ if leading
713
+ if spaces && breaks
714
+ mixed_breaks_spaces = true
715
+ elsif spaces
716
+ leading_spaces = true
717
+ elsif breaks
718
+ leading_breaks = true
719
+ end
720
+ else
721
+ if mixed
722
+ mixed_breaks_spaces = true
723
+ elsif spaces && breaks
724
+ inline_breaks_spaces = true
725
+ elsif spaces
726
+ inline_spaces = true
727
+ elsif breaks
728
+ inline_breaks = true
729
+ end
730
+ end
731
+ spaces = breaks = mixed = leading = false
732
+ end
733
+
734
+ # Series of whitespaces reach the end.
735
+ if (spaces || breaks) && (index == scalar.length-1)
736
+ if spaces && breaks
737
+ mixed_breaks_spaces = true
738
+ elsif spaces
739
+ trailing_spaces = true
740
+ leading_spaces = true if leading
741
+ elsif breaks
742
+ trailing_breaks = true
743
+ leading_breaks = true if leading
744
+ end
745
+ spaces = breaks = mixed = leading = false
746
+ end
747
+ # Prepare for the next character.
748
+ index += 1
749
+ preceeded_by_space = "\0 \t\r\n\x85".include?(ch)
750
+ followed_by_space = index+1 >= scalar.length || "\0 \t\r\n\x85".include?(scalar[index+1])
751
+ end
752
+ # Let's decide what styles are allowed.
753
+ allow_flow_plain = true
754
+ allow_block_plain = true
755
+ allow_single_quoted = true
756
+ allow_double_quoted = true
757
+ allow_block = true
758
+ # Leading and trailing whitespace are bad for plain scalars. We also
759
+ # do not want to mess with leading whitespaces for block scalars.
760
+ allow_flow_plain = allow_block_plain = allow_block = false if leading_spaces || leading_breaks || trailing_spaces
761
+
762
+ # Trailing breaks are fine for block scalars, but unacceptable for
763
+ # plain scalars.
764
+ allow_flow_plain = allow_block_plain = false if trailing_breaks
765
+
766
+ # The combination of (space+ break+) is only acceptable for block
767
+ # scalars.
768
+ allow_flow_plain = allow_block_plain = allow_single_quoted = false if inline_breaks_spaces
769
+
770
+ # Mixed spaces and breaks, as well as special character are only
771
+ # allowed for double quoted scalars.
772
+ allow_flow_plain = allow_block_plain = allow_single_quoted = allow_block = false if mixed_breaks_spaces || special_characters
773
+
774
+ # We don't emit multiline plain scalars.
775
+ allow_flow_plain = allow_block_plain = false if line_breaks
776
+
777
+ # Flow indicators are forbidden for flow plain scalars.
778
+ allow_flow_plain = false if flow_indicators
779
+
780
+ # Block indicators are forbidden for block plain scalars.
781
+ allow_block_plain = false if block_indicators
782
+
783
+ ScalarAnalysis.new(scalar,false,line_breaks,allow_flow_plain,allow_block_plain,allow_single_quoted,allow_double_quoted,allow_block)
784
+ end
785
+
786
+ # Writers.
787
+
788
+ def flush_stream
789
+ @stream.flush if @stream.respond_to?(:flush)
790
+ end
791
+
792
+ def write_stream_start
793
+ end
794
+
795
+ def write_stream_end
796
+ flush_stream
797
+ end
798
+
799
+ def write_indicator(indicator, need_whitespace,whitespace=false,indention=false)
800
+ if @whitespace || !need_whitespace
801
+ data = indicator
802
+ else
803
+ data = " "+indicator
804
+ end
805
+
806
+ @whitespace = whitespace
807
+ @indention = @indention && indention
808
+ @column += data.length
809
+ @stream.write(data)
810
+ end
811
+
812
+ def write_indent
813
+ indent = @indent || 0
814
+ write_line_break if !@indention || @column > indent || (@column == indent && !@whitespace)
815
+ if @column < indent
816
+ @whitespace = true
817
+ data = " "*(indent-@column)
818
+ @column = indent
819
+ @stream.write(data)
820
+ end
821
+ end
822
+
823
+ def write_line_break(data=nil)
824
+ data = @best_line_break if data.nil?
825
+ @whitespace = true
826
+ @indention = true
827
+ @line += 1
828
+ @column = 0
829
+ @stream.write(data)
830
+ end
831
+
832
+
833
+ def write_version_directive(version_text)
834
+ data = "%YAML #{version_text}"
835
+ @stream.write(data)
836
+ write_line_break
837
+ end
838
+
839
+ def write_tag_directive(handle_text, prefix_text)
840
+ data = "%TAG #{handle_text} #{prefix_text}"
841
+ @stream.write(data)
842
+ write_line_break
843
+ end
844
+
845
+ # Scalar streams.
846
+
847
+ def write_single_quoted(text, split=true)
848
+ write_indicator("'",true)
849
+ spaces = false
850
+ breaks = false
851
+ start = ending = 0
852
+ while ending <= text.length
853
+ ch = nil
854
+ ch = text[ending] if ending < text.length
855
+ if spaces
856
+ if ch.nil? || ch != 32
857
+ if start+1 == ending && @column > @best_width && split && start != 0 && ending != text.length
858
+ write_indent
859
+ else
860
+ data = text[start...ending]
861
+ @column += data.length
862
+ @stream.write(data)
863
+ end
864
+ start = ending
865
+ end
866
+ elsif breaks
867
+ if ch.nil? or !"\n\x85".include?(ch)
868
+ write_line_break if text[start] == ?\n
869
+ (text[start...ending]).each_byte { |br|
870
+ if br == ?\n
871
+ write_line_break
872
+ else
873
+ write_line_break(br)
874
+ end
875
+ }
876
+ write_indent
877
+ start = ending
878
+ end
879
+ else
880
+ if ch.nil? || "' \n\x85".include?(ch)
881
+ if start < ending
882
+ data = text[start...ending]
883
+ @column += data.length
884
+ @stream.write(data)
885
+ start = ending
886
+ end
887
+ if ch == ?'
888
+ data = "''"
889
+ @column += 2
890
+ @stream.write(data)
891
+ start = ending + 1
892
+ end
893
+ end
894
+ end
895
+
896
+ if !ch.nil?
897
+ spaces = ch == 32
898
+ breaks = "\n\x85".include?(ch)
899
+ end
900
+
901
+ ending += 1
902
+ end
903
+ write_indicator("'", false)
904
+ end
905
+
906
+ ESCAPE_REPLACEMENTS = {
907
+ ?\0 => "0",
908
+ ?\x07 => "a",
909
+ ?\x08 => "b",
910
+ ?\x09 => "t",
911
+ ?\x0A => "n",
912
+ ?\x0B => "v",
913
+ ?\x0C => "f",
914
+ ?\x0D => "r",
915
+ ?\x1B => "e",
916
+ ?" => "\"",
917
+ ?\\ => "\\",
918
+ ?\x85 => "N",
919
+ ?\xA0 => "_"
920
+ }
921
+
922
+ def write_double_quoted(text, split=true)
923
+ write_indicator('"', true)
924
+ start = ending = 0
925
+ while ending <= text.length
926
+ ch = nil
927
+ ch = text[ending] if ending < text.length
928
+ if ch.nil? || '"\\'.include?(ch) || !(?\x20 <= ch && ch <= ?\x7E)
929
+ if start < ending
930
+ data = text[start...ending]
931
+ @column += data.length
932
+ @stream.write(data)
933
+ start = ending
934
+ end
935
+ if !ch.nil?
936
+ if ESCAPE_REPLACEMENTS.include?(ch)
937
+ data = "\\"+ESCAPE_REPLACEMENTS[ch]
938
+ elsif ch <= ?\xFF
939
+ data = "\\x%02X" % ch
940
+ end
941
+ @column += data.length
942
+ @stream.write(data)
943
+ start = ending+1
944
+ end
945
+ end
946
+ if (0 < ending && ending < text.length-1) && (ch == 32 || start >= ending) && @column+(ending-start) > @best_width && split
947
+ data = text[start...ending]+"\\"
948
+ start = ending if start < ending
949
+ @column += data.length
950
+ @stream.write(data)
951
+ write_indent
952
+ @whitespace = false
953
+ @indention = false
954
+ if ch == 32
955
+ data = "\\"
956
+ @column += data.length
957
+ @stream.write(data)
958
+ end
959
+ end
960
+ ending += 1
961
+ end
962
+ write_indicator('"', false)
963
+ end
964
+
965
+ def determine_chomp(text)
966
+ tail = text[-2..-1]
967
+ tail = " "+tail while tail.length < 2
968
+ "\n\x85".include?(tail[-1])? ("\n\x85".include?(tail[-2])? "+" : "" ) : "-"
969
+ end
970
+
971
+ def write_folded(text)
972
+ chomp = determine_chomp(text)
973
+ write_indicator(">"+chomp, true)
974
+ write_indent
975
+ leading_space = false
976
+ spaces = false
977
+ breaks = false
978
+ start = ending = 0
979
+ while ending <= text.length
980
+ ch = nil
981
+ ch = text[ending] if ending < text.length
982
+ if breaks
983
+ if ch.nil? || !"\n\x85".include?(ch)
984
+ write_line_break if !leading_space && !ch.nil? && ch != 32 && text[start] == ?\n
985
+ leading_space = ch == 32
986
+ (text[start...ending]).each_byte { |br|
987
+ if br == ?\n
988
+ write_line_break
989
+ else
990
+ write_line_break(br)
991
+ end
992
+ }
993
+ write_indent if !ch.nil?
994
+ start = ending
995
+ end
996
+ elsif spaces
997
+ if ch != 32
998
+ if start+1 == ending && @column > @best_width
999
+ write_indent
1000
+ else
1001
+ data = text[start...ending]
1002
+ @column += data.length
1003
+ @stream.write(data)
1004
+ end
1005
+ start = ending
1006
+ end
1007
+ else
1008
+ if ch.nil? || " \n\x85".include?(ch)
1009
+ data = text[start...ending]
1010
+ @stream.write(data)
1011
+ write_line_break if ch.nil?
1012
+ start = ending
1013
+ end
1014
+ end
1015
+ if !ch.nil?
1016
+ breaks = "\n\x85".include?(ch)
1017
+ spaces = ch == 32
1018
+ end
1019
+ ending += 1
1020
+ end
1021
+ end
1022
+
1023
+ def write_literal(text)
1024
+ chomp = determine_chomp(text)
1025
+ write_indicator("|"+chomp, true)
1026
+ write_indent
1027
+ breaks = false
1028
+ start = ending = 0
1029
+ while ending <= text.length
1030
+ ch = nil
1031
+ ch = text[ending] if ending < text.length
1032
+ if breaks
1033
+ if ch.nil? || !"\n\x85".include?(ch)
1034
+ (text[start...ending]).each_byte { |br|
1035
+ if br == ?\n
1036
+ write_line_break
1037
+ else
1038
+ write_line_break(br)
1039
+ end
1040
+ }
1041
+ write_indent if !ch.nil?
1042
+ start = ending
1043
+ end
1044
+ else
1045
+ if ch.nil? || "\n\x85".include?(ch)
1046
+ data = text[start...ending]
1047
+ @stream.write(data)
1048
+ write_line_break if ch.nil?
1049
+ start = ending
1050
+ end
1051
+ end
1052
+ breaks = "\n\x85".include?(ch) if !ch.nil?
1053
+ ending += 1
1054
+ end
1055
+ end
1056
+
1057
+ def write_plain(text, split=true)
1058
+ return nil if text.nil? || text.empty?
1059
+ if !@whitespace
1060
+ data = " "
1061
+ @column += data.length
1062
+ @stream.write(data)
1063
+ end
1064
+ @writespace = false
1065
+ @indention = false
1066
+ spaces = false
1067
+ breaks = false
1068
+ start = ending = 0
1069
+ while ending <= text.length
1070
+ ch = nil
1071
+ ch = text[ending] if ending < text.length
1072
+ if spaces
1073
+ if ch != 32
1074
+ if start+1 == ending && @column > @best_width && split
1075
+ write_indent
1076
+ @writespace = false
1077
+ @indention = false
1078
+ else
1079
+ data = text[start...ending]
1080
+ @column += data.length
1081
+ @stream.write(data)
1082
+ end
1083
+ start = ending
1084
+ end
1085
+ elsif breaks
1086
+ if !"\n\x85".include?(ch)
1087
+ write_line_break if text[start] == ?\n
1088
+ (text[start...ending]).each_byte { |br|
1089
+ if br == ?\n
1090
+ write_line_break
1091
+ else
1092
+ write_line_break(br)
1093
+ end
1094
+ }
1095
+ write_indent
1096
+ @whitespace = false
1097
+ @indention = false
1098
+ start = ending
1099
+ end
1100
+ else
1101
+ if ch.nil? || " \n\x85".include?(ch)
1102
+ data = text[start...ending]
1103
+ @column += data.length
1104
+ @stream.write(data)
1105
+ start = ending
1106
+ end
1107
+ end
1108
+ if !ch.nil?
1109
+ spaces = ch == 32
1110
+ breaks = "\n\x85".include?(ch)
1111
+ end
1112
+ ending += 1
1113
+ end
1114
+ end
1115
+ end
1116
+ end