// See the file "COPYING" in the main distribution directory for copyright.

#include "pac_expr.h"

#include "pac_case.h"
#include "pac_cstr.h"
#include "pac_exception.h"
#include "pac_exttype.h"
#include "pac_id.h"
#include "pac_let.h"
#include "pac_nullptr.h"
#include "pac_number.h"
#include "pac_output.h"
#include "pac_record.h"
#include "pac_regex.h"
#include "pac_typedecl.h"
#include "pac_utils.h"

string OrigExprList(ExprList* list) {
    bool first = true;
    string str;
    foreach (i, ExprList, list) {
        Expr* expr = *i;
        if ( first )
            first = false;
        else
            str += ", ";
        str += expr->orig();
    }
    return str;
}

string EvalExprList(ExprList* exprlist, Output* out, Env* env) {
    string val_list("");
    bool first = true;

    foreach (i, ExprList, exprlist) {
        if ( ! first )
            val_list += ", ";
        val_list += (*i)->EvalExpr(out, env);
        first = false;
    }

    return val_list;
}

static const char* expr_fmt[] = {
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define EXPR_DEF(type, num_op, fmt) fmt,
#include "pac_expr.def"
#undef EXPR_DEF
};

void Expr::init() {
    id_ = nullptr;
    num_ = nullptr;
    cstr_ = nullptr;
    regex_ = nullptr;
    num_operands_ = 0;
    operand_[0] = nullptr;
    operand_[1] = nullptr;
    operand_[2] = nullptr;
    args_ = nullptr;
    cases_ = nullptr;
}

Expr::Expr(ID* arg_id) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_ID;
    id_ = arg_id;
    orig_ = strfmt("%s", id_->Name());
}

Expr::Expr(Number* arg_num) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_NUM;
    num_ = arg_num;
    orig_ = strfmt("((int) %s)", num_->Str());
}

Expr::Expr(Nullptr* arg_nullp) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_NULLPTR;
    nullp_ = arg_nullp;
    orig_ = strfmt("%s", nullp_->Str());
}

Expr::Expr(ConstString* cstr) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_CSTR;
    cstr_ = cstr;
    orig_ = cstr_->str();
}

Expr::Expr(RegEx* regex) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_REGEX;
    regex_ = regex;
    orig_ = strfmt("/%s/", regex_->str().c_str());
}

Expr::Expr(ExprType arg_type, Expr* op1) : DataDepElement(EXPR) {
    init();
    expr_type_ = arg_type;
    num_operands_ = 1;
    operand_[0] = op1;
    orig_ = strfmt(expr_fmt[expr_type_], op1->orig());
}

Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2) : DataDepElement(EXPR) {
    init();
    expr_type_ = arg_type;
    num_operands_ = 2;
    operand_[0] = op1;
    operand_[1] = op2;
    operand_[2] = nullptr;
    orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig());
}

Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2, Expr* op3) : DataDepElement(EXPR) {
    init();
    expr_type_ = arg_type;
    num_operands_ = 3;
    operand_[0] = op1;
    operand_[1] = op2;
    operand_[2] = op3;
    orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig(), op3->orig());
}

Expr::Expr(ExprList* args) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_CALLARGS;
    num_operands_ = -1;
    args_ = args;

    orig_ = OrigExprList(args_);
}

Expr::Expr(Expr* index, CaseExprList* cases) : DataDepElement(EXPR) {
    init();
    expr_type_ = EXPR_CASE;
    num_operands_ = -1;
    operand_[0] = index;
    cases_ = cases;

    orig_ = strfmt("case %s of { ", index->orig());
    foreach (i, CaseExprList, cases_) {
        CaseExpr* c = *i;
        orig_ += strfmt("%s => %s; ", OrigExprList(c->index()).c_str(), c->value()->orig());
    }
    orig_ += "}";
}

Expr::~Expr() {
    delete id_;
    delete operand_[0];
    delete operand_[1];
    delete operand_[2];
    delete_list(args_);
    delete_list(cases_);
}

void Expr::AddCaseExpr(CaseExpr* case_expr) {
    ASSERT(str_.empty());
    ASSERT(expr_type_ == EXPR_CASE);
    ASSERT(cases_);
    cases_->push_back(case_expr);
}

