psych 3.0.0.beta2-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +20 -0
  4. data/CHANGELOG.rdoc +576 -0
  5. data/Gemfile +3 -0
  6. data/Mavenfile +7 -0
  7. data/README.md +73 -0
  8. data/Rakefile +46 -0
  9. data/bin/console +7 -0
  10. data/bin/setup +6 -0
  11. data/ext/psych/.gitignore +11 -0
  12. data/ext/psych/depend +3 -0
  13. data/ext/psych/extconf.rb +39 -0
  14. data/ext/psych/psych.c +34 -0
  15. data/ext/psych/psych.h +17 -0
  16. data/ext/psych/psych_emitter.c +554 -0
  17. data/ext/psych/psych_emitter.h +8 -0
  18. data/ext/psych/psych_parser.c +568 -0
  19. data/ext/psych/psych_parser.h +6 -0
  20. data/ext/psych/psych_to_ruby.c +39 -0
  21. data/ext/psych/psych_to_ruby.h +8 -0
  22. data/ext/psych/psych_yaml_tree.c +24 -0
  23. data/ext/psych/psych_yaml_tree.h +8 -0
  24. data/ext/psych/yaml/LICENSE +19 -0
  25. data/ext/psych/yaml/api.c +1392 -0
  26. data/ext/psych/yaml/config.h +10 -0
  27. data/ext/psych/yaml/dumper.c +394 -0
  28. data/ext/psych/yaml/emitter.c +2329 -0
  29. data/ext/psych/yaml/loader.c +444 -0
  30. data/ext/psych/yaml/parser.c +1374 -0
  31. data/ext/psych/yaml/reader.c +469 -0
  32. data/ext/psych/yaml/scanner.c +3576 -0
  33. data/ext/psych/yaml/writer.c +141 -0
  34. data/ext/psych/yaml/yaml.h +1971 -0
  35. data/ext/psych/yaml/yaml_private.h +662 -0
  36. data/lib/2.2/psych.so +0 -0
  37. data/lib/2.3/psych.so +0 -0
  38. data/lib/2.4/psych.so +0 -0
  39. data/lib/psych/class_loader.rb +102 -0
  40. data/lib/psych/coder.rb +95 -0
  41. data/lib/psych/core_ext.rb +19 -0
  42. data/lib/psych/exception.rb +14 -0
  43. data/lib/psych/handler.rb +250 -0
  44. data/lib/psych/handlers/document_stream.rb +23 -0
  45. data/lib/psych/handlers/recorder.rb +40 -0
  46. data/lib/psych/json/ruby_events.rb +20 -0
  47. data/lib/psych/json/stream.rb +17 -0
  48. data/lib/psych/json/tree_builder.rb +13 -0
  49. data/lib/psych/json/yaml_events.rb +30 -0
  50. data/lib/psych/nodes/alias.rb +19 -0
  51. data/lib/psych/nodes/document.rb +61 -0
  52. data/lib/psych/nodes/mapping.rb +57 -0
  53. data/lib/psych/nodes/node.rb +56 -0
  54. data/lib/psych/nodes/scalar.rb +68 -0
  55. data/lib/psych/nodes/sequence.rb +82 -0
  56. data/lib/psych/nodes/stream.rb +38 -0
  57. data/lib/psych/nodes.rb +78 -0
  58. data/lib/psych/omap.rb +5 -0
  59. data/lib/psych/parser.rb +52 -0
  60. data/lib/psych/scalar_scanner.rb +149 -0
  61. data/lib/psych/set.rb +5 -0
  62. data/lib/psych/stream.rb +38 -0
  63. data/lib/psych/streaming.rb +28 -0
  64. data/lib/psych/syntax_error.rb +22 -0
  65. data/lib/psych/tree_builder.rb +97 -0
  66. data/lib/psych/versions.rb +9 -0
  67. data/lib/psych/visitors/depth_first.rb +27 -0
  68. data/lib/psych/visitors/emitter.rb +52 -0
  69. data/lib/psych/visitors/json_tree.rb +25 -0
  70. data/lib/psych/visitors/to_ruby.rb +401 -0
  71. data/lib/psych/visitors/visitor.rb +20 -0
  72. data/lib/psych/visitors/yaml_tree.rb +551 -0
  73. data/lib/psych/visitors.rb +7 -0
  74. data/lib/psych/y.rb +10 -0
  75. data/lib/psych.rb +511 -0
  76. data/psych.gemspec +64 -0
  77. metadata +175 -0
