/* 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/.
 */
#ifndef _include_sp2_symbol_h_
#define _include_sp2_symbol_h_

#include "../PoolAllocator.h"
#include "../Handles.h"
#include "../Opcodes.h"
#include "../Types.h"
#include "../Strings.h"

namespace ke {

class String;

#define SYMBOL_KINDS(_) \
    /* Any kind of variable or argument produces a VariableSymbol. */ \
    _(Variable)         \
    /* A function declaration produces a FunctionSymbol. */ \
    _(Function)         \
    /* A named constant produces a ConstantSymbol. */ \
    _(Constant)         \
    /* A named type (class struct, typedef, etc) produces a TypeSymbol. */ \
    _(Type)             \
    /* // A module import produces a ModuleSymbol. */   \
    _(Module)

#define _(name)     class name##Symbol;
SYMBOL_KINDS(_)
#undef _

class Scope;

// A symbol represents the declaration of a named entity.
class Symbol : public PoolObject
{
  public:
    enum Kind {
#       define _(name) k##name,
        SYMBOL_KINDS(_)
#       undef _
        kTotalSymbolKinds
    };

  public:
    Symbol(Scope *scope, Handle<String> name, const SourcePosition &pos)
      : scope_(scope),
        name_(name),
        pos_(pos)
    {
    }

    virtual Kind kind() const = 0;

    Handle<String> name() const {
        return name_;
    }
    const SourcePosition &pos() const {
        return pos_;
    }
    Handle<Type> type() const {
        return type_;
    }
    Scope *scope() const {
        return scope_;
    }

  public:
#define _(name)                                             \
    bool is##name() const {                                 \
        return (kind() == k##name);                         \
    }                                                       \
    name##Symbol *as##name() {                              \
        if (is##name())                                     \
            return to##name();                              \
        return NULL;                                        \
    }                                                       \
    name##Symbol *to##name() {                              \
        assert(is##name());                                 \
        return reinterpret_cast<name##Symbol *>(this);      \
    }
    SYMBOL_KINDS(_)
#undef _

  private:
    Scope *scope_;
    ScopedRoot<String> name_;
    SourcePosition pos_;

  protected:
    ScopedRoot<Type> type_;
};

class VariableSymbol : public Symbol
{
  public:
    enum Storage {
        Unknown,
        Local,
        Heap
    };

  public:
    VariableSymbol(Scope *scope, Handle<String> name, const SourcePosition &pos)
      : Symbol(scope, name, pos),
        storage_(Unknown)
    {
    }

    VariableSymbol(Scope *scope, Handle<String> name, const SourcePosition &pos, Handle<Type> type)
      : Symbol(scope, name, pos),
        storage_(Unknown)
    {
        type_ = type;
    }

    Kind kind() const {
        return kVariable;
    }
    void setType(Type *type) {
        assert(!type_);
        type_ = type;
    }
    void allocate(Storage storage, unsigned slot) {
        storage_ = storage;
        slot_ = slot;
    }
    Storage storage() const {
        return storage_;
    }
    unsigned slot() const {
        assert(storage() != Unknown);
        return slot_;
    }

  private:
    Storage storage_;
    unsigned slot_;
};

class TypeSymbol : public Symbol
{
  public:
    TypeSymbol(Scope *scope, Handle<String> name, Handle<Type> type)
      : Symbol(scope, name, SourcePosition())
    {
        type_ = type;
    }

    Kind kind() const {
        return kType;
    }
};

class FunctionSymbol : public Symbol
{
  public:
    FunctionSymbol(Scope *scope, Handle<String> name, const SourcePosition &pos, Handle<FunctionType> type)
      : Symbol(scope, name, pos)
    {
        type_ = type;
    }

    Kind kind() const {
        return kFunction;
    }
    FunctionType *type() const {
        return FunctionType::cast(type_);
    }
};

}

#endif // _include_sp2_symbol_h_