void Expr::GenStrFromFormat(Env* env) {
    // The format != "@custom@"
    ASSERT(*expr_fmt[expr_type_] != '@');

    switch ( num_operands_ ) {
        case 1: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str()); break;
        case 2: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str()); break;
        case 3: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str(), operand_[2]->str()); break;
        default:
            DEBUG_MSG("num_operands_ = %d, orig = %s\n", num_operands_, orig());
            ASSERT(0);
            break;
    }
}

namespace {

RecordField* GetRecordField(const ID* id, Env* env) {
    Field* field = env->GetField(id);
    ASSERT(field);
    if ( field->tof() != RECORD_FIELD && field->tof() != PADDING_FIELD )
        throw Exception(id, "not a record field");
    RecordField* r = static_cast<RecordField*>(field);
    ASSERT(r);
    return r;
}

} // namespace

void Expr::GenCaseEval(Output* out_cc, Env* env) {
    ASSERT(expr_type_ == EXPR_CASE);
    ASSERT(operand_[0]);
    ASSERT(cases_);

    Type* val_type = DataType(env);
    ID* val_var = env->AddTempID(val_type);

    // DataType(env) can return a null pointer if an enum value is not
    // defined.
    if ( ! val_type )
        throw Exception(this, "undefined case value");

    out_cc->println("%s %s;", val_type->DataTypeStr().c_str(), env->LValue(val_var));

    // force evaluation of IDs appearing in case stmt
    operand_[0]->ForceIDEval(out_cc, env);
    foreach (i, CaseExprList, cases_)
        (*i)->value()->ForceIDEval(out_cc, env);

    out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)");
    out_cc->println("switch ( %s ) {", operand_[0]->EvalExpr(out_cc, env));
    Type* switch_type = operand_[0]->DataType(env);

    out_cc->inc_indent();

    CaseExpr* default_case = nullptr;
    foreach (i, CaseExprList, cases_) {
        CaseExpr* c = *i;
        ExprList* index = c->index();
        if ( ! index ) {
            if ( default_case )
                throw Exception(c, "duplicate default cases");
            default_case = c;
        }
        else {
            GenCaseStr(index, out_cc, env, switch_type);
            out_cc->inc_indent();
            out_cc->println("%s = %s;", env->LValue(val_var), c->value()->EvalExpr(out_cc, env));
            out_cc->println("break;");
            out_cc->dec_indent();
        }
    }

    // Generate the default case after all other cases
    GenCaseStr(nullptr, out_cc, env, switch_type);
    out_cc->inc_indent();
    if ( default_case ) {
        out_cc->println("%s = %s;", env->LValue(val_var), default_case->value()->EvalExpr(out_cc, env));
    }
    else {
        out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", Location(),
                        operand_[0]->EvalExpr(out_cc, env));
    }
    out_cc->println("break;");
    out_cc->dec_indent();

    out_cc->dec_indent();
    out_cc->println("}");
    out_cc->println("// NOLINTEND(bugprone-branch-clone)");

    env->SetEvaluated(val_var);
    str_ = env->RValue(val_var);
}

