psych 3.0.0.beta3 → 3.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 3d6c3b9645d658019f7098fd5864615a1c7dfc2a
4
- data.tar.gz: b63e0bc012c1af5729f694911c0c866c0b28d521
2
+ SHA256:
3
+ metadata.gz: ab8cceae1dcf6bcc2f5a1f99597b618d7cd8d1b4c0e214da7c56a9be8e16c0cc
4
+ data.tar.gz: 9a1c117b8eea479163ce7c3a459d3116d04e6f0861660bd052139fddf217e48e
5
5
  SHA512:
6
- metadata.gz: 31954f81a31758e82291dddf7a4a79cf4e27f1102e9ff6d63819a4b0598cec0e2a90c5f32a38960928fd67ff6e12a80c9ee04a21b4eb5e61ded950a1fae88bc7
7
- data.tar.gz: '088fdde8a03d12c87e52ef355f932c4853e1d15c773f4f4521e863778494b8b1955e4b6dc02b2ef4162a5bb6977e9928fdcfb519d5ac89cc6dc6203f142c0ffc'
6
+ metadata.gz: 5ed2708d7c10501b45785c8e9a0000b804d144ac71e14bf411440d9a7c3dcb6742295a20ef8640aff5a154847c5724b35d4e5654f40d5c1b8277863d42f36d8a
7
+ data.tar.gz: 0c2e9404187db169aa2ea4c19198bbd60495690131fcf16aaa1137723dea4b2b3db36c40c1ca14bc5f2b8afb623f1c9ac106b9cdba4961d23cc5e78b446f328a
@@ -1,13 +1,13 @@
1
1
  rvm:
2
- - 2.2.7
3
- - 2.3.4
4
- - 2.4.1
2
+ - 2.2.8
3
+ - 2.3.5
4
+ - 2.4.2
5
5
  - ruby-head
6
- - jruby-9.1.8.0
6
+ - jruby-9.1.13.0
7
7
 
8
8
  matrix:
9
9
  allow_failures:
10
- - rvm: jruby-9.1.8.0
10
+ - rvm: jruby-9.1.13.0
11
11
 
12
12
  before_script:
13
13
  - unset JRUBY_OPTS
@@ -192,8 +192,8 @@ static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
192
192
  name = rb_str_export_to_enc(name, encoding);
193
193
  value = rb_str_export_to_enc(value, encoding);
194
194
 
195
- tail->handle = (yaml_char_t *)RSTRING_PTR(name);
196
- tail->prefix = (yaml_char_t *)RSTRING_PTR(value);
195
+ tail->handle = (yaml_char_t *)StringValueCStr(name);
196
+ tail->prefix = (yaml_char_t *)StringValueCStr(value);
197
197
 
198
198
  tail++;
199
199
  }
