# $Id: queryparser.y 269 2003-11-07 15:54:17Z bolzer $
# Author:: Oliver M. Bolzer (mailto:oliver@fakeroot.net)
# Copyright:: (c) Oliver M. Bolzer, 2003
# Licence:: Ruby licence.
#
# parse a VAPOR Query into Vapor::QueryStatement
# 

class Vapor::QueryParser #:nodoc:

  prechigh
   left BOOL
  preclow

rule

 statement  :  '(' statement ')'   { result = val[1] }
            |   IDENT COMP PLACEHOLDER  { result = BasicQueryStatement.new( val[0], val[1], @arguments[@ph_count] ); @ph_count += 1 }
            |   statement BOOL statement { result =  ComplexQueryStatement.new( val[0], val[1], val[2] ) } 


end
---- header ----
#:nodoc:
#
# queryparser.rb generated from $Id: queryparser.y 269 2003-11-07 15:54:17Z bolzer $
# with "racc -o query.rb query.y" 
#
# Author:: Oliver M. Bolzer (mailto:oliver@fakeroot.net)
# Copyright:: (c) Oliver M. Bolzer, 2003
# Licence:: Ruby licence
#
# Parse a Vapor Query into a Vapor::QueryStatement

require 'vapor/utils'

---- inner ----
  include Exceptions

  def parse( query, arguments )
    raise TypeError unless query.is_a? String
    raise TypeError unless arguments.is_a? Array
    
    @tokens = []
    @arguments = arguments
    @ph_count = 0

    # break up into tokens
    query.strip!
    query.scan(/\w+|=|~|\?|\(|\)/).each{ |token|
      case token
      when /\AAND/
        @tokens.push [:BOOL, $&]
      when /\A\w+/
         @tokens.push [:IDENT, $& ]
      when /\A\?/
         @tokens.push [:PLACEHOLDER, $& ]
      when /\A(=|~)/
         @tokens.push [:COMP, $& ]
      else
         @tokens.push [ $&, $& ]
      end
      
    }
    
    # check that number of placeholders match number of arguments
    num_ph = @tokens.select{|t| t[0] == :PLACEHOLDER }.size
    if num_ph > arguments.size then
      raise InvalidQueryError, "too few arguments to query"
    end

    # replace all Persistables with their OID
    arguments.map!{|arg|
      if arg.kind_of? Persistable then
        arg.oid
      else
        arg
      end
    }

    begin
      do_parse
    rescue Racc::ParseError => e
      raise InvalidQueryError, e.message
    end
  end

  private
  def next_token
    @tokens.shift
  end

