# The <tt>SQLTree::Token</tt> class is the base class for every token
# in the SQL language. Actual tokens are represented by a subclass.
#
# Tokens are produced by the <tt>SQLTree::Tokenizer</tt> from a string
# and are consumed by the <tt>SQLTree::Parser</tt> to construct an
# abstract syntax tree for the query that is being parsed.
class SQLTree::Token

  # For some tokens, the encountered literal value is important
  # during the parsing phase (e.g. strings and variable names).
  # Therefore, the literal value encountered that represented the
  # token in the original SQL query string is stored.
  attr_accessor :literal

  # Creates a token instance with a given literal representation.
  #
  # <tt>literal<tt>:: The literal string value that was encountered
  #                   while tokenizing.
  def initialize(literal)
    @literal = literal
  end

  # Compares two tokens. Tokens are considered equal when they are
  # instances of the same class, i.e. do literal is not used.
  def ==(other)
    other.class == self.class
  end

  def inspect # :nodoc:
    literal
  end

  def not?
    SQLTree::Token::NOT === self
  end

  def optional_not_prefix?
    [SQLTree::Token::LIKE, SQLTree::Token::ILIKE, SQLTree::Token::IN].include?(self.class)
  end
  
  def optional_not_suffix?
    [SQLTree::Token::IS].include?(self.class)
  end
  
  # Returns true if this is a JOIN token?
  def join?
    [SQLTree::Token::JOIN, SQLTree::Token::LEFT, SQLTree::Token::RIGHT,
      SQLTree::Token::INNER, SQLTree::Token::OUTER, SQLTree::Token::NATURAL,
      SQLTree::Token::FULL].include?(self.class)
  end
  
  def prefix_operator?
    SQLTree::Node::Expression::PrefixOperator::TOKENS.include?(self.class)
  end
  
  def variable?
    self.kind_of?(SQLTree::Token::Identifier)
  end
  
  def value?
    self.kind_of?(SQLTree::Token::LiteralValue)
  end

  # Returns true if this is an order direction token.
  def direction?
    [SQLTree::Token::ASC, SQLTree::Token::DESC].include?(self.class)
  end

  ###################################################################
  # DYNAMIC TOKEN TYPES
  ###################################################################

  # The <tt>SQLTree::Token::DynamicToken</tt> class is the base class for
  # every dynamic token. A dynamic token is a token for which the
  # literal value used remains impoirtant during parsing.
  class DynamicToken < SQLTree::Token

    def inspect # :nodoc:
      "#<#{self.class.name.split('::').last}:#{literal.inspect}>"
    end

    # Compares two tokens. For values, the literal encountered value
    # of the token is also taken into account besides the class.
    def ==(other)
      other.class == self.class && @literal == other.literal
    end
  end
  
  # The <tt>SQLTree::Token::Identifier</tt> class represents SQL
  # variables. The variable name is stored in the literal as string,
  # without quotes if they were present.
  class Identifier < DynamicToken
  end
  
  # The <tt>SQLTree::Token::LiteralVakue</tt> class represents literal values
  # like strings and numbers that occur in SQL.
  class LiteralValue < DynamicToken
  end

  # The <tt>SQLTree::Token::String</tt> class represents strings.
  # The actual string is stored in the literal as string without quotes.
  class String < LiteralValue
  end

  # The <tt>SQLTree::Token::Keyword</tt> class represents numbers.
  # The actual number is stored as an integer or float in the token's
  # literal.
  class Number < LiteralValue
  end

  ###################################################################
  # STATIC TOKEN TYPES
  ###################################################################

  # The <tt>SQLTree::Token::Keyword</tt> class represents reserved SQL
  # keywords. These keywords are used to structure the query. Keywords
  # are static, i.e. the literal value is not important during the
  # parsing process.
  class Keyword < SQLTree::Token
    def inspect # :nodoc:
      ":#{literal.gsub(/ /, '_').downcase}"
    end
  end

  # The <tt>SQLTree::Token::Operator</tt> class represents logical and
  # arithmetic operators in SQL. These tokens are static, i.e. the literal
  # value is not important during the parsing process.
  class Operator < SQLTree::Token
    def inspect # :nodoc:
      OPERATORS_HASH[literal].inspect
    end
  end

  ###################################################################
  # STATIC TOKEN CONSTANTS
  ###################################################################

  # Create some static token classes and a single instance of them
  LPAREN = Class.new(SQLTree::Token).new('(')
  RPAREN = Class.new(SQLTree::Token).new(')')
  DOT    = Class.new(SQLTree::Token).new('.')
  COMMA  = Class.new(SQLTree::Token).new(',')
  STRING_ESCAPE = Class.new(SQLTree::Token).new('E')

  # A list of all the SQL reserverd keywords.
  KEYWORDS = %w{SELECT FROM WHERE GROUP HAVING ORDER DISTINCT LEFT RIGHT INNER FULL OUTER NATURAL JOIN USING
                AND OR NOT AS ON IS NULL BY LIKE ILIKE BETWEEN IN ASC DESC INSERT INTO VALUES DELETE UPDATE
                SET BEGIN COMMIT ROLLBACK TO INTERVAL COUNT}

  # Create a token for all the reserved keywords in SQL
  KEYWORDS.each { |kw| const_set(kw, Class.new(SQLTree::Token::Keyword)) }

  OPERATORS_HASH = { '+' => :plus, '-' => :minus, '*' => :multiply, 
      '/' => :divide, '%' => :modulo, '||' => :concat, '|' => :binary_or, 
      '&' => :binary_and, '<<' => :lshift, '>>' => :rshift, '=' => :eq, 
      '!=' => :ne, '<>' => :ne, '>' => :gt, '<' => :lt, '>=' => :gte, 
      '<=' => :lte, '==' => :eq }

  # Register a token class and single instance for every operator.
  OPERATORS_HASH.each_pair do |literal, symbol|
    self.const_set(symbol.to_s.upcase, Class.new(SQLTree::Token::Operator)) unless self.const_defined?(symbol.to_s.upcase)
  end
end