Class: AtCoderFriends::FormatParser

Inherits:
Object
  • Object
show all
Defined in:
lib/at_coder_friends/format_parser.rb

Overview

parses input data format and generates input definitons

Constant Summary collapse

PARSERS =
[
  {
    container: :harray,
    item: :number,
    pat: /^(?<v>[a-z]+)[01](\s+\k<v>.)*(\s+\.+)?(\s+\k<v>.)+$/i,
    names: ->(m) { [m[:v]] },
    pat2: ->(_) { nil },
    size: ->(f) { [f[-1]] }
  },
  {
    container: :harray,
    item: :char,
    pat: /^(?<v>[a-z]+)[01](\k<v>.)*(\s*\.+\s*)?(\k<v>.)+$/i,
    names: ->(m) { [m[:v]] },
    pat2: ->(_) { nil },
    size: ->(f) { [f[-1]] }
  },
  {
    container: :matrix,
    item: :number,
    pat: /^(?<v>[a-z]+)[01][01](\s+\k<v>..)*(\s+\.+)?(\s+\k<v>..)+$/i,
    names: ->(m) { [m[:v]] },
    pat2: ->(v) { /(^#{v}..(\s+#{v}..)*(\s+\.+)?(\s+#{v}..)+|\.+)$/ },
    size: ->(f) { f[-2..-1].chars.to_a }
  },
  {
    container: :matrix,
    item: :char,
    pat: /^(?<v>[a-z]+)[01][01](\k<v>..)*(\s*\.+\s*)?(\k<v>..)+$/i,
    names: ->(m) { [m[:v]] },
    pat2: ->(v) { /(^#{v}..(#{v}..)*(\s*\.+\s*)?(#{v}..)+|\.+)$/ },
    size: ->(f) { f[-2..-1].chars.to_a }
  },
  {
    container: :varray,
    item: :number,
    pat: /^[a-z]+(?<i>[0-9])(\s+[a-z]+\k<i>)*$/i,
    names: ->(m) { m[0].split.map { |w| w[0..-2] } },
    pat2: lambda { |vs|
      pat = vs.map { |v| v + '.+' }.join('\s+')
      /^(#{pat}|\.+)$/
    },
    size: ->(f) { /(?<sz>\d+)$/ =~ f ? [sz] : [f[-1]] }
  },
  {
    container: :single,
    item: :number,
    pat: /^[a-z]+(\s+[a-z]+)*$/i,
    names: ->(m) { m[0].split },
    pat2: ->(_) { nil },
    size: ->(_) { [] }
  }
].freeze

Instance Method Summary collapse

Instance Method Details

#match_smp!(inpdefs, smp) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/at_coder_friends/format_parser.rb', line 139

def match_smp!(inpdefs, smp)
  lines = smp.split("\n")
  inpdefs.each_with_index do |inpdef, i|
    break if i > lines.size
    next if inpdef.item != :number

    inpdef.item = :string if lines[i].split[0] =~ /[^\-0-9]/
    break if %i[varray matrix].include?(inpdef.container)
  end
  inpdefs
end

#max_smp(smps) ⇒ Object

rubocop:enable Metrics/MethodLength, Metrics/AbcSize



132
133
134
135
136
137
# File 'lib/at_coder_friends/format_parser.rb', line 132

def max_smp(smps)
  smps
    .select { |smp| smp.ext == :in }
    .max_by { |smp| smp.txt.size }
    &.txt
end

#parse(fmt, smps) ⇒ Object



83
84
85
86
87
88
89
90
# File 'lib/at_coder_friends/format_parser.rb', line 83

def parse(fmt, smps)
  lines = split_trim(fmt)
  defs = parse_fmt(lines)
  smpx = max_smp(smps)
  return defs unless smpx

  match_smp!(defs, smpx)
end

#parse_fmt(lines) ⇒ Object

rubocop:disable Metrics/MethodLength, Metrics/AbcSize



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/at_coder_friends/format_parser.rb', line 106

def parse_fmt(lines)
  it = Iterator.new(lines + ['']) # sentinel
  prv = nil
  cur = it.next
  Enumerator.new do |y|
    loop do
      unless (parser = PARSERS.find { |ps| ps[:pat] =~ cur })
        puts "unknown format: #{cur}" unless cur.empty?
        (cur = it.next) ? next : break
      end
      container, item = parser.values_at(:container, :item)
      m = parser[:pat].match(cur)
      names = parser[:names].call(m)
      pat2 = parser[:pat2].call(names)
      loop do
        prv = cur
        cur = it.next
        break unless pat2 && pat2 =~ cur
      end
      size = parser[:size].call(prv)
      y << InputDef.new(container, item, names, size)
    end
  end.to_a
end

#process(pbm) ⇒ Object



78
79
80
81
# File 'lib/at_coder_friends/format_parser.rb', line 78

def process(pbm)
  defs = parse(pbm.fmt, pbm.smps)
  pbm.defs = defs
end

#split_trim(fmt) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/at_coder_friends/format_parser.rb', line 92

def split_trim(fmt)
  fmt
    .gsub(/[+-]1/, '') # N-1, N+1 -> N
    .gsub(%r{[-/ ]}, ' ') # a-b, a/b -> a b
    .gsub(/\{.*?\}/) { |w| w.delete(' ') } # {1, 1} -> {1,1} shortest match
    .gsub(/[_,'\\(){}]/, '')
    .gsub(/[::…‥]+/, '..')
    .gsub(/ldots/, '..')
    .gsub(/^[.\s]+$/, '..')
    .split("\n")
    .map(&:strip)
end