void Expr::GenEval(Output* out_cc, Env* env) {
    switch ( expr_type_ ) {
        case EXPR_NUM: str_ = num_->Str(); break;
        case EXPR_NULLPTR: str_ = nullp_->Str(); break;

        case EXPR_ID:
            if ( ! env->Evaluated(id_) )
                env->Evaluate(out_cc, id_);
            str_ = env->RValue(id_);
            break;

        case EXPR_MEMBER: {
            /*
            For member expressions such X.Y, evaluating
            X only is sufficient. (Actually trying to
            evaluate Y will lead to error because Y is
            not defined in the current environment.)
            */
            operand_[0]->GenEval(out_cc, env);

            Type* ty0 = operand_[0]->DataType(env);

            if ( ty0 ) {
                str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), ty0->EvalMember(operand_[1]->id()).c_str());
            }
            else {
                string tmp = strfmt("->%s()", operand_[1]->id()->Name());
                str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), tmp.c_str());
            }
        } break;

        case EXPR_SUBSCRIPT: {
            operand_[0]->GenEval(out_cc, env);
            operand_[1]->GenEval(out_cc, env);

            string v0 = operand_[0]->EvalExpr(out_cc, env);
            string v1 = operand_[1]->EvalExpr(out_cc, env);

            Type* ty0 = operand_[0]->DataType(env);
            if ( ty0 )
                str_ = ty0->EvalElement(v0, v1);
            else
                str_ = strfmt("%s[%s]", v0.c_str(), v1.c_str());
        } break;

        case EXPR_SIZEOF: {
            const ID* id = operand_[0]->id();
            RecordField* rf;
            Type* ty;

            try {
                if ( rf = GetRecordField(id, env); rf != nullptr ) {
                    str_ = strfmt("%s", rf->FieldSize(out_cc, env));
                }
            } catch ( ExceptionIDNotFound& e ) {
                if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) {
                    int ty_size = ty->StaticSize(global_env());
                    if ( ty_size >= 0 )
                        str_ = strfmt("%d", ty_size);
                    else
                        throw Exception(id, "unknown size");
                }
                else
                    throw Exception(id, "not a record field or type");
            }
        } break;

        case EXPR_OFFSETOF: {
            const ID* id = operand_[0]->id();
            RecordField* rf = GetRecordField(id, env);
            str_ = strfmt("%s", rf->FieldOffset(out_cc, env));
        } break;

        case EXPR_CALLARGS: str_ = EvalExprList(args_, out_cc, env); break;

        case EXPR_CASE: GenCaseEval(out_cc, env); break;

        default:
            // Evaluate every operand by default
            for ( auto& op : operand_ )
                if ( op )
                    op->GenEval(out_cc, env);
            GenStrFromFormat(env);
            break;
    }
}

void Expr::ForceIDEval(Output* out_cc, Env* env) {
    switch ( expr_type_ ) {
        case EXPR_NUM:
        case EXPR_SIZEOF:
        case EXPR_OFFSETOF: break;

        case EXPR_ID:
            if ( ! env->Evaluated(id_) )
                env->Evaluate(out_cc, id_);
            break;

        case EXPR_MEMBER: operand_[0]->ForceIDEval(out_cc, env); break;

        case EXPR_CALLARGS: {
            foreach (i, ExprList, args_)
                (*i)->ForceIDEval(out_cc, env);
        } break;

        case EXPR_CASE: {
            operand_[0]->ForceIDEval(out_cc, env);
            foreach (i, CaseExprList, cases_)
                (*i)->value()->ForceIDEval(out_cc, env);
        } break;

        default:
            // Evaluate every operand by default
            for ( auto& op : operand_ )
                if ( op )
                    op->ForceIDEval(out_cc, env);
            break;
    }
}

const char* Expr::EvalExpr(Output* out_cc, Env* env) {
    GenEval(out_cc, env);
    return str();
}

