正規表現からその正規表現にマッチするような例を生成する

本当にやりたいことは、

型から、その型を持つようなRubyプログラムを生成する

なんだけど(というかこれはHaskell界隈の人が喜んでやってるやつだよな、つまんね)、とりあえず正規表現で考えてみてた。

正規表現は有限状態オートマトンになるわけで、ループがあるとやだなぁ、とか考えてたんだけど、研究室の後輩(オートマトン屋さん)に相談してみたら、少なくとも正規表現に限定してしまえば簡単になると言われた。確かに、よく考えてみれば、ループがある場合は*とかで明示されてる。そういうわけで30分くらいで作ったプログラム。

実行例:

$ ruby regen.rb
/red|blue|green|(#[01234567890ABCDEF][01234567890ABCDEF][01234567890ABCDEF][01234567890ABCDEF][01234567890ABCDEF][01234567890ABCDEF]?)/
OK : #9405D9
OK : #5D06FC
OK : red
OK : #298
OK : #D0347B
OK : blue
OK : red
OK : green
OK : blue
OK : blue

JavaScriptで書いておいて、簡単なパーサも書けば、Webで試してもらえて楽しいと思う。気が向いたらやるかも。

module RE
  class Literal
    def initialize(c)
      @char = c
    end
    
    def example()
      @char
    end

    def to_re()
      @char
    end
  end
  
  class Group
    def initialize(res)
      @res = res
    end
    
    def example()
      @res.collect {|re| re.example() }.join('')
    end

    def to_re()
      "(" + @res.collect{|re| re.to_re}.join('') + ")"
    end
  end

  class Alter
    def initialize(res)
      @res = res
    end
    
    def example()
      @res[rand(@res.length)].example()
    end

    def to_re()
      @res.collect {|re| re.to_re}.join('|')
    end
  end

  class Star
    def initialize(re)
      @re = re
    end
    
    def example()
      acc = ""
      rand(10).times {
        acc += @re.example()
      }
      acc
    end

    def to_re()
      @re.to_re + "*"
    end
  end

  class Set
    def initialize(set)
      @set = set
    end
    
    def example()
      @set[rand(@set.length)]
    end
    
    def to_re()
      "[#{@set.join('')}]"
    end
  end

  class Sequence
    def initialize(seq)
      @seq = seq
    end
    
    def example()
      @seq.collect{|re| re.example }.join('')
    end
    
    def to_re()
      @seq.collect{|re| re.to_re}.join('')
    end
  end
  
  class Question
    def initialize(re)
      @re = re
    end
    
    def example()
      rand(2) == 0 ? @re.example : ""
    end

    def to_re()
      @re.to_re+"?"
    end
  end
end


include RE

a = /#[0123456789ABCDEF][0123456789ABCDEF][0123456789ABCDEF]([0123456789ABCDEF][0123456789ABCDEF][0123456789ABCDEF])?|red|blue|green/

b = %w(0 1 2 3 4 5 6 7 8 9 0 A B C D E F)
d = Set.new(b)
fff = Group.new([Literal.new("#"), d, d, d, Question.new(Sequence.new([d, d, d]))])
lit = %w(red blue green).collect{|l| Literal.new(l) }
lit << fff

re = Alter.new(lit)

puts "/#{re.to_re}/"
10.times {
  s = re.example
  puts("#{a=~s ? 'OK':'NG'} : #{s}")
  
}