RbYAML 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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