class Asciidoctor::Parser

def self.parse_colspecs records

and layout the cells in the table.
returns a Hash of attributes that specify how to format

Every column spec is guaranteed to have a width

the column.
column, and/or default styles or filters applied to the cells in
width of columns, default alignments for cells in each
The column specs dictate the number of columns, relative

Internal: Parse the column specs for this table.
def self.parse_colspecs records
  records = records.delete ' ' if records.include? ' '
  # check for deprecated syntax: single number, equal column spread
  if records == records.to_i.to_s
    return ::Array.new(records.to_i) { { 'width' => 1 } }
  end
  specs = []
  # NOTE -1 argument ensures we don't drop empty records
  ((records.include? ',') ? (records.split ',', -1) : (records.split ';', -1)).each do |record|
    if record.empty?
      specs << { 'width' => 1 }
    # TODO might want to use scan rather than this mega-regexp
    elsif (m = ColumnSpecRx.match(record))
      spec = {}
      if m[2]
        # make this an operation
        colspec, rowspec = m[2].split '.'
        if !colspec.nil_or_empty? && TableCellHorzAlignments.key?(colspec)
          spec['halign'] = TableCellHorzAlignments[colspec]
        end
        if !rowspec.nil_or_empty? && TableCellVertAlignments.key?(rowspec)
          spec['valign'] = TableCellVertAlignments[rowspec]
        end
      end
      if (width = m[3])
        # to_i will strip the optional %
        spec['width'] = width == '~' ? -1 : width.to_i
      else
        spec['width'] = 1
      end
      # make this an operation
      if m[4] && TableCellStyles.key?(m[4])
        spec['style'] = TableCellStyles[m[4]]
      end
      if m[1]
        1.upto(m[1].to_i) { specs << spec.merge }
      else
        specs << spec
      end
    end
  end
  specs
end