Type* Expr::DataType(Env* env) const {
    Type* data_type;

    switch ( expr_type_ ) {
        case EXPR_ID: data_type = env->GetDataType(id_); break;

        case EXPR_MEMBER: {
            // Get type of the parent
            Type* parent_type = operand_[0]->DataType(env);
            if ( ! parent_type )
                return nullptr;
            data_type = parent_type->MemberDataType(operand_[1]->id());
        } break;

        case EXPR_SUBSCRIPT: {
            // Get type of the parent
            Type* parent_type = operand_[0]->DataType(env);
            data_type = parent_type->ElementDataType();
        } break;

        case EXPR_PAREN: data_type = operand_[0]->DataType(env); break;

        case EXPR_COND: {
            Type* type1 = operand_[1]->DataType(env);
            Type* type2 = operand_[2]->DataType(env);
            if ( ! Type::CompatibleTypes(type1, type2) ) {
                throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
                                             type2->DataTypeStr().c_str()));
            }
            data_type = type1;
        } break;

        case EXPR_CALL: data_type = operand_[0]->DataType(env); break;

        case EXPR_CASE: {
            if ( cases_ && ! cases_->empty() ) {
                Type* type1 = cases_->front()->value()->DataType(env);
                Type* numeric_with_largest_width = nullptr;

                foreach (i, CaseExprList, cases_) {
                    Type* type2 = (*i)->value()->DataType(env);
                    if ( ! Type::CompatibleTypes(type1, type2) ) {
                        throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(),
                                                     type2->DataTypeStr().c_str()));
                    }
                    if ( type1 == extern_type_nullptr )
                        type1 = type2;

                    if ( type2 && type2->IsNumericType() ) {
                        if ( numeric_with_largest_width ) {
                            int largest;
                            int contender;

                            // External C++ types like "int", "bool", "enum" use "int"
                            // storage internally.
                            if ( numeric_with_largest_width->tot() == Type::EXTERN )
                                largest = sizeof(int);
                            else
                                largest = numeric_with_largest_width->StaticSize(env);

                            if ( type2->tot() == Type::EXTERN )
                                contender = sizeof(int);
                            else
                                contender = type2->StaticSize(env);

                            if ( contender > largest )
                                numeric_with_largest_width = type2;
                        }
                        else
                            numeric_with_largest_width = type2;
                    }
                }
                data_type = numeric_with_largest_width ? numeric_with_largest_width : type1;
            }
            else
                data_type = nullptr;
        } break;

        case EXPR_NUM:
        case EXPR_SIZEOF:
        case EXPR_OFFSETOF:
        case EXPR_NEG:
        case EXPR_PLUS:
        case EXPR_MINUS:
        case EXPR_TIMES:
        case EXPR_DIV:
        case EXPR_MOD:
        case EXPR_BITNOT:
        case EXPR_BITAND:
        case EXPR_BITOR:
        case EXPR_BITXOR:
        case EXPR_LSHIFT:
        case EXPR_RSHIFT:
        case EXPR_EQUAL:
        case EXPR_GE:
        case EXPR_LE:
        case EXPR_GT:
        case EXPR_LT:
        case EXPR_NOT:
        case EXPR_AND:
        case EXPR_OR: data_type = extern_type_int; break;

        default: data_type = nullptr; break;
    }

    return data_type;
}

string Expr::DataTypeStr(Env* env) const {
    Type* type = DataType(env);

    if ( ! type ) {
        throw Exception(this, strfmt("cannot find data type for expression `%s'", orig()));
    }

    return type->DataTypeStr();
}

string Expr::SetFunc(Output* out, Env* env) {
    switch ( expr_type_ ) {
        case EXPR_ID: return set_function(id_);
        case EXPR_MEMBER: {
            // Evaluate the parent
            string parent_val(operand_[0]->EvalExpr(out, env));
            return parent_val + "->" + set_function(operand_[1]->id());
        } break;
        default:
            throw Exception(this, strfmt("cannot generate set function "
                                         "for expression `%s'",
                                         orig()));
            break;
    }
}

bool Expr::ConstFold(Env* env, int* pn) const {
    switch ( expr_type_ ) {
        case EXPR_NUM: *pn = num_->Num(); return true;
        case EXPR_ID: return env->GetConstant(id_, pn);
        default:
            // ### FIXME: folding consts
            return false;
    }
}