@@ -0,0 +1,551 @@
1
+ # frozen_string_literal: true
2
+ require 'psych/tree_builder'
3
+ require 'psych/scalar_scanner'
4
+ require 'psych/class_loader'
5
+
6
+ module Psych
7
+ module Visitors
8
+ ###
9
+ # YAMLTree builds a YAML ast given a Ruby object. For example:
10
+ #
11
+ # builder = Psych::Visitors::YAMLTree.new
12
+ # builder << { :foo => 'bar' }
13
+ # builder.tree # => #<Psych::Nodes::Stream .. }
14
+ #
15
+ class YAMLTree < Psych::Visitors::Visitor
16
+ class Registrar # :nodoc:
17
+ def initialize
18
+ @obj_to_id = {}
19
+ @obj_to_node = {}
20
+ @targets = []
21
+ @counter = 0
22
+ end
23
+
24
+ def register target, node
25
+ return unless target.respond_to? :object_id
26
+ @targets << target
27
+ @obj_to_node[target.object_id] = node
28
+ end
29
+
30
+ def key? target
31
+ @obj_to_node.key? target.object_id
32
+ rescue NoMethodError
33
+ false
34
+ end
35
+
36
+ def id_for target
37
+ @obj_to_id[target.object_id] ||= (@counter += 1)
38
+ end
39
+
40
+ def node_for target
41
+ @obj_to_node[target.object_id]
42
+ end
43
+ end
44
+
45
+ attr_reader :started, :finished
46
+ alias :finished? :finished
47
+ alias :started? :started
48
+
49
+ def self.create options = {}, emitter = nil
50
+ emitter ||= TreeBuilder.new
51
+ class_loader = ClassLoader.new
52
+ ss = ScalarScanner.new class_loader
53
+ new(emitter, ss, options)
54
+ end
55
+
56
+ def initialize emitter, ss, options
57
+ super()
58
+ @started = false
59
+ @finished = false
60
+ @emitter = emitter
61
+ @st = Registrar.new
62
+ @ss = ss
63
+ @options = options
64
+ @line_width = options[:line_width]
65
+ if @line_width && @line_width < 0
66
+ if @line_width == -1
67
+ # Treat -1 as unlimited line-width, same as libyaml does.
68
+ @line_width = nil
69
+ else
70
+ fail(ArgumentError, "Invalid line_width #{@line_width}, must be non-negative or -1 for unlimited.")
71
+ end
72
+ end
73
+ @coders = []
74
+
75
+ @dispatch_cache = Hash.new do |h,klass|
76
+ method = "visit_#{(klass.name || '').split('::').join('_')}"
77
+
78
+ method = respond_to?(method) ? method : h[klass.superclass]
79
+
80
+ raise(TypeError, "Can't dump #{target.class}") unless method
81
+
82
+ h[klass] = method
83
+ end
84
+ end
85
+
86
+ def start encoding = Nodes::Stream::UTF8
87
+ @emitter.start_stream(encoding).tap do
88
+ @started = true
89
+ end
90
+ end
91
+
92
+ def finish
93
+ @emitter.end_stream.tap do
94
+ @finished = true
95
+ end
96
+ end
97
+
98
+ def tree
99
+ finish unless finished?
100
+ @emitter.root
101
+ end
102
+
103
+ def push object
104
+ start unless started?
105
+ version = []
106
+ version = [1,1] if @options[:header]
107
+
108
+ case @options[:version]
109
+ when Array
110
+ version = @options[:version]
111
+ when String
112
+ version = @options[:version].split('.').map { |x| x.to_i }
113
+ else
114
+ version = [1,1]
115
+ end if @options.key? :version
116
+
117
+ @emitter.start_document version, [], false
118
+ accept object
119
+ @emitter.end_document !@emitter.streaming?
120
+ end
121
+ alias :<< :push
122
+
123
+ def accept target
124
+ # return any aliases we find
125
+ if @st.key? target
126
+ oid = @st.id_for target
127
+ node = @st.node_for target
128
+ anchor = oid.to_s
129
+ node.anchor = anchor
130
+ return @emitter.alias anchor
131
+ end
132
+
133
+ if target.respond_to?(:encode_with)
134
+ dump_coder target
135
+ else
136
+ send(@dispatch_cache[target.class], target)
137
+ end
138
+ end
139
+
140
+ def visit_Psych_Omap o
141
+ seq = @emitter.start_sequence(nil, 'tag:yaml.org,2002:omap', false, Nodes::Sequence::BLOCK)
142
+ register(o, seq)
143
+
144
+ o.each { |k,v| visit_Hash k => v }
145
+ @emitter.end_sequence
146
+ end
147
+
148
+ def visit_Encoding o
149
+ tag = "!ruby/encoding"
150
+ @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY
151
+ end
152
+
153
+ def visit_Object o
154
+ tag = Psych.dump_tags[o.class]
155
+ unless tag
156
+ klass = o.class == Object ? nil : o.class.name
157
+ tag = ['!ruby/object', klass].compact.join(':')
158
+ end
159
+
160
+ map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
161
+ register(o, map)
162
+
163
+ dump_ivars o
164
+ @emitter.end_mapping
165
+ end
166
+
167
+ alias :visit_Delegator :visit_Object
168
+
169
+ def visit_Struct o
170
+ tag = ['!ruby/struct', o.class.name].compact.join(':')
171
+
172
+ register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
173
+ o.members.each do |member|
174
+ @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY
175
+ accept o[member]
176
+ end
177
+
178
+ dump_ivars o
179
+
180
+ @emitter.end_mapping
181
+ end
182
+
183
+ def visit_Exception o
184
+ tag = ['!ruby/exception', o.class.name].join ':'
185
+
186
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
187
+
188
+ {
189
+ 'message' => private_iv_get(o, 'mesg'),
190
+ 'backtrace' => private_iv_get(o, 'backtrace'),
191
+ }.each do |k,v|
192
+ next unless v
193
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
194
+ accept v
195
+ end
196
+
197
+ dump_ivars o
198
+
199
+ @emitter.end_mapping
200
+ end
201
+
202
+ def visit_NameError o
203
+ tag = ['!ruby/exception', o.class.name].join ':'
204
+
205
+ @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK
206
+
207
+ {
208
+ 'message' => o.message.to_s,
209
+ 'backtrace' => private_iv_get(o, 'backtrace'),
210
+ }.each do |k,v|
211
+ next unless v
212
+ @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY
213
+ accept v
214
+ end
215
+
216
+ dump_ivars o
217
+
218
+ @emitter.end_mapping
219
+ end
220
+
221
+ def visit_Regexp o
222
+ register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY)
223
+ end
224
+
225
+ def visit_DateTime o
226
+ formatted = if o.offset.zero?
227
+ o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze)
228
+ else
229
+ o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze)
230
+ end
231
+ tag = '!ruby/object:DateTime'
232
+ register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY)
233
+ end
234
+
235
+ def visit_Time o
236
+ formatted = format_time o
237
+ register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY)
238
+ end
239
+
240
+ def visit_Rational o
241
+ register o, @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK)
242
+
243
+ [
244
+ 'denominator', o.denominator.to_s,
245
+ 'numerator', o.numerator.to_s
246
+ ].each do |m|
247
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
248
+ end
249
+
250
+ @emitter.end_mapping
251
+ end
252
+
253
+ def visit_Complex o
254
+ register o, @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK)
255
+
256
+ ['real', o.real.to_s, 'image', o.imag.to_s].each do |m|
257
+ @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY
258
+ end
259
+
260
+ @emitter.end_mapping
261
+ end
262
+
263
+ def visit_Integer o
264
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
265
+ end
266
+ alias :visit_TrueClass :visit_Integer
267
+ alias :visit_FalseClass :visit_Integer
268
+ alias :visit_Date :visit_Integer
269
+
270
+ def visit_Float o
271
+ if o.nan?
272
+ @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY
273
+ elsif o.infinite?
274
+ @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'),
275
+ nil, nil, true, false, Nodes::Scalar::ANY)
276
+ else
277
+ @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY
278
+ end
279
+ end
280
+
281
+ def visit_BigDecimal o
282
+ @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY
283
+ end
284
+
285
+ def visit_String o
286
+ plain = true
287
+ quote = true
288
+ style = Nodes::Scalar::PLAIN
289
+ tag = nil
290
+
291
+ if binary?(o)
292
+ o = [o].pack('m0')
293
+ tag = '!binary' # FIXME: change to below when syck is removed
294
+ #tag = 'tag:yaml.org,2002:binary'
295
+ style = Nodes::Scalar::LITERAL
296
+ plain = false
297
+ quote = false
298
+ elsif o =~ /\n(?!\Z)/ # match \n except blank line at the end of string
299
+ style = Nodes::Scalar::LITERAL
300
+ elsif o == '<<'
301
+ style = Nodes::Scalar::SINGLE_QUOTED
302
+ tag = 'tag:yaml.org,2002:str'
303
+ plain = false
304
+ quote = false
305
+ elsif @line_width && o.length > @line_width
306
+ style = Nodes::Scalar::FOLDED
307
+ elsif o =~ /^[^[:word:]][^"]*$/
308
+ style = Nodes::Scalar::DOUBLE_QUOTED
309
+ elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/ =~ o
310
+ style = Nodes::Scalar::SINGLE_QUOTED
311
+ end
312
+
313
+ is_primitive = o.class == ::String
314
+ ivars = is_primitive ? [] : o.instance_variables
315
+
316
+ if ivars.empty?
317
+ unless is_primitive
318
+ tag = "!ruby/string:#{o.class}"
319
+ plain = false
320
+ quote = false
321
+ end
322
+ @emitter.scalar o, nil, tag, plain, quote, style
323
+ else
324
+ maptag = '!ruby/string'.dup
325
+ maptag << ":#{o.class}" unless o.class == ::String
326
+
327
+ register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK)
328
+ @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY
329
+ @emitter.scalar o, nil, tag, plain, quote, style
330
+
331
+ dump_ivars o
332
+
333
+ @emitter.end_mapping
334
+ end
335
+ end
336
+
337
+ def visit_Module o
338
+ raise TypeError, "can't dump anonymous module: #{o}" unless o.name
339
+ register o, @emitter.scalar(o.name, nil, '!ruby/module', false, false, Nodes::Scalar::SINGLE_QUOTED)
340
+ end
341
+
342
+ def visit_Class o
343
+ raise TypeError, "can't dump anonymous class: #{o}" unless o.name
344
+ register o, @emitter.scalar(o.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED)
345
+ end
346
+
347
+ def visit_Range o
348
+ register o, @emitter.start_mapping(nil, '!ruby/range', false, Nodes::Mapping::BLOCK)
349
+ ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m|
350
+ accept m
351
+ end
352
+ @emitter.end_mapping
353
+ end
354
+
355
+ def visit_Hash o
356
+ if o.class == ::Hash
357
+ register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK))
358
+ o.each do |k,v|
359
+ accept k
360
+ accept v
361
+ end
362
+ @emitter.end_mapping
363
+ else
364
+ visit_hash_subclass o
365
+ end
366
+ end
367
+
368
+ def visit_Psych_Set o
369
+ register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK))
370
+
371
+ o.each do |k,v|
372
+ accept k
373
+ accept v
374
+ end
375
+
376
+ @emitter.end_mapping
377
+ end
378
+
379
+ def visit_Array o
380
+ if o.class == ::Array
381
+ visit_Enumerator o
382
+ else
383
+ visit_array_subclass o
384
+ end
385
+ end
386
+
387
+ def visit_Enumerator o
388
+ register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
389
+ o.each { |c| accept c }
390
+ @emitter.end_sequence
391
+ end
392
+
393
+ def visit_NilClass o
394
+ @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY)
395
+ end
396
+
397
+ def visit_Symbol o
398
+ if o.empty?
399
+ @emitter.scalar "", nil, '!ruby/symbol', false, false, Nodes::Scalar::ANY
400
+ else
401
+ @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY
402
+ end
403
+ end
404
+
405
+ def visit_BasicObject o
406
+ tag = Psych.dump_tags[o.class]
407
+ tag ||= "!ruby/marshalable:#{o.class.name}"
408
+
409
+ map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK)
410
+ register(o, map)
411
+
412
+ o.marshal_dump.each(&method(:accept))
413
+
414
+ @emitter.end_mapping
415
+ end
416
+
417
+ private
418
+
419
+ def binary? string
420
+ string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?
421
+ end
422
+
423
+ def visit_array_subclass o
424
+ tag = "!ruby/array:#{o.class}"
425
+ ivars = o.instance_variables
426
+ if ivars.empty?
427
+ node = @emitter.start_sequence(nil, tag, false, Nodes::Sequence::BLOCK)
428
+ register o, node
429
+ o.each { |c| accept c }
430
+ @emitter.end_sequence
431
+ else
432
+ node = @emitter.start_mapping(nil, tag, false, Nodes::Sequence::BLOCK)
433
+ register o, node
434
+
435
+ # Dump the internal list
436
+ accept 'internal'
437
+ @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK)
438
+ o.each { |c| accept c }
439
+ @emitter.end_sequence
440
+
441
+ # Dump the ivars
442
+ accept 'ivars'
443
+ @emitter.start_mapping(nil, nil, true, Nodes::Sequence::BLOCK)
444
+ ivars.each do |ivar|
445
+ accept ivar
446
+ accept o.instance_variable_get ivar
447
+ end
448
+ @emitter.end_mapping
449
+
450
+ @emitter.end_mapping
451
+ end
452
+ end
453
+
454
+ def visit_hash_subclass o
455
+ ivars = o.instance_variables
456
+ if ivars.any?
457
+ tag = "!ruby/hash-with-ivars:#{o.class}"
458
+ node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
459
+ register(o, node)
460
+
461
+ # Dump the elements
462
+ accept 'elements'
463
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
464
+ o.each do |k,v|
465
+ accept k
466
+ accept v
467
+ end
468
+ @emitter.end_mapping
469
+
470
+ # Dump the ivars
471
+ accept 'ivars'
472
+ @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK
473
+ o.instance_variables.each do |ivar|
474
+ accept ivar
475
+ accept o.instance_variable_get ivar
476
+ end
477
+ @emitter.end_mapping
478
+
479
+ @emitter.end_mapping
480
+ else
481
+ tag = "!ruby/hash:#{o.class}"
482
+ node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK)
483
+ register(o, node)
484
+ o.each do |k,v|
485
+ accept k
486
+ accept v
487
+ end
488
+ @emitter.end_mapping
489
+ end
490
+ end
491
+
492
+ def dump_list o
493
+ end
494
+
495
+ def format_time time
496
+ if time.utc?
497
+ time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
498
+ else
499
+ time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
500
+ end
501
+ end
502
+
503
+ def register target, yaml_obj
504
+ @st.register target, yaml_obj
505
+ yaml_obj
506
+ end
507
+
508
+ def dump_coder o
509
+ @coders << o
510
+ tag = Psych.dump_tags[o.class]
511
+ unless tag
512
+ klass = o.class == Object ? nil : o.class.name
513
+ tag = ['!ruby/object', klass].compact.join(':')
514
+ end
515
+
516
+ c = Psych::Coder.new(tag)
517
+ o.encode_with(c)
518
+ emit_coder c, o
519
+ end
520
+
521
+ def emit_coder c, o
522
+ case c.type
523
+ when :scalar
524
+ @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY
525
+ when :seq
526
+ @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK
527
+ c.seq.each do |thing|
528
+ accept thing
529
+ end
530
+ @emitter.end_sequence
531
+ when :map
532
+ register o, @emitter.start_mapping(nil, c.tag, c.implicit, c.style)
533
+ c.map.each do |k,v|
534
+ accept k
535
+ accept v
536
+ end
537
+ @emitter.end_mapping
538
+ when :object
539
+ accept c.object
540
+ end
541
+ end
542
+
543
+ def dump_ivars target
544
+ target.instance_variables.each do |iv|
545
+ @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY)
546
+ accept target.instance_variable_get(iv)
547
+ end
548
+ end
549
+ end
550
+ end
551
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require 'psych/visitors/visitor'
3
+ require 'psych/visitors/to_ruby'
4
+ require 'psych/visitors/emitter'
5
+ require 'psych/visitors/yaml_tree'
6
+ require 'psych/visitors/json_tree'
7
+ require 'psych/visitors/depth_first'
data/lib/psych/y.rb ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Kernel
3
+ ###
4
+ # An alias for Psych.dump_stream meant to be used with IRB.
5
+ def y *objects
6
+ puts Psych.dump_stream(*objects)
7
+ end
8
+ private :y
9
+ end
10
+