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/^((?:[^#:]|:[^ ])+):(?:[ ]+(.*))?$/
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
- #extract_item_anchor(content) private
- #parse_alias_ref 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 34
def initialize(source) @lines = source.split("\n") @anchors = {} 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 254
def coerce(val) val = val.sub(/^! /, "") if val.start_with?("! ") if val =~ /^"(.*)"$/ $1.gsub(/\\"/, '"').gsub(/\\n/, "\n").gsub(/\\r/, "\r").gsub(/\\t/, "\t").gsub(/\\\\/, "\\") elsif val =~ /^'(.*)'$/ $1.gsub(/''/, "'") elsif val == "true" true elsif val == "false" false elsif val == "nil" 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 305
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 322
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 300
def current_indent line = @lines[0] line.size - line.lstrip.size end
#extract_item_anchor(content) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 315
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 40
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_block_scalar(base_indent, modifier) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 210
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 243
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 139
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) 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 159
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.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 71
def parse_node(base_indent) 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 end
#parse_plain_scalar(indent, anchor) (private)
[ GitHub ]# File 'lib/rubygems/yaml_serializer.rb', line 232
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 104
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 119
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?("-") @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 195
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 189
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 329
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 337
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 346
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 60
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