# strsearch: a class to parse a search string with search terms
# ANDed, NOTed, ORed and XORed together in RPN notation
# + and
# - or
# ! not
# ^ xor
# ' ' break
# starting a term with / makes it a regexp
# ~ toggles case sensitivity, starts out case insensitive

class StrSearch
  attr_accessor :tree

  def initialize str
    stack = []
    cur = ""
    escape = false
    regexp = false
    cs = false
    str.each_byte do |b|
      if escape
        cur << b.chr
        escape = false
      else
        r = case b
          when ?\\: escape = true
          when ?+: :and
          when ?-: :or
          when ?^: :xor
          when ?!: :not
          when ?~: cs = !cs
          when ?\ : :break
          else cur << b.chr
        end
        if r.kind_of? Symbol
          reg = cs ? (/#{cur}/) : (/#{cur}/i)
          stack << reg unless cur == ""
          cur = ""
          if [:and, :or, :not, :xor].include? r
            tmp = [r, stack.pop]
            tmp << stack.pop unless r == :not
            raise "StackError" if tmp.include? nil
            stack << tmp
          end
        end
      end
    end

    r = cs ? (/#{cur}/) : (/#{cur}/i)
    stack << r unless cur == ""

    while stack.length > 1
      tmp = [:and, stack.pop, stack.pop]
      raise "StackError" if tmp.include? nil
      stack << tmp
    end
    @tree = stack.first
  end
  def search str, t=tree
    case t
      when Array
        case t.first
          when :and: t[1,2].all? {|s| search str, s}
          when :or: t[1,2].any? {|s| search str, s}
          when :xor: !!search(str, t[1]) ^ search(str, t[2])
          when :not: !search(str, t[1])
        end
      else str[t]
    end
  end

  def to_search
    self
  end
end

class String
  def to_search
    StrSearch.new self
  end

  def search str
    str.to_search.search self
  end
end