// TODO: build a generic data dependency extraction process
namespace {

// Maximum of two minimal header sizes
int mhs_max(int h1, int h2) {
    if ( h1 < 0 || h2 < 0 )
        return -1;
    else {
        // return max(h1, h2);
        return h1 > h2 ? h1 : h2;
    }
}

// MHS required to evaluate the field
int mhs_letfield(Env* env, LetField* field) { return field->expr()->MinimalHeaderSize(env); }

int mhs_recordfield(Env* env, RecordField* field) {
    int offset = field->static_offset();
    if ( offset < 0 ) // offset cannot be statically determined
        return -1;
    int size = field->StaticSize(env, offset);
    if ( size < 0 ) // size cannot be statically determined
        return -1;
    return offset + size;
}

int mhs_casefield(Env* env, CaseField* field) {
    // TODO: deal with the index
    int size = field->StaticSize(env);
    if ( size < 0 ) // size cannot be statically determined
        return -1;
    return size;
}

int mhs_field(Env* env, Field* field) {
    int mhs = -1;
    switch ( field->tof() ) {
        case LET_FIELD: {
            LetField* f = static_cast<LetField*>(field);
            ASSERT(f);
            mhs = mhs_letfield(env, f);
        } break;

        case CONTEXT_FIELD:
        case FLOW_FIELD: ASSERT(0); break;

        case PARAM_FIELD: mhs = 0; break;

        case RECORD_FIELD:
        case PADDING_FIELD: {
            RecordField* f = static_cast<RecordField*>(field);
            ASSERT(f);
            mhs = mhs_recordfield(env, f);
        } break;

        case CASE_FIELD: {
            CaseField* f = static_cast<CaseField*>(field);
            ASSERT(f);
            mhs = mhs_casefield(env, f);
        } break;

        case PARSE_VAR_FIELD:
        case PRIV_VAR_FIELD:
        case PUB_VAR_FIELD:
        case TEMP_VAR_FIELD: mhs = 0; break;

        case WITHINPUT_FIELD: {
            // ### TODO: fix this
            mhs = -1;
        } break;
    }
    return mhs;
}

int mhs_id(Env* env, const ID* id) {
    int mhs = -1;
    switch ( env->GetIDType(id) ) {
        case CONST:
        case GLOBAL_VAR:
        case TEMP_VAR:
        case STATE_VAR:
        case FUNC_ID:
        case FUNC_PARAM: mhs = 0; break;
        case MEMBER_VAR:
        case PRIV_MEMBER_VAR: {
            Field* field = env->GetField(id);
            if ( ! field )
                throw ExceptionIDNotField(id);
            mhs = mhs_field(env, field);
        } break;
        case UNION_VAR:
            // TODO: deal with UNION_VAR
            mhs = -1;
            break;
        case MACRO: {
            Expr* e = env->GetMacro(id);
            mhs = e->MinimalHeaderSize(env);
        } break;
    }
    return mhs;
}
} // namespace

int Expr::MinimalHeaderSize(Env* env) {
    int mhs;

    switch ( expr_type_ ) {
        case EXPR_NUM:
            // Zero byte is required
            mhs = 0;
            break;

        case EXPR_ID: mhs = mhs_id(env, id_); break;

        case EXPR_MEMBER:
            // TODO: this is not a tight bound because
            // one actually does not have to parse the
            // whole record to compute one particular
            // field.
            mhs = operand_[0]->MinimalHeaderSize(env);
            break;

        case EXPR_SUBSCRIPT: {
            int index;
            Type* array_type = operand_[0]->DataType(env);
            Type* elem_type = array_type->ElementDataType();
            int elem_size = elem_type->StaticSize(env);
            if ( elem_size >= 0 && operand_[1]->ConstFold(env, &index) ) {
                mhs = elem_size * index;
            }
            else {
                mhs = -1;
            }
        } break;

        case EXPR_SIZEOF: {
            const ID* id = operand_[0]->id();
            ASSERT(id);
            RecordField* rf;
            Type* ty;

            if ( rf = GetRecordField(id, env); rf != nullptr ) {
                if ( rf->StaticSize(env, -1) >= 0 )
                    mhs = 0;
                else
                    mhs = mhs_recordfield(env, rf);
            }

            else if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) {
                mhs = 0;
            }

            else
                throw Exception(id, "not a record field or type");
        } break;

        case EXPR_OFFSETOF: {
            const ID* id = operand_[0]->id();
            ASSERT(id);
            RecordField* field = GetRecordField(id, env);

            mhs = field->static_offset();
            if ( mhs < 0 ) {
                mhs = 0;
                // Take the MHS of the preceding (non-let) field
                RecordField* prev_field = field->prev();
                ASSERT(prev_field);
                mhs = mhs_recordfield(env, prev_field);
            }
        } break;

        case EXPR_CALLARGS: {
            mhs = 0;
            if ( args_ )
                for ( const auto& arg : *args_ )
                    mhs = mhs_max(mhs, arg->MinimalHeaderSize(env));
        } break;
        case EXPR_CASE: {
            mhs = operand_[0]->MinimalHeaderSize(env);
            for ( const auto& ce : *cases_ ) {
                if ( ce->index() )
                    for ( const auto& idx : *(ce->index()) )
                        mhs = mhs_max(mhs, idx->MinimalHeaderSize(env));
                mhs = mhs_max(mhs, ce->value()->MinimalHeaderSize(env));
            }
        } break;
        default:
            // Evaluate every operand by default
            mhs = 0;
            for ( auto& op : operand_ )
                if ( op )
                    mhs = mhs_max(mhs, op->MinimalHeaderSize(env));
            break;
    }

    return mhs;
}