@@ -272,8 +272,8 @@ static VALUE scalar(
272
272
 
273
273
  yaml_scalar_event_initialize(
274
274
  &event,
275
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
276
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
275
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
276
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
277
277
  (yaml_char_t*)StringValuePtr(value),
278
278
  (int)RSTRING_LEN(value),
279
279
  plain ? 1 : 0,
@@ -319,8 +319,8 @@ static VALUE start_sequence(
319
319
 
320
320
  yaml_sequence_start_event_initialize(
321
321
  &event,
322
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
323
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
322
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
323
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
324
324
  implicit ? 1 : 0,
325
325
  (yaml_sequence_style_t)NUM2INT(style)
326
326
  );
@@ -383,8 +383,8 @@ static VALUE start_mapping(
383
383
 
384
384
  yaml_mapping_start_event_initialize(
385
385
  &event,
386
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
387
- (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
386
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor)),
387
+ (yaml_char_t *)(NIL_P(tag) ? NULL : StringValueCStr(tag)),
388
388
  implicit ? 1 : 0,
389
389
  (yaml_mapping_style_t)NUM2INT(style)
390
390
  );
@@ -432,7 +432,7 @@ static VALUE alias(VALUE self, VALUE anchor)
432
432
 
433
433
  yaml_alias_event_initialize(
434
434
  &event,
435
- (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor))
435
+ (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValueCStr(anchor))
436
436
  );
437
437
 
438
438
  emit(emitter, &event);
@@ -16,6 +16,7 @@ static ID id_start_sequence;
16
16
  static ID id_end_sequence;
17
17
  static ID id_start_mapping;
18
18
  static ID id_end_mapping;
19
+ static ID id_event_location;
19
20
 
20
21
  #define PSYCH_TRANSCODE(_str, _yaml_enc, _internal_enc) \
21
22
  do { \
@@ -232,6 +233,12 @@ static VALUE protected_end_stream(VALUE handler)
232
233
  return rb_funcall(handler, id_end_stream, 0);
233
234
  }
234
235
 
236
+ static VALUE protected_event_location(VALUE pointer)
237
+ {
238
+ VALUE *args = (VALUE *)pointer;
239
+ return rb_funcall3(args[0], id_event_location, 4, args + 1);
240
+ }
241
+
235
242
  /*
236
243
  * call-seq:
237
244
  * parser.parse(yaml)
@@ -295,6 +302,21 @@ static VALUE parse(int argc, VALUE *argv, VALUE self)
295
302
  rb_exc_raise(exception);
296
303
  }
297
304
 
305
+ VALUE event_args[5];
306
+ VALUE start_line, start_column, end_line, end_column;
307
+
308
+ start_line = INT2NUM((long)event.start_mark.line);
309
+ start_column = INT2NUM((long)event.start_mark.column);
310
+ end_line = INT2NUM((long)event.end_mark.line);
311
+ end_column = INT2NUM((long)event.end_mark.column);
312
+
313
+ event_args[0] = handler;
314
+ event_args[1] = start_line;
315
+ event_args[2] = start_column;
316
+ event_args[3] = end_line;
317
+ event_args[4] = end_column;
318
+ rb_protect(protected_event_location, (VALUE)event_args, &state);
319
+
298
320
  switch(event.type) {
299
321
  case YAML_STREAM_START_EVENT:
300
322
  {
@@ -551,18 +573,19 @@ void Init_psych_parser(void)
551
573
  rb_define_method(cPsychParser, "parse", parse, -1);
552
574
  rb_define_method(cPsychParser, "mark", mark, 0);
553
575
 
554
- id_read = rb_intern("read");
555
- id_path = rb_intern("path");
556
- id_empty = rb_intern("empty");
557
- id_start_stream = rb_intern("start_stream");
558
- id_end_stream = rb_intern("end_stream");
559
- id_start_document = rb_intern("start_document");
560
- id_end_document = rb_intern("end_document");
561
- id_alias = rb_intern("alias");
562
- id_scalar = rb_intern("scalar");
563
- id_start_sequence = rb_intern("start_sequence");
564
- id_end_sequence = rb_intern("end_sequence");
565
- id_start_mapping = rb_intern("start_mapping");
566
- id_end_mapping = rb_intern("end_mapping");
576
+ id_read = rb_intern("read");
577
+ id_path = rb_intern("path");
578
+ id_empty = rb_intern("empty");
579
+ id_start_stream = rb_intern("start_stream");
580
+ id_end_stream = rb_intern("end_stream");
581
+ id_start_document = rb_intern("start_document");
582
+ id_end_document = rb_intern("end_document");
583
+ id_alias = rb_intern("alias");
584
+ id_scalar = rb_intern("scalar");
585
+ id_start_sequence = rb_intern("start_sequence");
586
+ id_end_sequence = rb_intern("end_sequence");
587
+ id_start_mapping = rb_intern("start_mapping");
588
+ id_end_mapping = rb_intern("end_mapping");
589
+ id_event_location = rb_intern("event_location");
567
590
  }
568
591
  /* vim: set noet sws=4 sw=4: */
@@ -9,7 +9,7 @@ VALUE cPsychVisitorsYamlTree;
9
9
  */
10
10
  static VALUE private_iv_get(VALUE self, VALUE target, VALUE prop)
11
11
  {
12
- return rb_attr_get(target, rb_intern(StringValuePtr(prop)));
12
+ return rb_attr_get(target, rb_intern(StringValueCStr(prop)));
13
13
  }
14
14
 
15
15
  void Init_psych_yaml_tree(void)
@@ -24,8 +24,8 @@
24
24
  */
25
25
 
26
26
  #define PUT_BREAK(emitter) \
27
- (FLUSH(emitter) \
28
- && ((emitter->line_break == YAML_CR_BREAK ? \
27
+ (FLUSH(emitter) ? \
28
+ ((emitter->line_break == YAML_CR_BREAK ? \
29
29
  (*(emitter->buffer.pointer++) = (yaml_char_t) '\r') : \
30
30
  emitter->line_break == YAML_LN_BREAK ? \
31
31
  (*(emitter->buffer.pointer++) = (yaml_char_t) '\n') : \
@@ -34,7 +34,7 @@
34
34
  *(emitter->buffer.pointer++) = (yaml_char_t) '\n') : 0), \
35
35
  emitter->column = 0, \
36
36
  emitter->line ++, \
37
- 1))
37
+ 1) : 0)
38
38
 
39
39
  /*
40
40
  * Copy a character from a string into buffer.
@@ -221,7 +221,7 @@ yaml_emitter_write_indent(yaml_emitter_t *emitter);
221
221
 
222
222
  static int
223
223
  yaml_emitter_write_indicator(yaml_emitter_t *emitter,
224
- char *indicator, int need_whitespace,
224
+ const char *indicator, int need_whitespace,
225
225
  int is_whitespace, int is_indention);
226
226
 
227
227
  static int
@@ -1784,7 +1784,7 @@ yaml_emitter_write_indent(yaml_emitter_t *emitter)
1784
1784
 
1785
1785
  static int
1786
1786
  yaml_emitter_write_indicator(yaml_emitter_t *emitter,
1787
- char *indicator, int need_whitespace,
1787
+ const char *indicator, int need_whitespace,
1788
1788
  int is_whitespace, int is_indention)
1789
1789
  {
1790
1790
  size_t indicator_length;
@@ -2178,7 +2178,7 @@ yaml_emitter_write_block_scalar_hints(yaml_emitter_t *emitter,
2178
2178
  yaml_string_t string)
2179
2179
  {
2180
2180
  char indent_hint[2];
2181
- char *chomp_hint = NULL;
2181
+ const char *chomp_hint = NULL;
2182
2182
 
2183
2183
  if (IS_SPACE(string) || IS_BREAK(string))
2184
2184
  {
@@ -759,9 +759,8 @@ yaml_parser_parse_block_sequence_entry(yaml_parser_t *parser,
759
759
 
760
760
  else if (token->type == YAML_BLOCK_END_TOKEN)
761
761
  {
762
- yaml_mark_t dummy_mark; /* Used to eliminate a compiler warning. */
763
762
  parser->state = POP(parser, parser->states);
764
- dummy_mark = POP(parser, parser->marks);
763
+ (void)POP(parser, parser->marks);
765
764
  SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
766
765
  SKIP_TOKEN(parser);
767
766
  return 1;
@@ -869,9 +868,8 @@ yaml_parser_parse_block_mapping_key(yaml_parser_t *parser,
869
868
 
870
869
  else if (token->type == YAML_BLOCK_END_TOKEN)
871
870
  {
872
- yaml_mark_t dummy_mark; /* Used to eliminate a compiler warning. */
873
871
  parser->state = POP(parser, parser->states);
874
- dummy_mark = POP(parser, parser->marks);
872
+ (void)POP(parser, parser->marks);
875
873
  MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
876
874
  SKIP_TOKEN(parser);
877
875
  return 1;
@@ -952,7 +950,6 @@ yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser,
952
950
  yaml_event_t *event, int first)
953
951
  {
954
952
  yaml_token_t *token;
955
- yaml_mark_t dummy_mark; /* Used to eliminate a compiler warning. */
956
953
 
957
954
  if (first) {
958
955
  token = PEEK_TOKEN(parser);
@@ -997,7 +994,7 @@ yaml_parser_parse_flow_sequence_entry(yaml_parser_t *parser,
997
994
  }
998
995
 
999
996
  parser->state = POP(parser, parser->states);
1000
- dummy_mark = POP(parser, parser->marks);
997
+ (void)POP(parser, parser->marks);
1001
998
  SEQUENCE_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
1002
999
  SKIP_TOKEN(parser);
1003
1000
  return 1;
@@ -1104,7 +1101,6 @@ yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser,
1104
1101
  yaml_event_t *event, int first)
1105
1102
  {
1106
1103
  yaml_token_t *token;
1107
- yaml_mark_t dummy_mark; /* Used to eliminate a compiler warning. */
1108
1104
 
1109
1105
  if (first) {
1110
1106
  token = PEEK_TOKEN(parser);
@@ -1158,7 +1154,7 @@ yaml_parser_parse_flow_mapping_key(yaml_parser_t *parser,
1158
1154
  }
1159
1155
 
1160
1156
  parser->state = POP(parser, parser->states);
1161
- dummy_mark = POP(parser, parser->marks);
1157
+ (void)POP(parser, parser->marks);
1162
1158
  MAPPING_END_EVENT_INIT(*event, token->start_mark, token->end_mark);
1163
1159
  SKIP_TOKEN(parser);
1164
1160
  return 1;
@@ -1186,11 +1186,9 @@ yaml_parser_increase_flow_level(yaml_parser_t *parser)
1186
1186
  static int
1187
1187
  yaml_parser_decrease_flow_level(yaml_parser_t *parser)
1188
1188
  {
1189
- yaml_simple_key_t dummy_key; /* Used to eliminate a compiler warning. */
1190
-
1191
1189
  if (parser->flow_level) {
1192
1190
  parser->flow_level --;
1193
- dummy_key = POP(parser, parser->simple_keys);
1191
+ (void)POP(parser, parser->simple_keys);
1194
1192
  }
1195
1193
 
1196
1194
  return 1;
@@ -1638,7 +1636,7 @@ yaml_parser_fetch_key(yaml_parser_t *parser)
1638
1636
 
1639
1637
  if (!parser->flow_level)
1640
1638
  {
1641
- /* Check if we are allowed to start a new key (not nessesary simple). */
1639
+ /* Check if we are allowed to start a new key (not necessary simple). */
1642
1640
 
1643
1641
  if (!parser->simple_key_allowed) {
1644
1642
  return yaml_parser_set_scanner_error(parser, NULL, parser->mark,
@@ -198,12 +198,13 @@ require 'psych/class_loader'
198
198
  #
199
199
  # ==== Receiving an events stream
200
200
  #
201
- # parser = Psych::Parser.new(Psych::Handlers::Recorder.new)
201
+ # recorder = Psych::Handlers::Recorder.new
202
+ # parser = Psych::Parser.new(recorder)
202
203
  #
203
204
  # parser.parse("---\n - a\n - b")
204
- # parser.events # => [list of [event, args] lists]
205
- # # event is one of: Psych::Handler::EVENTS
206
- # # args are the arguments passed to the event
205
+ # recorder.events # => [list of [event, args] lists]
206
+ # # event is one of: Psych::Handler::EVENTS
207
+ # # args are the arguments passed to the event
207
208
  #
208
209
  # === Emitting
209
210
  #
@@ -251,9 +252,11 @@ module Psych
251
252
  # ex.file # => 'file.txt'
252
253
  # ex.message # => "(file.txt): found character that cannot start any token"
253
254
  # end
254
- def self.load yaml, filename = nil, fallback = false
255
+ def self.load yaml, filename = nil, fallback = false, symbolize_names: false
255
256
  result = parse(yaml, filename, fallback)
256
- result ? result.to_ruby : result
257
+ result = result.to_ruby if result
258
+ symbolize_names!(result) if symbolize_names
259
+ result
257
260
  end
258
261
 
259
262
  ###
@@ -502,6 +505,19 @@ module Psych
502
505
  @dump_tags[klass] = tag
503
506
  end
504
507
 
508
+ def self.symbolize_names!(result)
509
+ case result
510
+ when Hash
511
+ result.keys.each do |key|
512
+ result[key.to_sym] = symbolize_names!(result.delete(key))
513
+ end
514
+ when Array
515
+ result.map! { |r| symbolize_names!(r) }
516
+ end
517
+ result
518
+ end
519
+ private_class_method :symbolize_names!
520
+
505
521
  class << self
506
522
  attr_accessor :load_tags
507
523
  attr_accessor :dump_tags
@@ -241,6 +241,11 @@ module Psych
241
241
  def end_stream
242
242
  end
243
243
 
244
+ ###
245
+ # Called before each event with line/column information.
246
+ def event_location(start_line, start_column, end_line, end_column)
247
+ end
248
+
244
249
  ###
245
250
  # Is this handler a streaming handler?
246
251
  def streaming?
@@ -17,6 +17,18 @@ module Psych
17
17
  # An associated tag
18
18
  attr_reader :tag
19
19
 
20
+ # The line number where this node start
21
+ attr_accessor :start_line
22
+
23
+ # The column number where this node start
24
+ attr_accessor :start_column
25
+
26
+ # The line number where this node ends
27
+ attr_accessor :end_line
28
+
29
+ # The column number where this node ends
30
+ attr_accessor :end_column
31
+
20
32
  # Create a new Psych::Nodes::Node
21
33
  def initialize
22
34
  @children = []
@@ -23,6 +23,18 @@ module Psych
23
23
  @stack = []
24
24
  @last = nil
25
25
  @root = nil
26
+
27
+ @start_line = nil
28
+ @start_column = nil
29
+ @end_line = nil
30
+ @end_column = nil
31
+ end
32
+
33
+ def event_location(start_line, start_column, end_line, end_column)
34
+ @start_line = start_line
35
+ @start_column = start_column
36
+ @end_line = end_line
37
+ @end_column = end_column
26
38
  end
27
39
 
28
40
  %w{
@@ -32,12 +44,15 @@ module Psych
32
44
  class_eval %{
33
45
  def start_#{node.downcase}(anchor, tag, implicit, style)
34
46
  n = Nodes::#{node}.new(anchor, tag, implicit, style)
47
+ set_start_location(n)
35
48
  @last.children << n
36
49
  push n
37
50
  end
38
51
 
39
52
  def end_#{node.downcase}
40
- pop
53
+ n = pop
54
+ set_end_location(n)
55
+ n
41
56
  end
42
57
  }
43
58
  end
@@ -49,6 +64,7 @@ module Psych
49
64
  # See Psych::Handler#start_document
50
65
  def start_document version, tag_directives, implicit
51
66
  n = Nodes::Document.new version, tag_directives, implicit
67
+ set_start_location(n)
52
68
  @last.children << n
53
69
  push n
54
70
  end
@@ -60,26 +76,35 @@ module Psych
60
76
  # See Psych::Handler#start_document
61
77
  def end_document implicit_end = !streaming?
62
78
  @last.implicit_end = implicit_end
63
- pop
79
+ n = pop
80
+ set_end_location(n)
81
+ n
64
82
  end
65
83
 
66
84
  def start_stream encoding
67
85
  @root = Nodes::Stream.new(encoding)
86
+ set_start_location(@root)
68
87
  push @root
69
88
  end
70
89
 
71
90
  def end_stream
72
- pop
91
+ n = pop
92
+ set_end_location(n)
93
+ n
73
94
  end
74
95
 
75
96
  def scalar value, anchor, tag, plain, quoted, style
76
97
  s = Nodes::Scalar.new(value,anchor,tag,plain,quoted,style)
98
+ set_location(s)
77
99
  @last.children << s
78
100
  s
79
101
  end
80
102
 
81
103
  def alias anchor
82
- @last.children << Nodes::Alias.new(anchor)
104
+ a = Nodes::Alias.new(anchor)
105
+ set_location(a)
106
+ @last.children << a
107
+ a
83
108
  end
84
109
 
85
110
  private
@@ -93,5 +118,20 @@ module Psych
93
118
  @last = @stack.last
94
119
  x
95
120
  end
121
+
122
+ def set_location(node)
123
+ set_start_location(node)
124
+ set_end_location(node)
125
+ end
126
+
127
+ def set_start_location(node)
128
+ node.start_line = @start_line
129
+ node.start_column = @start_column
130
+ end
131
+
132
+ def set_end_location(node)
133
+ node.end_line = @end_line
134
+ node.end_column = @end_column
135
+ end
96
136
  end
97
137
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Psych
3
3
  # The version is Psych you're using
4
- VERSION = '3.0.0.beta3'
4
+ VERSION = '3.0.0.beta4'
5
5
 
6
6
  if RUBY_ENGINE == 'jruby'
7
7
  DEFAULT_SNAKEYAML_VERSION = '1.18'.freeze
@@ -304,7 +304,7 @@ module Psych
304
304
  quote = false
305
305
  elsif @line_width && o.length > @line_width
306
306
  style = Nodes::Scalar::FOLDED
307
- elsif o =~ /^[^[:word:]][^"]*$/
307
+ elsif o =~ /^[^[:word:]][^"]*$/ or o =~ /^([^"]*'+[^"]*)+$/
308
308
  style = Nodes::Scalar::DOUBLE_QUOTED
309
309
  elsif not String === @ss.tokenize(o) or /\A0[0-7]*[89]/ =~ o
310
310
  style = Nodes::Scalar::SINGLE_QUOTED
@@ -3,10 +3,10 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "psych"
6
- s.version = "3.0.0.beta3"
6
+ s.version = "3.0.0.beta4"
7
7
  s.authors = ["Aaron Patterson", "SHIBATA Hiroshi", "Charles Oliver Nutter"]
8
8
  s.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org", "headius@headius.com"]
9
- s.date = "2017-06-16"
9
+ s.date = "2017-11-27"
10
10
  s.summary = "Psych is a YAML parser and emitter"
11
11
  s.description = <<-DESCRIPTION
12
12
  Psych is a YAML parser and emitter. Psych leverages libyaml[http://pyyaml.org/wiki/LibYAML]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: psych
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta3
4
+ version: 3.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-06-16 00:00:00.000000000 Z
13
+ date: 2017-11-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake-compiler
@@ -163,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
163
  version: '0'
164
164
  requirements: []
165
165
  rubyforge_project:
166
- rubygems_version: 2.6.12
166
+ rubygems_version: 2.7.2
167
167
  signing_key:
168
168
  specification_version: 4
169
169
  summary: Psych is a YAML parser and emitter