構文解析(4)

意外と引っ張るなこのネタ.

http://ja.doukaku.org/51/に挑戦してみる.

最初はparseだけでなんとかしようとして,気が狂いそうになったが,こっそりrubyの回答を見て,scanがあればいいのだと知る.

module Parse

  def scan( str, target=:target)
    r = []
    pos = 0
    while pos<str.size
      m_type, m_len, result = _parse( target, pos, 0, str)
      if m_type
        # 常にrに集めさせて、検索終了後にr.each{|x| yield(x)}のほうがいい?
        # それだと、yield内でパーサの内部情報にアクセスできないので、とりあえず現状維持
        if block_given?
          yield(result)
        else
          r << result
        end
      end
      pos += (m_len<=0)? 1: m_len
    end
    return (block_given?)? str: r
  end

end
include Parse

str =
  "aaa abc-hidden.png>hoge-big.jpeg"  \
  "---foo-hidden-small.gif|^_^a.bmp"  \
  "--hiddena-hoge.png<=not hidden~~"  \
  "--small.jpg<=not small(^_^)"   \
  "normal-small-big.hoge"

sel :str_p => /\A[A-Za-z]+/
#rep(:str_p => ['A'..'Z','a'..'z']) {|v| v.join}
seq :body => [:str_p, opt('-hidden'), opt('-small','-big'), '.', :str_p]

scan(str, :body) {|n,h,s,period,e|
  s = (s.empty?)? 'normal': s[0][1..-1]
  h = (!h.empty?)? 'True': 'False'
  puts "name:'#{n}', ext:'#{e}', size: #{s}, hidden: #{h}"
}


規則Aを満たす文字列が出るまでスキップするfind(A)を作れば,parse(rep0(find(A)))でscan(A)と等価な処理が,他と統一的な方法で記述できる.

そうすれば.find相当(scanがfind_allとして)も簡単に書けるな.