module SexpPath
  
  # The SexpQueryBuilder is the simplest way to build SexpPath queries.
  # 
  # Typically, one access the SexpQueryBuilder through the helper method
  # Q? which accepts a block, and will return a SexpPath matcher.
  #
  # For example here is a SexpPath query that looks for s(:a):
  #
  #   query = Q?{ s(:a) }
  # 
  # A more interesting query might look for classes with names starting
  # in Test:
  #
  #   query = Q?{ s(:class, m(/^Test\w+/ % 'name', _, _)) }
  #
  # This makes use of a SexpPath::Matcher::Pattern, two SexpPath::Matcher::Wild
  # matchers and SexpPath::Traverse#capture_as for capturing the name to a 
  # variable 'name'.
  #
  # For more examples, see the various SexpQueryBuilder class methods, the
  # examples, and the tests supplied with SexpPath.
  #
  class SexpQueryBuilder    
    class << self
      
      # This is the longhand method for create a SexpPath query, normally
      # one would use Q?{ ... }, however it is also possible to do:
      # 
      #   SexpPath::SexpQueryBuilder.do{ s() }
      #
      def do(&block)
        instance_eval(&block)
      end
      
      # Matches an S-Expression.
      #
      # example
      #   s(:a) / Q?{ s(:a) }       #=> [s(:a)]
      #   s(:a) / Q?{ s() }         #=> []
      #   s(:a, s(:b) / Q?{ s(:b) } #=> [s(:b)]
      #
      def s(*args)
        SexpPath::Matcher::Base.new(*args)
      end
      
      # Matches any single item.
      #
      # example:
      #   s(:a) / Q?{ _ }        #=> [s(:a)]
      #   s(:a, s(s(:b))) / Q?{ s(_) } #=> [s(s(:b))]
      #
      # Can also be called with +wild+
      #   s(:a) / Q?{ wild }     #=> [s(:a)]
      #
      def wild()
        SexpPath::Matcher::Wild.new
      end
      alias_method :_, :wild
      
      # Matches all remaining input.
      # This is a special case pattern and has a few odd characteristics.
      # 
      # - You cannot capture with +remaining+ 
      # - If remaining comes before any other matchers, they will be ignored.
      # 
      # example:
      #   s(:a) / Q?{ s(:a, ___ ) }    #=> [s(:a)]
      #   s(:a, :b, :c) / Q?{ s(:a, ___ ) } #=> [s(:a, :b, :c)]
      #
      # Can also be called with +remaining+
      #   s(:a) / Q?{ s(:a, remaining) } #=> [s(:a)]
      #
      def remaining()
        SexpPath::Matcher::Remaining.new
      end
      alias_method :___, :remaining
      
      # Matches an expression or any expression that includes the child.
      #
      # example:
      #   s(:a, :b)   / Q?{ include(:b) }
      #   s(s(s(:a))) / Q?{ include(:a) }
      # 
      def include(child)
        SexpPath::Matcher::Include.new(child)
      end
      
      # Matches any atom.
      #
      # example:
      #   s(:a) / Q?{ s(atom) }        #=> [s(:a)]
      #   s(:a, s(:b)) / Q?{ s(atom) } #=> [s(:b)]
      #
      def atom
        SexpPath::Matcher::Atom.new
      end
    
      # Matches when any of the sub expressions match.
      # 
      # example:
      #   s(:a) / Q?{ any(s(:a), s(:b)) } #=> [s(:a)]
      #   s(:a) / Q?{ any(s(:b), s(:c)) } #=> []
      #
      def any(*args)
        SexpPath::Matcher::Any.new(*args)
      end
      
      # Matches only when all sub expressions match
      #
      # example:
      #   s(:a) / Q?{ all(s(:a), s(:b)) } #=> []
      #   s(:a,:b) / Q?{ t(:a), include(:b)) } #=> [s(:a,:b)]
      #
      def all(*args)
        SexpPath::Matcher::All.new(*args)
      end
      
      # Matches when sub expression does not match, see SexpPath::Matcher::Base#-@
      #
      # example:
      #   s(:a) / Q?{ is_not(s(:b)) } #=> [s(:a)]
      #   s(:a) / Q?{ s(is_not :a) } #=> []
      #
      def is_not(arg)
        SexpPath::Matcher::Not.new(arg)
      end
      
      # Matches anything that has a child matching the sub expression
      # 
      # example:
      #   s(s(s(s(s(:a))))) / Q?{ child(s(:a)) } #=> [s(s(s(s(s(:a)))))]
      #
      def child(child)
        SexpPath::Matcher::Child.new(child)
      end
      
      # Matches anything having the same sexp_type, which is the first value in a Sexp.
      # 
      # example:
      #   s(:a, :b) / Q?{ t(:a) } #=> [s(:a, :b)]
      #   s(:a, :b) / Q?{ t(:b) } #=> []
      #   s(:a, s(:b, :c)) / Q?{ t(:b) } #=> [s(:b, :c)]
      def t(name)
        SexpPath::Matcher::Type.new(name)
      end
      
      # Matches any atom who's string representation matches the patterns passed in.
      # 
      # example:
      #   s(:a) / Q?{ m('a') } #=> [s(:a)]
      #   s(:a) / Q?{ m(/\w/,/\d/) } #=> [s(:a)]
      #   s(:tests, s(s(:test_a), s(:test_b))) / Q?{ m(/test_\w/) } #=> [s(:test_a), s(:test_b)]
      def m(* patterns)
        patterns = patterns.map{|p| p.is_a?(Regexp) ? p : Regexp.new("\\A"+Regexp.escape(p.to_s)+"\\Z")}
        regexp = Regexp.union(*patterns)
        SexpPath::Matcher::Pattern.new(regexp)
      end
    end
  end
end