123456789_123456789_123456789_123456789_123456789_

Class: Psych::Parser

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: ext/psych/psych_parser_fy.c,
ext/psych/psych_parser.c,
ext/psych/lib/psych/parser.rb

Overview

YAML event parser class. This class parses a YAML document and calls events on the handler that is passed to the constructor. The events can be used for things such as constructing a YAML AST or deserializing YAML documents. It can even be fed back to Emitter to emit the same document that was parsed.

See Handler for documentation on the events that Parser emits.

Here is an example that prints out ever scalar found in a YAML document:

# Handler for detecting scalar values
class ScalarHandler < Psych::Handler
def scalar value, anchor, tag, plain, quoted, style
  puts value
end
end

parser = Psych::Parser.new(ScalarHandler.new)
parser.parse(yaml_document)

Here is an example that feeds the parser back in to Emitter. The YAML document is read from STDIN and written back out to STDERR:

parser = Psych::Parser.new(Psych::Emitter.new($stderr))
parser.parse($stdin)

::Psych uses Parser in combination with TreeBuilder to construct an AST of the parsed YAML document.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(handler = Handler.new) ⇒ Parser

Creates a new Parser instance with #handler. YAML events will be called on #handler. See Parser for more details.

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 47

def initialize handler = Handler.new
  @handler = handler
  @external_encoding = ANY
end

Instance Attribute Details

#external_encoding=(value) (writeonly)

Set the encoding for this parser to encoding

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 41

attr_writer :external_encoding

#handler (rw)

The handler on which events will be called

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 38

attr_accessor :handler

Instance Method Details

#_native_parse(handler, yaml, path) (private)

[ GitHub ]

  
# File 'ext/psych/psych_parser.c', line 550

static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path)
{
    psych_parser_t * parser;
    struct parse_args pargs;

    TypedData_Get_Struct(self, psych_parser_t, &psych_parser_type, parser);

    if (parser->parsing) {
        rb_raise(rb_const_get(mPsych, rb_intern("Exception")),
                "parser is already parsing, it cannot be reused from a handler callback");
    }
    parser->parsing = 1;

    pargs.psych_parser = parser;
    pargs.self         = self;
    pargs.handler      = handler;
    pargs.yaml         = yaml;
    pargs.path         = path;

    return rb_ensure(parse_body, (VALUE)&pargs, parse_ensure, (VALUE)parser);
}

#mark(#) ⇒ Parser

Returns a Parser::Mark object that contains line, column, and index information.

[ GitHub ]

  
# File 'ext/psych/psych_parser.c', line 579

static VALUE mark(VALUE self)
{
    VALUE mark_klass;
    VALUE args[3];
    psych_parser_t * parser;

    TypedData_Get_Struct(self, psych_parser_t, &psych_parser_type, parser);
    mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark"));
    args[0] = SIZET2NUM(parser->yaml_parser.mark.index);
    args[1] = SIZET2NUM(parser->yaml_parser.mark.line);
    args[2] = SIZET2NUM(parser->yaml_parser.mark.column);

    return rb_class_new_instance(3, args, mark_klass);
}

#parse(yaml)

Parse the YAML document contained in yaml. Events will be called on the handler set on the parser instance.

See Parser and #handler

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 61

def parse yaml, path = yaml.respond_to?(:path) ? yaml.path : "<unknown>"
  _native_parse @handler, strip_bom(yaml), path
end

#skip_io_bom(io, bom) (private)

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 94

def skip_io_bom io, bom
  begin
    pos = io.pos
  rescue SystemCallError, IOError
    return # Not seekable; nothing has been consumed yet.
  end
  head = io.read(bom.bytesize)
  io.seek(pos, IO::SEEK_SET) if head && head.b != bom
end

#strip_bom(yaml) (private)

libyaml only skips a leading byte order mark when it detects the stream encoding by itself. ::Psych passes the encoding explicitly whenever it is known, and on that path libyaml counts the BOM as a first-line character, which shifts the column of every token on the first line and silently terminates a block mapping at the second line [Bug #13615].

[ GitHub ]

  
# File 'ext/psych/lib/psych/parser.rb', line 81

def strip_bom yaml
  if String === yaml
    bom = BOM[yaml.encoding]
    # delete_prefix copies even when there is no prefix, so keep the guard.
    return yaml.delete_prefix(bom) if bom && yaml.start_with?(bom)
  elsif yaml.respond_to?(:read) && yaml.respond_to?(:external_encoding) &&
        yaml.respond_to?(:pos) && yaml.respond_to?(:seek)
    bom = BOM[yaml.external_encoding]
    skip_io_bom yaml, bom.b if bom
  end
  yaml
end