bool Expr::HasReference(const ID* id) const {
    switch ( expr_type_ ) {
        case EXPR_ID: return *id == *id_;

        case EXPR_MEMBER: return operand_[0]->HasReference(id);

        case EXPR_CALLARGS: {
            foreach (i, ExprList, args_)
                if ( (*i)->HasReference(id) )
                    return true;
        }
            return false;

        case EXPR_CASE: {
            foreach (i, CaseExprList, cases_)
                if ( (*i)->HasReference(id) )
                    return true;
        }
            return false;

        default:
            // Evaluate every operand by default
            for ( auto& op : operand_ ) {
                if ( op && op->HasReference(id) ) {
                    return true;
                }
            }
            return false;
    }
}

bool Expr::DoTraverse(DataDepVisitor* visitor) {
    switch ( expr_type_ ) {
        case EXPR_ID: break;

        case EXPR_MEMBER:
            /*
            For member expressions such X.Y, evaluating
            X only is sufficient. (Actually trying to
            evaluate Y will lead to error because Y is
            not defined in the current environment.)
            */
            if ( ! operand_[0]->Traverse(visitor) )
                return false;
            break;

        case EXPR_CALLARGS: {
            foreach (i, ExprList, args_)
                if ( ! (*i)->Traverse(visitor) )
                    return false;
        } break;

        case EXPR_CASE: {
            foreach (i, CaseExprList, cases_)
                if ( ! (*i)->Traverse(visitor) )
                    return false;
        } break;

        default:
            // Evaluate every operand by default
            for ( auto& op : operand_ ) {
                if ( op && ! op->Traverse(visitor) ) {
                    return false;
                }
            }
            break;
    }

    return true;
}

bool Expr::RequiresAnalyzerContext() const {
    switch ( expr_type_ ) {
        case EXPR_ID: return *id_ == *analyzer_context_id;

        case EXPR_MEMBER:
            /*
            For member expressions such X.Y, evaluating
            X only is sufficient. (Actually trying to
            evaluate Y will lead to error because Y is
            not defined in the current environment.)
            */
            return operand_[0]->RequiresAnalyzerContext();

        case EXPR_CALLARGS: {
            foreach (i, ExprList, args_)
                if ( (*i)->RequiresAnalyzerContext() )
                    return true;
        }
            return false;

        case EXPR_CASE: {
            foreach (i, CaseExprList, cases_)
                if ( (*i)->RequiresAnalyzerContext() )
                    return true;
        }
            return false;

        default:
            // Evaluate every operand by default
            for ( auto& op : operand_ )
                if ( op && op->RequiresAnalyzerContext() ) {
                    DEBUG_MSG("'%s' requires analyzer context\n", op->orig());
                    return true;
                }
            return false;
    }
}

CaseExpr::CaseExpr(ExprList* index, Expr* value)
    : DataDepElement(DataDepElement::CASEEXPR), index_(index), value_(value) {}

CaseExpr::~CaseExpr() {
    delete_list(index_);
    index_ = nullptr;
    delete value_;
}

bool CaseExpr::DoTraverse(DataDepVisitor* visitor) {
    foreach (i, ExprList, index_)
        if ( ! (*i)->Traverse(visitor) )
            return false;
    return value_->Traverse(visitor);
}

bool CaseExpr::HasReference(const ID* id) const { return value_->HasReference(id); }

bool CaseExpr::RequiresAnalyzerContext() const {
    // index_ should evaluate to constants
    return value_->RequiresAnalyzerContext();
}
