Class: Gem::YAMLSerializer::Parser
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/rubygems/yaml_serializer.rb |
Constant Summary
-
MAPPING_KEY_RE =
# File 'lib/rubygems/yaml_serializer.rb', line 32/^((?:[^#:]|:[^ ])+):(?:[ ]+(.*))?$/ -
MAX_NESTING_DEPTH =
# File 'lib/rubygems/yaml_serializer.rb', line 331_000
Class Method Summary
- .new(source) ⇒ Parser constructor
Instance Method Summary
- #parse
- #apply_tag(node, tag, anchor) private
- #coerce(val) private
- #consume_anchor private
- #consume_value_anchor(val) private
- #current_indent private
- #decode_binary_tag(str) private
- #extract_item_anchor(content) private
- #parse_alias_ref private
- #parse_binary_value(val, indent) private
- #parse_block_scalar(base_indent, modifier) private
- #parse_inline_alias(content) private
- #parse_inline_scalar(val, indent) private
- #parse_mapping(indent, anchor) private
- #parse_mapping_value(val, indent) private
- #parse_node(base_indent) private
- #parse_plain_scalar(indent, anchor) private
- #parse_sequence(indent, anchor) private
- #parse_sequence_item(content, indent) private
- #parse_tagged_content(tag, indent) private
- #parse_tagged_node(indent, anchor) private
- #register_anchor(name, node) private
- #skip_blank_and_comments private
- #strip_comment(val) private
- #strip_document_prefix private
Constructor Details
.new(source) ⇒ Parser
# File 'lib/rubygems/yaml_serializer.rb', line 35
def initialize(source) @lines = source.split("\n") @anchors = {} @depth = 0 strip_document_prefix end
Instance Method Details
#apply_tag(node, tag, anchor) (private)
[ GitHub ]#coerce(val) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 269
def coerce(val) val = val.sub(/^! /, "") if val.start_with?("! ") if val =~ /^"(.*)"$/ $1.gsub(/\\["nrt\\]/) do |m| case m when '\\"' then '"' when "\\n" then "\n" when "\\r" then "\r" when "\\t" then "\t" when "\\\\" then "\\" end end elsif val =~ /^'(.*)'$/ $1.gsub(/''/, "'") elsif val == "true" true elsif val == "false" false elsif ["~", "null"].include?(val) nil elsif val == "{}" Mapping.new elsif val =~ /^\[(.*)\]$/ inner = $1.strip return Sequence.new if inner.empty? items = inner.split(/\s*,\s*/).reject(&:empty?).map {|e| Scalar.new(value: coerce(e)) } Sequence.new(items: items) elsif /\A\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}:\d{2})?/.match?(val) begin Time.new(val) rescue ArgumentError # date-only format like "2024-06-15" is not supported by Time.new if /\A(\d{4})-(\d{2})-(\d{2})\z/.match(val) Time.utc($1.to_i, $2.to_i, $3.to_i) else val end end elsif /^-?\d+$/.match?(val) val.to_i else val end end
#consume_anchor (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 344
def consume_anchor line = @lines[0] stripped = line.lstrip return nil unless stripped.start_with?("&") && stripped =~ /^&(\S)\s/ anchor = $1 @lines[0] = line.sub(/&#{Regexp.escape(anchor)}\s+/, "") anchor end
#consume_value_anchor(val) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 361
def consume_value_anchor(val) return [nil, val] unless val =~ /^&(\S)\s/ anchor = $1 [anchor, val.sub(/^&#{Regexp.escape(anchor)}\s+/, "")] end
#current_indent (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 339
def current_indent line = @lines[0] line.size - line.lstrip.size end
#decode_binary_tag(str) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 315
def decode_binary_tag(str) content = str.sub(/\A!binary\s+/, "") content = $1 if content =~ /\A"(.*)"\z/ || content =~ /\A'(.*)'\z/ content.unpack1("m") end
#extract_item_anchor(content) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 354
def extract_item_anchor(content) return [nil, content] unless content =~ /^&(\S+)/ anchor = $1 [anchor, content.sub(/^&#{Regexp.escape(anchor)}\s*/, "")] end
#parse
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 42
def parse return nil if @lines.empty? root = nil while @lines.any? before = @lines.size node = parse_node(-1) @lines.shift if @lines.size == before && @lines.any? if root.is_a?(Mapping) && node.is_a?(Mapping) root.pairs.concat(node.pairs) elsif root.nil? root = node end end root end
#parse_alias_ref (private)
[ GitHub ]#parse_binary_value(val, indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 321
def parse_binary_value(val, indent) rest = val.sub(/\A!binary\s+/, "") if rest.start_with?("|") content = parse_block_scalar(indent, rest[1..].to_s.strip) Scalar.new(value: content.unpack1("m")) else Scalar.new(value: decode_binary_tag(val)) end end
#parse_block_scalar(base_indent, modifier) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 225
def parse_block_scalar(base_indent, modifier) parts = [] block_indent = nil while @lines.any? line = @lines[0] if line.strip.empty? parts << "\n" @lines.shift else line_indent = line.size - line.lstrip.size break if line_indent <= base_indent block_indent ||= line_indent parts << @lines.shift[block_indent..].to_s << "\n" end end res = parts.join res.chomp! if modifier == "-" && res.end_with?("\n") res end
#parse_inline_alias(content) (private)
[ GitHub ]#parse_inline_scalar(val, indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 258
def parse_inline_scalar(val, indent) result = coerce(val) return result if result.is_a?(Mapping) || result.is_a?(Sequence) while result.is_a?(String) && @lines.any? && !@lines[0].strip.empty? && current_indent > indent result << " " << @lines.shift.strip end Scalar.new(value: result) end
#parse_mapping(indent, anchor) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 150
def parse_mapping(indent, anchor) pairs = [] while @lines.any? line = @lines[0] stripped = line.lstrip break unless line.size - stripped.size == indent && stripped =~ MAPPING_KEY_RE && !stripped.start_with?("!ruby/object:") key = $1.strip @lines.shift val = strip_comment($2.to_s.strip) key = decode_binary_tag(key) if key.start_with?("!binary ") val_anchor, val = consume_value_anchor(val) value = parse_mapping_value(val, indent) value = register_anchor(val_anchor, value) if val_anchor pairs << [Scalar.new(value: key), value] end register_anchor(anchor, Mapping.new(pairs: pairs)) end
#parse_mapping_value(val, indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 172
def parse_mapping_value(val, indent) if val.start_with?("*") parse_inline_alias(val) elsif val.start_with?("!ruby/object:") parse_tagged_content(val.strip, indent) elsif val.start_with?("!binary ") parse_binary_value(val, indent) elsif val.empty? next_stripped = nil next_indent = nil if @lines.any? next_stripped = @lines[0].lstrip next_indent = @lines[0].size - next_stripped.size end if next_stripped && (next_stripped.start_with?("- ") || next_stripped == "-") && next_indent == indent parse_node(indent) else parse_node(indent + 1) end elsif val == "[]" Sequence.new elsif val == "{}" Mapping.new elsif val.start_with?("|") Scalar.new(value: parse_block_scalar(indent, val[1..].to_s.strip)) else parse_inline_scalar(val, indent) end end
#parse_node(base_indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 73
def parse_node(base_indent) @depth += 1 if @depth > MAX_NESTING_DEPTH raise Psych::SyntaxError, "exceeded maximum nesting depth (#{MAX_NESTING_DEPTH})" end skip_blank_and_comments return nil if @lines.empty? line = @lines[0] stripped = line.lstrip indent = line.size - stripped.size return nil if indent < base_indent return parse_alias_ref if stripped.start_with?("*") anchor = consume_anchor if anchor line = @lines[0] stripped = line.lstrip end if stripped.start_with?("- ") || stripped == "-" parse_sequence(indent, anchor) elsif stripped =~ MAPPING_KEY_RE && !stripped.start_with?("!ruby/object:") parse_mapping(indent, anchor) elsif stripped.start_with?("!ruby/object:") parse_tagged_node(indent, anchor) elsif stripped.start_with?("|") modifier = stripped[1..].to_s.strip @lines.shift register_anchor(anchor, Scalar.new(value: parse_block_scalar(indent, modifier))) else parse_plain_scalar(indent, anchor) end ensure @depth -= 1 end
#parse_plain_scalar(indent, anchor) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 247
def parse_plain_scalar(indent, anchor) result = coerce(@lines.shift.strip) return register_anchor(anchor, result) if result.is_a?(Mapping) || result.is_a?(Sequence) while result.is_a?(String) && @lines.any? && !@lines[0].strip.empty? && current_indent > indent result << " " << @lines.shift.strip end register_anchor(anchor, Scalar.new(value: result)) end
#parse_sequence(indent, anchor) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 113
def parse_sequence(indent, anchor) items = [] while @lines.any? line = @lines[0] stripped = line.lstrip break unless line.size - stripped.size == indent && (stripped.start_with?("- ") || stripped == "-") content = @lines.shift.lstrip[1..].strip item_anchor, content = extract_item_anchor(content) item = parse_sequence_item(content, indent) items << register_anchor(item_anchor, item) end register_anchor(anchor, Sequence.new(items: items)) end
#parse_sequence_item(content, indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 128
def parse_sequence_item(content, indent) if content.start_with?("*") parse_inline_alias(content) elsif content.empty? @lines.any? && current_indent > indent ? parse_node(indent) : nil elsif content.start_with?("!ruby/object:") parse_tagged_content(content.strip, indent) elsif content.start_with?("!binary ") parse_binary_value(content, indent) elsif content.start_with?("-") @lines.unshift("#{" " * (indent + 2)}#{content}") parse_node(indent) elsif content =~ MAPPING_KEY_RE && !content.start_with?("!ruby/object:") @lines.unshift("#{" " * (indent + 2)}#{content}") parse_node(indent) elsif content.start_with?("|") Scalar.new(value: parse_block_scalar(indent, content[1..].to_s.strip)) else parse_inline_scalar(content, indent) end end
#parse_tagged_content(tag, indent) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 210
def parse_tagged_content(tag, indent) nested = parse_node(indent) apply_tag(nested, tag, nil) end
#parse_tagged_node(indent, anchor) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 204
def parse_tagged_node(indent, anchor) tag = @lines.shift.strip nested = parse_node(indent) apply_tag(nested, tag, anchor) end
#register_anchor(name, node) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 368
def register_anchor(name, node) if name @anchors[name] = node node.anchor = name if node.respond_to?(:anchor=) end node end
#skip_blank_and_comments (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 376
def skip_blank_and_comments while @lines.any? line = @lines[0] stripped = line.lstrip break unless stripped.empty? || stripped.start_with?("#") @lines.shift end end
#strip_comment(val) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 385
def strip_comment(val) return val unless val.include?("#") return val if val.lstrip.start_with?("#") in_single = false in_double = false escape = false val.each_char.with_index do |ch, i| if escape escape = false next end if in_single in_single = false if ch == "'" elsif in_double if ch == "\\" escape = true elsif ch == '"' in_double = false end else case ch when "'" then in_single = true when '"' then in_double = true when "#" then return val[0...i].rstrip end end end val end
#strip_document_prefix (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 62
def strip_document_prefix return if @lines.empty? return unless @lines[0]&.start_with?("---") if @lines[0].strip == "---" @lines.shift else @lines[0] = @lines[0].sub(/^---\s*/, "") end end