// $Id: init.cpp,v 1.5 1999/02/01 17:43:06 shields Exp $
copyright notice

#include "config.h"
#include "semantic.h"
#include "control.h"

void Semantic::ProcessVariableInitializer(AstVariableDeclarator *variable_declarator)
{
    if (! variable_declarator -> variable_initializer_opt)
        return;

    VariableSymbol *symbol = variable_declarator -> symbol;

    AstArrayInitializer *array_initializer = variable_declarator -> variable_initializer_opt -> ArrayInitializerCast();
    if (array_initializer)
    {
        //
        // This operation may throw OutOfMemoryError
        //
        SymbolSet *exception_set = TryExceptionTableStack().Top();
        if (exception_set)
        {
            exception_set -> AddElement(control.RuntimeException());
            exception_set -> AddElement(control.Error());
        }
        
        ProcessArrayInitializer(array_initializer, symbol -> Type());
    }
    else
    {
        AstExpression *init = (AstExpression *) variable_declarator -> variable_initializer_opt;
        ProcessExpressionOrStringConstant(init);

        if (symbol -> Type() != init -> Type() && init -> Type() != control.no_type)
        {
            if (CanAssignmentConvert(symbol -> Type(), init))
            {
                init = ConvertToType(init, symbol -> Type());
                variable_declarator -> variable_initializer_opt = init;
            }
            else if (init -> IsConstant() && control.IsSimpleIntegerValueType(init -> Type())
                                          && control.IsSimpleIntegerValueType(symbol -> Type()))
            {
                if (symbol -> Type() == control.byte_type)
                     ReportSemError(SemanticError::INVALID_BYTE_VALUE,
                                    init -> LeftToken(),
                                    init -> RightToken());
                else if (symbol -> Type() == control.char_type)
                     ReportSemError(SemanticError::INVALID_CHARACTER_VALUE,
                                    init -> LeftToken(),
                                    init -> RightToken());
                else ReportSemError(SemanticError::INVALID_SHORT_VALUE,
                                    init -> LeftToken(),
                                    init -> RightToken());
            }
            else
            {
                ReportSemError(SemanticError::INCOMPATIBLE_TYPE_FOR_ASSIGNMENT,
                               variable_declarator -> LeftToken(),
                               init -> RightToken(),
                               symbol -> Type() -> ContainingPackage() -> PackageName(),
                               symbol -> Type() -> ExternalName(),
                               init -> Type() -> ContainingPackage() -> PackageName(),
                               init -> Type() -> ExternalName());
            }
        }

        if (symbol -> ACC_FINAL() && init -> IsConstant())
            symbol -> initial_value = init -> value;
    }

    return;
}


void Semantic::ProcessArrayInitializer(AstArrayInitializer *array_initializer, TypeSymbol *type)
{
    if (! type -> IsArray())
    {
        ReportSemError(SemanticError::INIT_SCALAR_WITH_ARRAY,
                       array_initializer -> LeftToken(),
                       array_initializer -> RightToken(),
                       type -> Name());
    }
    else
    {
        for (int i = 0; i < array_initializer -> NumVariableInitializers(); i++)
        {
            AstArrayInitializer *sub_array_initializer = array_initializer -> VariableInitializer(i) -> ArrayInitializerCast();
            TypeSymbol *array_subtype = type -> ArraySubtype();
            if (sub_array_initializer)
                 ProcessArrayInitializer(sub_array_initializer, array_subtype);
            else
            {
                AstExpression *init = (AstExpression *) array_initializer -> VariableInitializer(i);
                ProcessExpressionOrStringConstant(init);

                if (array_subtype != init -> Type())
                {
                    if (CanAssignmentConvert(array_subtype, init))
                        array_initializer -> VariableInitializer(i) = ConvertToType(init, array_subtype);
                    else if (array_subtype -> IsArray() && init -> Type() -> Primitive())
                    {
                        ReportSemError(SemanticError::INIT_ARRAY_WITH_SCALAR,
                                       init -> LeftToken(),
                                       init -> RightToken(),
                                       array_subtype -> Name());
                    }
                    else if (init -> IsConstant() && control.IsSimpleIntegerValueType(init -> Type())
                                                  && control.IsSimpleIntegerValueType(array_subtype))
                    {
                        if (array_subtype == control.byte_type)
                             ReportSemError(SemanticError::INVALID_BYTE_VALUE,
                                            init -> LeftToken(),
                                            init -> RightToken());
                        else if (array_subtype == control.char_type)
                             ReportSemError(SemanticError::INVALID_CHARACTER_VALUE,
                                            init -> LeftToken(),
                                            init -> RightToken());
                        else ReportSemError(SemanticError::INVALID_SHORT_VALUE,
                                            init -> LeftToken(),
                                            init -> RightToken());
                    }
                    else
                    {
                        ReportSemError(SemanticError::INCOMPATIBLE_TYPE_FOR_INITIALIZATION,
                                       init -> LeftToken(),
                                       init -> RightToken(),
                                       array_subtype -> ContainingPackage() -> PackageName(),
                                       array_subtype -> ExternalName(),
                                       init -> Type() -> ContainingPackage() -> PackageName(),
                                       init -> Type() -> ExternalName());
                    }
                }
            }
        }
    }

    return;
}


LiteralValue *Semantic::ComputeFinalValue(AstVariableDeclarator *variable_declarator)
{
    LiteralValue *value = NULL;

    VariableSymbol *variable = variable_declarator -> symbol;
    TypeSymbol *type = (TypeSymbol *) variable -> owner;

    state_stack.Push(type -> semantic_environment);
    if (! error)
        error = new SemanticError(control, source_file_symbol);
    error -> EnteringClone();
    variable_declarator -> pending = true;

    AstExpression *init_expr = (AstExpression *) variable_declarator -> variable_initializer_opt;
    AstExpression *init_clone = (AstExpression *) init_expr -> Clone(compilation_unit -> ast_pool);
    ProcessExpressionOrStringConstant(init_clone);
    if (variable -> Type() != init_clone -> Type() && init_clone -> Type() != control.no_type)
    {
        if (CanAssignmentConvert(variable -> Type(), init_clone))
             init_clone = ConvertToType(init_clone, variable -> Type());
        else init_clone -> value = NULL;
    }
    value = init_clone -> value;

//
// STG:
//        delete init_clone; // destroy the clone
//

    variable_declarator -> pending = false;
    error -> ExitingClone();
    state_stack.Pop();

    return value;
}