Module: AtCoderFriends::Parser::FormatParser

Defined in:
lib/at_coder_friends/parser/format_parser.rb

Overview

parses input data format and generates input definitons

Defined Under Namespace

Classes: Iterator

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

Class Method Summary collapse

Class Method Details

.match_smp!(inpdefs, smp) ⇒ Object



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

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



134
135
136
137
138
139
# File 'lib/at_coder_friends/parser/format_parser.rb', line 134

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

.normalize(fmt) ⇒ Object



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

def normalize(fmt)
  fmt
    .gsub(/[+*-]\d+/, '') # 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(/[clv]?dots/, '..')
    .gsub(/^[.\s]+$/, '..')
    .split("\n")
    .map(&:strip)
end

.parse(fmt, smps) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/at_coder_friends/parser/format_parser.rb', line 86

def parse(fmt, smps)
  lines = normalize(fmt)
  defs = parse_fmt(lines)
  smpx = max_smp(smps)
  smpx && match_smp!(defs, smpx)
  defs
end

.parse_fmt(lines) ⇒ Object

rubocop:disable Metrics/MethodLength, Metrics/AbcSize



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

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



81
82
83
84
# File 'lib/at_coder_friends/parser/format_parser.rb', line 81

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