class Asciidoctor::AttributeList


=> {‘style’ => ‘quote’, ‘attribution’ => ‘Famous Person’, ‘citetitle’ => ‘Famous Book (2001)’}
attrlist.parse([‘style’, ‘attribution’, ‘citetitle’])
attrlist = Asciidoctor::AttributeList.new(‘quote, Famous Person, Famous Book (2001)’)
=> {‘style’ => ‘astyle’}
)
=> {0 => ‘astyle’}
attrlist.parse
attrlist = Asciidoctor::AttributeList.new(‘astyle’)
Examples
given a posattrs array either during parsing or after the fact.
to a 1-based positional key, The positional attributes can be “rekeyed” when
may be used around the value. If a key is not detected, the value is assigned
pairs. By default, attributes must each be separated by a comma and quotes
Public: Handles parsing AsciiDoc attribute lists into a Hash of key/value

def self.rekey(attributes, pos_attrs)

def self.rekey(attributes, pos_attrs)
  pos_attrs.each_with_index do |key, index|
    next if key.nil?
    pos = index + 1
    unless (val = attributes[pos]).nil?
      attributes[key] = val
      #QUESTION should we delete the positional key?
      #attributes.delete pos
    end
  end
  attributes
end

def initialize(source, block = nil, quotes = ['\'', '"'], delimiter = ',', escape_char = '\\')

def initialize(source, block = nil, quotes = ['\'', '"'], delimiter = ',', escape_char = '\\')
  @scanner = ::StringScanner.new source
  @block = block
  @quotes = quotes
  @escape_char = escape_char
  @delimiter = delimiter
  @attributes = nil
end

def parse(posattrs = [])

def parse(posattrs = [])
  return @attributes unless @attributes.nil?
  @attributes = {}
  # not sure if I want this assignment or not
  #attributes[0] = @scanner.string
  index = 0
  while parse_attribute(index, posattrs)
    break if @scanner.eos?
    skip_delimiter
    index += 1
  end
  @attributes
end

def parse_attribute(index = 0, pos_attrs = [])

def parse_attribute(index = 0, pos_attrs = [])
  single_quoted_value = false
  skip_blank
  first = @scanner.peek(1)
  # example: "quote" || 'quote'
  if @quotes.include? first
    value = nil
    name = parse_attribute_value @scanner.get_byte
    if first == '\''
      single_quoted_value = true
    end
  else
    name = scan_name
    skipped = 0
    c = nil
    if @scanner.eos?
      if name.nil?
        return false
      end
    else
      skipped = skip_blank || 0
      c = @scanner.get_byte
    end
    # example: quote
    if c.nil? || c == @delimiter
      value = nil
    # example: Sherlock Holmes || =foo=
    elsif c != '=' || name.nil?
      remainder = scan_to_delimiter
      name = '' if name.nil?
      name += ' ' * skipped + c
      name += remainder unless remainder.nil?
      value = nil
    else
      skip_blank
      # example: foo=,
      if @scanner.peek(1) == @delimiter
        value = nil
      else
        c = @scanner.get_byte
        # example: foo="bar" || foo='bar' || foo="ba\"zaar" || foo='ba\'zaar' || foo='ba"zaar' (all spaces ignored)
        if @quotes.include? c
          value = parse_attribute_value c
          if c == '\''
            single_quoted_value = true
          end
        # example: foo=bar (all spaces ignored)
        elsif !c.nil?
          value = c + scan_to_delimiter
        end
      end
    end
  end
  if value.nil?
    resolved_name = single_quoted_value && !@block.nil? ? @block.apply_normal_subs(name) : name
    if !(pos_name = pos_attrs[index]).nil?
      @attributes[pos_name] = resolved_name
    else
      #@attributes[index + 1] = resolved_name
    end
    # not sure if we want to always assign the positional key
    @attributes[index + 1] = resolved_name
    # not sure if I want this assignment or not
    #@attributes[resolved_name] = nil
  else
    resolved_value = value
    # example: options="opt1,opt2,opt3"
    # opts is an alias for options
    if name == 'options' || name == 'opts'
      name = 'options'
      resolved_value.split(',').each do |o|
        @attributes[o.strip + '-option'] = ''
      end
    elsif single_quoted_value && !@block.nil?
      resolved_value = @block.apply_normal_subs(value)
    end
    @attributes[name] = resolved_value
  end
  true
end

def parse_attribute_value(quote)

def parse_attribute_value(quote)
  # empty quoted value
  if @scanner.peek(1) == quote
    @scanner.get_byte 
    return ''
  end
  value = scan_to_quote quote
  if value.nil?
    quote + scan_to_delimiter
  else
    @scanner.get_byte
    value.gsub(UNESCAPE_PATTERNS[@escape_char + quote], quote)
  end
end

def parse_into(attributes, posattrs = [])

def parse_into(attributes, posattrs = [])
  attributes.update(parse(posattrs))
end

def rekey(posattrs)

def rekey(posattrs)
  AttributeList.rekey(@attributes, posattrs)
end

def scan_name

def scan_name
  @scanner.scan NAME_PATTERN
end

def scan_to_delimiter

def scan_to_delimiter
  @scanner.scan BOUNDARY_PATTERNS[@delimiter]
end

def scan_to_quote(quote)

def scan_to_quote(quote)
  @scanner.scan BOUNDARY_PATTERNS[quote]
end

def skip_blank

def skip_blank
  @scanner.skip SKIP_PATTERNS[:blank]
end

def skip_delimiter

def skip_delimiter
  @scanner.skip SKIP_PATTERNS[@delimiter]
end