Class: Psych::Pure::Parser

Inherits:
StringScanner
  • Object
show all
Defined in:
lib/psych/pure.rb

Overview

The parser is responsible for taking a YAML string and converting it into a series of events that can be used by the consumer.

Defined Under Namespace

Classes: Context, Debug

Instance Method Summary collapse

Constructor Details

#initialize(handler) ⇒ Parser

Initialize a new parser with the given source string.



1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
# File 'lib/psych/pure.rb', line 1029

def initialize(handler)
  super("")

  # These are used to track the current state of the parser.
  @filename = nil
  @source = nil

  # The handler is the consumer of the events generated by the parser.
  @handler = handler

  # This functions as a list of temporary lists of events that may be
  # flushed into the handler if current context is matched.
  @events_cache = []
  @events_cache_marks = []
  @events_cache_depth = 0

  # Document start/end state. These are deferred and flushed lazily —
  # the document start is emitted when the first content event arrives,
  # and the document end is emitted when the next document starts.
  @doc_start_pos = nil
  @doc_start_version = nil
  @doc_start_implicit = true
  @doc_end_pos = nil
  @doc_end_implicit = true

  # Each document gets its own set of tags. This is a mapping of tag
  # handles to tag prefixes.
  @tag_directives = nil

  # When a tag property is parsed, it is stored here until it can be
  # flushed into the next event.
  @tag = nil

  # When a tag handle is parsed, it is stored here until the tag prefix
  # is parsed and the full tag can be resolved.
  @tag_handle = nil

  # When an anchor is parsed, it is stored here until it can be flushed
  # into the next event.
  @anchor = nil

  # In a bare document, explicit document starts (---) and ends (...) are
  # disallowed. In that case we need to check for those delimiters.
  @in_bare_document = false
  @check_forbidden = false

  # In a literal or folded scalar, we need to track that state in order to
  # insert the correct plain text prefix.
  @in_scalar = false
  @text_prefix = +""

  # This parser can optionally parse comments and attach them to the
  # resulting tree, if the option is passed.
  @comments = nil

  # The context of the parser at any given time, which is used to decorate
  # error messages to make it easier to find the specific location where
  # they occurred.
  @context = Context.new
end

Instance Method Details

#parse(yaml, filename = yaml.respond_to?(:path) ? yaml.path : "<unknown>", comments: false) ⇒ Object

Top-level parse function that starts the parsing process.



1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
# File 'lib/psych/pure.rb', line 1091

def parse(yaml, filename = yaml.respond_to?(:path) ? yaml.path : "<unknown>", comments: false)
  if yaml.respond_to?(:read)
    yaml = yaml.read
  elsif !yaml.is_a?(String)
    raise TypeError, "Expected an IO or a String, got #{yaml.class}"
  end

  # This parser only supports UTF-8 encoding at the moment. This is
  # largely due to the performance impact of having to convert all of the
  # strings and regular expressions into compatible encodings. We do not
  # attempt to transcode, as the locations would all be off at that point.
  if yaml.encoding != Encoding::UTF_8
    raise ArgumentError, "Expected UTF-8 encoding, got #{yaml.encoding}"
  end

  yaml += "\n" if !yaml.empty? && !yaml.end_with?("\n")

  # Set StringScanner's source (used by skip/match/eos?) and keep a
  # direct reference for raw byte access (getbyte/byteslice) which
  # bypasses StringScanner for performance-critical paths.
  self.string = yaml
  @string = yaml
  @filename = filename
  @source = Source.new(yaml)
  @comments = {} if comments

  # Precompute positions where --- or ... appear at start of a line
  # followed by whitespace or end of string. These are forbidden content
  # positions in bare documents.
  @forbidden_content = {}
  yaml.scan(/^(?:---|\.\.\.)(?=[\s]|\z)/m) { @forbidden_content[$~.begin(0)] = true }
  @has_forbidden_content = !@forbidden_content.empty?

  parse_l_yaml_stream
  @comments = nil if comments
  true
end