/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of SourcePawn.
 *
 * SourcePawn is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 * 
 * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * SourcePawn. If not, see http://www.gnu.org/licenses/.
 */
#include "AST.h"
#include "ConstantEvaluator.h"
#include "../Heap-inl.h"
#include "../Handles-inl.h"

using namespace ke;

static inline bool
EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right,
                        BoxedPrimitive *out)
{
    switch (kind) {
      case TOK_PLUS:
        *out = BoxedPrimitive::Int(left.toInt() + right.toInt());
        return true;

      case TOK_SHL:
        *out = BoxedPrimitive::Int(left.toInt() << right.toInt());
        return true;

      default:
        return false;
    }
}

static inline bool
EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out)
{
  switch (kind) {
    case TOK_MINUS:
      *out = BoxedPrimitive::Int(-inner.toInt());
      return true;

    default:
      return false;
  }
}

bool
ke::EvaluateForConstant(Expression *expr, BoxedPrimitive *out)
{
    switch (expr->kind()) {
      case AstNode::kBinaryExpression:
      {
        BoxedPrimitive left;
        BoxedPrimitive right;
        BinaryExpression *b = expr->toBinaryExpression();
        if (!EvaluateForConstant(b->left(), &left) ||
            !EvaluateForConstant(b->right(), &right))
        {
            return false;
        }
        return EvaluateBinaryOperation(b->token(), left, right, out);
      }

      case AstNode::kUnaryExpression:
      {
        BoxedPrimitive inner;
        UnaryExpression *u = expr->toUnaryExpression();
        if (!EvaluateForConstant(u->expression(), &inner))
            return false;
        return EvaluateUnaryOperation(u->token(), inner, out);
      }

      case AstNode::kBooleanLiteral:
        *out = BoxedPrimitive::Int(expr->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0);
        return true;

      case AstNode::kIntegerLiteral:
        *out = BoxedPrimitive::Int(expr->toIntegerLiteral()->value());
        return true;

      default:
        return false;
    }
}
