/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include namespace MiniZinc { bool checkParDomain(EnvI& env, Expression* e, Expression* domain) { if (e->type() == Type::parint()) { IntSetVal* isv = eval_intset(env, domain); if (!isv->contains(eval_int(env, e))) return false; } else if (e->type() == Type::parfloat()) { FloatSetVal* fsv = eval_floatset(env, domain); if (!fsv->contains(eval_float(env, e))) return false; } else if (e->type() == Type::parsetint()) { IntSetVal* isv = eval_intset(env, domain); IntSetRanges ir(isv); IntSetVal* rsv = eval_intset(env, e); IntSetRanges rr(rsv); if (!Ranges::subset(rr, ir)) return false; } else if (e->type() == Type::parsetfloat()) { FloatSetVal* fsv = eval_floatset(env, domain); FloatSetRanges fr(fsv); FloatSetVal* rsv = eval_floatset(env, e); FloatSetRanges rr(rsv); if (!Ranges::subset(rr, fr)) return false; } return true; } template typename E::Val eval_id(EnvI& env, Expression* e) { Id* id = e->cast(); if (id->decl() == NULL) throw EvalError(env, e->loc(), "undeclared identifier", id->str().str()); VarDecl* vd = id->decl(); while (vd->flat() && vd->flat() != vd) vd = vd->flat(); if (vd->e() == NULL) throw EvalError(env, vd->loc(), "cannot evaluate expression", id->str().str()); typename E::Val r = E::e(env, vd->e()); if (!vd->evaluated() && (vd->toplevel() || vd->type().dim() > 0)) { Expression* ne = E::exp(r); vd->e(ne); vd->evaluated(true); } return r; } class EvalIntLit { public: typedef IntLit* Val; typedef Expression* ArrayVal; static IntLit* e(EnvI& env, Expression* e) { return IntLit::a(eval_int(env, e)); } static Expression* exp(IntLit* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalIntVal { public: typedef IntVal Val; typedef IntVal ArrayVal; static IntVal e(EnvI& env, Expression* e) { return eval_int(env, e); } static Expression* exp(IntVal e) { return IntLit::a(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); if (!isv->contains(v)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatVal { public: typedef FloatVal Val; typedef FloatVal ArrayVal; static FloatVal e(EnvI& env, Expression* e) { return eval_float(env, e); } static Expression* exp(FloatVal e) { return FloatLit::a(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); if (!fsv->contains(v)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatLit { public: typedef FloatLit* Val; typedef Expression* ArrayVal; static FloatLit* e(EnvI& env, Expression* e) { return FloatLit::a(eval_float(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalString { public: typedef std::string Val; typedef std::string ArrayVal; static std::string e(EnvI& env, Expression* e) { return eval_string(env, e); } static Expression* exp(const std::string& e) { return new StringLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalStringLit { public: typedef StringLit* Val; typedef Expression* ArrayVal; static StringLit* e(EnvI& env, Expression* e) { return new StringLit(Location(), eval_string(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolLit { public: typedef BoolLit* Val; typedef Expression* ArrayVal; static BoolLit* e(EnvI& env, Expression* e) { return constants().boollit(eval_bool(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolVal { public: typedef bool Val; static bool e(EnvI& env, Expression* e) { return eval_bool(env, e); } static Expression* exp(bool e) { return constants().boollit(e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalArrayLit { public: typedef ArrayLit* Val; typedef Expression* ArrayVal; static ArrayLit* e(EnvI& env, Expression* e) { return eval_array_lit(env, e); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalArrayLitCopy { public: typedef ArrayLit* Val; typedef Expression* ArrayVal; static ArrayLit* e(EnvI& env, Expression* e) { return copy(env, eval_array_lit(env, e), true)->cast(); } static Expression* exp(Expression* e) { return e; } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { for (unsigned int i = 0; i < fi->ti()->ranges().size(); i++) { if (fi->ti()->ranges()[i]->domain() && !fi->ti()->ranges()[i]->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->ranges()[i]->domain()); bool bothEmpty = isv->min() > isv->max() && v->min(i) > v->max(i); if (!bothEmpty && (v->min(i) != isv->min() || v->max(i) != isv->max())) { std::ostringstream oss; oss << "array index set " << (i + 1) << " of function result violates function type-inst"; throw ResultUndefinedError(env, fi->e()->loc(), oss.str()); } } } if (fi->ti()->domain() && !fi->ti()->domain()->isa() && fi->ti()->type().ti() == Type::TI_PAR) { Type base_t = fi->ti()->type(); if (base_t.bt() == Type::BT_INT) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); if (base_t.st() == Type::ST_PLAIN) { for (unsigned int i = 0; i < v->size(); i++) { IntVal iv = eval_int(env, (*v)[i]); if (!isv->contains(iv)) { std::ostringstream oss; oss << "array contains value " << iv << " which is not contained in " << *isv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } else { for (unsigned int i = 0; i < v->size(); i++) { IntSetVal* iv = eval_intset(env, (*v)[i]); IntSetRanges isv_r(isv); IntSetRanges v_r(iv); if (!Ranges::subset(v_r, isv_r)) { std::ostringstream oss; oss << "array contains value " << *iv << " which is not a subset of " << *isv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } } else if (base_t.bt() == Type::BT_FLOAT) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); if (base_t.st() == Type::ST_PLAIN) { for (unsigned int i = 0; i < v->size(); i++) { FloatVal fv = eval_float(env, (*v)[i]); if (!fsv->contains(fv)) { std::ostringstream oss; oss << "array contains value " << fv << " which is not contained in " << *fsv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } else { for (unsigned int i = 0; i < v->size(); i++) { FloatSetVal* fv = eval_floatset(env, (*v)[i]); FloatSetRanges fsv_r(fsv); FloatSetRanges v_r(fv); if (!Ranges::subset(v_r, fsv_r)) { std::ostringstream oss; oss << "array contains value " << *fv << " which is not a subset of " << *fsv; throw ResultUndefinedError( env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); } } } } } } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalIntSet { public: typedef IntSetVal* Val; static IntSetVal* e(EnvI& env, Expression* e) { return eval_intset(env, e); } static Expression* exp(IntSetVal* e) { return new SetLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { IntSetVal* isv = eval_intset(env, fi->ti()->domain()); IntSetRanges isv_r(isv); IntSetRanges v_r(v); if (!Ranges::subset(v_r, isv_r)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatSet { public: typedef FloatSetVal* Val; static FloatSetVal* e(EnvI& env, Expression* e) { return eval_floatset(env, e); } static Expression* exp(FloatSetVal* e) { return new SetLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); FloatSetRanges fsv_r(fsv); FloatSetRanges v_r(v); if (!Ranges::subset(v_r, fsv_r)) { throw ResultUndefinedError(env, Location().introduce(), "function result violates function type-inst"); } } } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolSet { public: typedef IntSetVal* Val; static IntSetVal* e(EnvI& env, Expression* e) { return eval_boolset(env, e); } static Expression* exp(IntSetVal* e) { return new SetLit(Location(), e); } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalSetLit { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_intset(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalFloatSetLit { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_floatset(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalBoolSetLit { public: typedef SetLit* Val; typedef Expression* ArrayVal; static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_boolset(env, e)); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalCopy { public: typedef Expression* Val; typedef Expression* ArrayVal; static Expression* e(EnvI& env, Expression* e) { return copy(env, e, true); } static Expression* exp(Expression* e) { return e; } Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; class EvalPar { public: typedef Expression* Val; typedef Expression* ArrayVal; static Expression* e(EnvI& env, Expression* e) { return eval_par(env, e); } static Expression* exp(Expression* e) { return e; } static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} Expression* flatten(EnvI&, Expression*) { throw InternalError("evaluating var assignment generator inside par expression not supported"); } }; void checkDom(EnvI& env, Id* arg, IntSetVal* dom, Expression* e) { bool oob = false; if (!e->type().isopt()) { if (e->type().isintset()) { IntSetVal* ev = eval_intset(env, e); IntSetRanges ev_r(ev); IntSetRanges dom_r(dom); oob = !Ranges::subset(ev_r, dom_r); } else { oob = !dom->contains(eval_int(env, e)); } } if (oob) { std::ostringstream oss; oss << "value for argument `" << *arg << "' out of bounds"; throw EvalError(env, e->loc(), oss.str()); } } void checkDom(EnvI& env, Id* arg, FloatVal dom_min, FloatVal dom_max, Expression* e) { if (!e->type().isopt()) { FloatVal ev = eval_float(env, e); if (ev < dom_min || ev > dom_max) { std::ostringstream oss; oss << "value for argument `" << *arg << "' out of bounds"; throw EvalError(env, e->loc(), oss.str()); } } } template typename Eval::Val eval_call(EnvI& env, CallClass* ce) { std::vector previousParameters(ce->decl()->params().size()); std::vector params(ce->decl()->params().size()); for (unsigned int i = 0; i < ce->decl()->params().size(); i++) { params[i] = eval_par(env, ce->arg(i)); } for (unsigned int i = ce->decl()->params().size(); i--;) { VarDecl* vd = ce->decl()->params()[i]; if (vd->type().dim() > 0) { // Check array index sets ArrayLit* al = params[i]->cast(); for (unsigned int j = 0; j < vd->ti()->ranges().size(); j++) { TypeInst* range_ti = vd->ti()->ranges()[j]; if (range_ti->domain() && !range_ti->domain()->isa()) { IntSetVal* isv = eval_intset(env, range_ti->domain()); if (isv->min() != al->min(j) || isv->max() != al->max(j)) { std::ostringstream oss; oss << "array index set " << (j + 1) << " of argument " << (i + 1) << " does not match declared index set"; throw EvalError(env, ce->loc(), oss.str()); } } } } previousParameters[i] = vd->e(); vd->flat(vd); vd->e(params[i]); if (vd->e()->type().ispar()) { if (Expression* dom = vd->ti()->domain()) { if (!dom->isa()) { if (vd->e()->type().bt() == Type::BT_INT) { IntSetVal* isv = eval_intset(env, dom); if (vd->e()->type().dim() > 0) { ArrayLit* al = eval_array_lit(env, vd->e()); for (unsigned int i = 0; i < al->size(); i++) { checkDom(env, vd->id(), isv, (*al)[i]); } } else { checkDom(env, vd->id(), isv, vd->e()); } } else if (vd->e()->type().bt() == Type::BT_FLOAT) { GCLock lock; FloatSetVal* fsv = eval_floatset(env, dom); FloatVal dom_min = fsv->min(); FloatVal dom_max = fsv->max(); checkDom(env, vd->id(), dom_min, dom_max, vd->e()); } } } } } typename Eval::Val ret = Eval::e(env, ce->decl()->e()); Eval::checkRetVal(env, ret, ce->decl()); for (unsigned int i = ce->decl()->params().size(); i--;) { VarDecl* vd = ce->decl()->params()[i]; vd->e(previousParameters[i]); vd->flat(vd->e() ? vd : NULL); } return ret; } ArrayLit* eval_array_comp(EnvI& env, Comprehension* e) { ArrayLit* ret; if (e->type() == Type::parint(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parbool(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parfloat(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parsetint(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else if (e->type() == Type::parstring(1)) { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } else { std::vector a = eval_comp(env, e); ret = new ArrayLit(e->loc(), a); } ret->type(e->type()); return ret; } ArrayLit* eval_array_lit(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_SETLIT: case Expression::E_ANON: case Expression::E_TI: case Expression::E_TIID: case Expression::E_VARDECL: throw EvalError(env, e->loc(), "not an array expression"); case Expression::E_ID: return eval_id(env, e); case Expression::E_ARRAYLIT: return e->cast(); case Expression::E_ARRAYACCESS: throw EvalError(env, e->loc(), "arrays of arrays not supported"); case Expression::E_COMP: return eval_array_comp(env, e->cast()); case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_array_lit(env, ite->e_then(i)); } return eval_array_lit(env, ite->e_else()); } case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->op() == BOT_PLUSPLUS) { ArrayLit* al0 = eval_array_lit(env, bo->lhs()); ArrayLit* al1 = eval_array_lit(env, bo->rhs()); std::vector v(al0->size() + al1->size()); for (unsigned int i = al0->size(); i--;) v[i] = (*al0)[i]; for (unsigned int i = al1->size(); i--;) v[al0->size() + i] = (*al1)[i]; ArrayLit* ret = new ArrayLit(e->loc(), v); ret->flat(al0->flat() && al1->flat()); ret->type(e->type()); return ret; } else { if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } throw EvalError(env, e->loc(), "not an array expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not an array expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.e) return eval_array_lit(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } ArrayLit* l_in = eval_array_lit(env, l->in()); ArrayLit* ret = copy(env, l_in, true)->cast(); ret->flat(l_in->flat()); l->popbindings(); return ret; } } assert(false); return NULL; } Expression* eval_arrayaccess(EnvI& env, ArrayLit* al, const std::vector& dims, bool& success) { success = true; assert(al->dims() == dims.size()); IntVal realidx = 0; int realdim = 1; for (int i = 0; i < al->dims(); i++) realdim *= al->max(i) - al->min(i) + 1; for (int i = 0; i < al->dims(); i++) { IntVal ix = dims[i]; if (ix < al->min(i) || ix > al->max(i)) { success = false; Type t = al->type(); t.dim(0); if (t.isint()) return IntLit::a(0); if (t.isbool()) return constants().lit_false; if (t.isfloat()) return FloatLit::a(0.0); if (t.st() == Type::ST_SET || t.isbot()) { SetLit* ret = new SetLit(Location(), std::vector()); ret->type(t); return ret; } if (t.isstring()) return new StringLit(Location(), ""); throw EvalError(env, al->loc(), "Internal error: unexpected type in array access expression"); } realdim /= al->max(i) - al->min(i) + 1; realidx += (ix - al->min(i)) * realdim; } assert(realidx >= 0 && realidx <= al->size()); return (*al)[static_cast(realidx.toInt())]; } Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e, bool& success) { ArrayLit* al = eval_array_lit(env, e->v()); std::vector dims(e->idx().size()); for (unsigned int i = e->idx().size(); i--;) { dims[i] = eval_int(env, e->idx()[i]); } return eval_arrayaccess(env, al, dims, success); } Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e) { bool success; Expression* ret = eval_arrayaccess(env, e, success); if (success) return ret; else throw ResultUndefinedError(env, e->loc(), "array access out of bounds"); } IntSetVal* eval_intset(EnvI& env, Expression* e) { if (SetLit* sl = e->dyn_cast()) { if (sl->isv()) return sl->isv(); } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { SetLit* sl = e->cast(); std::vector vals(sl->v().size()); for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_int(env, sl->v()[i]); return IntSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of int expression"); break; case Expression::E_ARRAYLIT: { ArrayLit* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_int(env, (*al)[i]); return IntSetVal::a(vals); } break; case Expression::E_COMP: { Comprehension* c = e->cast(); std::vector a = eval_comp(env, c); return IntSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_id(env, e)->isv(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_intset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_intset(env, ite->e_then(i)); } return eval_intset(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isintset() && rhs->type().isintset()) { IntSetVal* v0 = eval_intset(env, lhs); IntSetVal* v1 = eval_intset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(ir0, ir1); return IntSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(ir0, ir1); return IntSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(ir0, ir1); Ranges::Inter i(ir0, ir1); Ranges::Diff, Ranges::Inter> sd(u, i); return IntSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(ir0, ir1); return IntSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } else if (lhs->type().isint() && rhs->type().isint()) { if (bo->op() != BOT_DOTDOT) throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); return IntSetVal::a(eval_int(env, lhs), eval_int(env, rhs)); } else { throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of int expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.s) return ce->decl()->_builtins.s(env, ce); if (ce->decl()->_builtins.e) return eval_intset(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntSetVal* ret = eval_intset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return NULL; } } FloatSetVal* eval_floatset(EnvI& env, Expression* e) { if (SetLit* sl = e->dyn_cast()) { if (sl->fsv()) return sl->fsv(); } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { SetLit* sl = e->cast(); std::vector vals(sl->v().size()); for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_float(env, sl->v()[i]); return FloatSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of float expression"); break; case Expression::E_ARRAYLIT: { ArrayLit* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_float(env, (*al)[i]); return FloatSetVal::a(vals); } break; case Expression::E_COMP: { Comprehension* c = e->cast(); std::vector a = eval_comp(env, c); return FloatSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_floatset(env, eval_id(env, e)); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_floatset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_floatset(env, ite->e_then(i)); } return eval_floatset(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isfloatset() && rhs->type().isfloatset()) { FloatSetVal* v0 = eval_floatset(env, lhs); FloatSetVal* v1 = eval_floatset(env, rhs); FloatSetRanges fr0(v0); FloatSetRanges fr1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(fr0, fr1); return FloatSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(fr0, fr1); return FloatSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(fr0, fr1); Ranges::Inter i(fr0, fr1); Ranges::Diff, Ranges::Inter> sd(u, i); return FloatSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(fr0, fr1); return FloatSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); } } else if (lhs->type().isfloat() && rhs->type().isfloat()) { if (bo->op() != BOT_DOTDOT) throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); return FloatSetVal::a(eval_float(env, lhs), eval_float(env, rhs)); } else { throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of float expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.e) return eval_floatset(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } FloatSetVal* ret = eval_floatset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return NULL; } } bool eval_bool(EnvI& env, Expression* e) { CallStackItem csi(env, e); try { if (BoolLit* bl = e->dyn_cast()) { return bl->v(); } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: assert(false); throw EvalError(env, e->loc(), "not a bool expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_bool(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_bool(env, ite->e_then(i)); } return eval_bool(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); Expression* lhs = bo->lhs(); if (lhs->type().bt() == Type::BT_TOP) lhs = eval_par(env, lhs); Expression* rhs = bo->rhs(); if (rhs->type().bt() == Type::BT_TOP) rhs = eval_par(env, rhs); if (bo->op() == BOT_EQ && (lhs->type().isopt() || rhs->type().isopt())) { if (lhs == constants().absent || rhs == constants().absent) return lhs == rhs; } if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } if (lhs->type().isbool() && rhs->type().isbool()) { try { switch (bo->op()) { case BOT_LE: return eval_bool(env, lhs) < eval_bool(env, rhs); case BOT_LQ: return eval_bool(env, lhs) <= eval_bool(env, rhs); case BOT_GR: return eval_bool(env, lhs) > eval_bool(env, rhs); case BOT_GQ: return eval_bool(env, lhs) >= eval_bool(env, rhs); case BOT_EQ: return eval_bool(env, lhs) == eval_bool(env, rhs); case BOT_NQ: return eval_bool(env, lhs) != eval_bool(env, rhs); case BOT_EQUIV: return eval_bool(env, lhs) == eval_bool(env, rhs); case BOT_IMPL: return (!eval_bool(env, lhs)) || eval_bool(env, rhs); case BOT_RIMPL: return (!eval_bool(env, rhs)) || eval_bool(env, lhs); case BOT_OR: return eval_bool(env, lhs) || eval_bool(env, rhs); case BOT_AND: return eval_bool(env, lhs) && eval_bool(env, rhs); case BOT_XOR: return eval_bool(env, lhs) ^ eval_bool(env, rhs); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isint() && rhs->type().isint()) { try { IntVal v0 = eval_int(env, lhs); IntVal v1 = eval_int(env, rhs); switch (bo->op()) { case BOT_LE: return v0 < v1; case BOT_LQ: return v0 <= v1; case BOT_GR: return v0 > v1; case BOT_GQ: return v0 >= v1; case BOT_EQ: return v0 == v1; case BOT_NQ: return v0 != v1; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isfloat() && rhs->type().isfloat()) { try { FloatVal v0 = eval_float(env, lhs); FloatVal v1 = eval_float(env, rhs); switch (bo->op()) { case BOT_LE: return v0 < v1; case BOT_LQ: return v0 <= v1; case BOT_GR: return v0 > v1; case BOT_GQ: return v0 >= v1; case BOT_EQ: return v0 == v1; case BOT_NQ: return v0 != v1; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isint() && rhs->type().isintset()) { try { IntVal v0 = eval_int(env, lhs); GCLock lock; IntSetVal* v1 = eval_intset(env, rhs); switch (bo->op()) { case BOT_IN: return v1->contains(v0); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isfloat() && rhs->type().isfloatset()) { try { FloatVal v0 = eval_float(env, lhs); GCLock lock; FloatSetVal* v1 = eval_floatset(env, rhs); switch (bo->op()) { case BOT_IN: return v1->contains(v0); default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().is_set() && rhs->type().is_set()) { try { GCLock lock; IntSetVal* v0 = eval_intset(env, lhs); IntSetVal* v1 = eval_intset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_LE: return Ranges::less(ir0, ir1); case BOT_LQ: return Ranges::lessEq(ir0, ir1); case BOT_GR: return Ranges::less(ir1, ir0); case BOT_GQ: return Ranges::lessEq(ir1, ir0); case BOT_EQ: return Ranges::equal(ir0, ir1); case BOT_NQ: return !Ranges::equal(ir0, ir1); case BOT_SUBSET: return Ranges::subset(ir0, ir1); case BOT_SUPERSET: return Ranges::subset(ir1, ir0); default: throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (lhs->type().isstring() && rhs->type().isstring()) { try { GCLock lock; std::string s0 = eval_string(env, lhs); std::string s1 = eval_string(env, rhs); switch (bo->op()) { case BOT_EQ: return s0 == s1; case BOT_NQ: return s0 != s1; case BOT_LE: return s0 < s1; case BOT_LQ: return s0 <= s1; case BOT_GR: return s0 > s1; case BOT_GQ: return s0 >= s1; default: throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } catch (ResultUndefinedError&) { return false; } } else if (bo->op() == BOT_EQ && lhs->type().isann()) { return Expression::equal(lhs, rhs); } else if (bo->op() == BOT_EQ && lhs->type().dim() > 0 && rhs->type().dim() > 0) { try { ArrayLit* al0 = eval_array_lit(env, lhs); ArrayLit* al1 = eval_array_lit(env, rhs); if (al0->size() != al1->size()) return false; for (unsigned int i = 0; i < al0->size(); i++) { if (!Expression::equal(eval_par(env, (*al0)[i]), eval_par(env, (*al1)[i]))) { return false; } } return true; } catch (ResultUndefinedError&) { return false; } } else { throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } bool v0 = eval_bool(env, uo->e()); switch (uo->op()) { case UOT_NOT: return !v0; default: assert(false); throw EvalError(env, e->loc(), "not a bool expression", uo->opToString()); } } break; case Expression::E_CALL: { try { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.b) return ce->decl()->_builtins.b(env, ce); if (ce->decl()->_builtins.e) return eval_bool(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } catch (ResultUndefinedError&) { return false; } } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); bool ret = true; for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { if (vdi->ann().contains(constants().ann.maybe_partial)) { ret = false; } else { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is false. if (!eval_bool(env, l->let()[i])) { if (l->let()[i]->ann().contains(constants().ann.maybe_partial)) { ret = false; } else { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } } ret = ret && eval_bool(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return false; } } catch (ResultUndefinedError&) { // undefined means false return false; } } IntSetVal* eval_boolset(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_SETLIT: { SetLit* sl = e->cast(); if (sl->isv()) return sl->isv(); std::vector vals(sl->v().size()); for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_bool(env, sl->v()[i]); return IntSetVal::a(vals); } case Expression::E_BOOLLIT: case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a set of bool expression"); break; case Expression::E_ARRAYLIT: { ArrayLit* al = e->cast(); std::vector vals(al->size()); for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_bool(env, (*al)[i]); return IntSetVal::a(vals); } break; case Expression::E_COMP: { Comprehension* c = e->cast(); std::vector a = eval_comp(env, c); return IntSetVal::a(a); } case Expression::E_ID: { GCLock lock; return eval_id(env, e)->isv(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_boolset(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_boolset(env, ite->e_then(i)); } return eval_boolset(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } Expression* lhs = eval_par(env, bo->lhs()); Expression* rhs = eval_par(env, bo->rhs()); if (lhs->type().isintset() && rhs->type().isintset()) { IntSetVal* v0 = eval_boolset(env, lhs); IntSetVal* v1 = eval_boolset(env, rhs); IntSetRanges ir0(v0); IntSetRanges ir1(v1); switch (bo->op()) { case BOT_UNION: { Ranges::Union u(ir0, ir1); return IntSetVal::ai(u); } case BOT_DIFF: { Ranges::Diff u(ir0, ir1); return IntSetVal::ai(u); } case BOT_SYMDIFF: { Ranges::Union u(ir0, ir1); Ranges::Inter i(ir0, ir1); Ranges::Diff, Ranges::Inter> sd(u, i); return IntSetVal::ai(sd); } case BOT_INTERSECT: { Ranges::Inter u(ir0, ir1); return IntSetVal::ai(u); } default: throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); } } else if (lhs->type().isbool() && rhs->type().isbool()) { if (bo->op() != BOT_DOTDOT) throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); return IntSetVal::a(eval_bool(env, lhs), eval_bool(env, rhs)); } else { throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a set of bool expression"); } case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.s) return ce->decl()->_builtins.s(env, ce); if (ce->decl()->_builtins.e) return eval_boolset(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntSetVal* ret = eval_boolset(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return NULL; } } IntVal eval_int(EnvI& env, Expression* e) { if (e->type().isbool()) { return eval_bool(env, e); } if (IntLit* il = e->dyn_cast()) { return il->v(); } CallStackItem csi(env, e); try { switch (e->eid()) { case Expression::E_FLOATLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not an integer expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_int(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_int(env, ite->e_then(i)); } return eval_int(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } IntVal v0 = eval_int(env, bo->lhs()); IntVal v1 = eval_int(env, bo->rhs()); switch (bo->op()) { case BOT_PLUS: return v0 + v1; case BOT_MINUS: return v0 - v1; case BOT_MULT: return v0 * v1; case BOT_POW: return v0.pow(v1); case BOT_IDIV: if (v1 == 0) throw ResultUndefinedError(env, e->loc(), "division by zero"); return v0 / v1; case BOT_MOD: if (v1 == 0) throw ResultUndefinedError(env, e->loc(), "division by zero"); return v0 % v1; default: throw EvalError(env, e->loc(), "not an integer expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } IntVal v0 = eval_int(env, uo->e()); switch (uo->op()) { case UOT_PLUS: return v0; case UOT_MINUS: return -v0; default: throw EvalError(env, e->loc(), "not an integer expression", uo->opToString()); } } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.i) return ce->decl()->_builtins.i(env, ce); if (ce->decl()->_builtins.e) return eval_int(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } IntVal ret = eval_int(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return 0; } } catch (ArithmeticError& err) { throw EvalError(env, e->loc(), err.msg()); } } FloatVal eval_float(EnvI& env, Expression* e) { CallStackItem csi(env, e); try { if (e->type().isint()) { return FloatVal(eval_int(env, e).toInt()); } else if (e->type().isbool()) { return eval_bool(env, e); } if (FloatLit* fl = e->dyn_cast()) { return fl->v(); } CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a float expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_float(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_float(env, ite->e_then(i)); } return eval_float(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } FloatVal v0 = eval_float(env, bo->lhs()); FloatVal v1 = eval_float(env, bo->rhs()); switch (bo->op()) { case BOT_PLUS: return v0 + v1; case BOT_MINUS: return v0 - v1; case BOT_MULT: return v0 * v1; case BOT_POW: return std::pow(v0.toDouble(), v1.toDouble()); case BOT_DIV: if (v1 == 0.0) throw ResultUndefinedError(env, e->loc(), "division by zero"); return v0 / v1; default: throw EvalError(env, e->loc(), "not a float expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } FloatVal v0 = eval_float(env, uo->e()); switch (uo->op()) { case UOT_PLUS: return v0; case UOT_MINUS: return -v0; default: throw EvalError(env, e->loc(), "not a float expression", uo->opToString()); } } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.f) return ce->decl()->_builtins.f(env, ce); if (ce->decl()->_builtins.e) return eval_float(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } FloatVal ret = eval_float(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return 0.0; } } catch (ArithmeticError& err) { throw EvalError(env, e->loc(), err.msg()); } } std::string eval_string(EnvI& env, Expression* e) { CallStackItem csi(env, e); switch (e->eid()) { case Expression::E_STRINGLIT: return e->cast()->v().str(); case Expression::E_FLOATLIT: case Expression::E_INTLIT: case Expression::E_BOOLLIT: case Expression::E_ANON: case Expression::E_TIID: case Expression::E_SETLIT: case Expression::E_ARRAYLIT: case Expression::E_COMP: case Expression::E_VARDECL: case Expression::E_TI: throw EvalError(env, e->loc(), "not a string expression"); break; case Expression::E_ID: { GCLock lock; return eval_id(env, e)->v().str(); } break; case Expression::E_ARRAYACCESS: { GCLock lock; return eval_string(env, eval_arrayaccess(env, e->cast())); } break; case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (eval_bool(env, ite->e_if(i))) return eval_string(env, ite->e_then(i)); } return eval_string(env, ite->e_else()); } break; case Expression::E_BINOP: { BinOp* bo = e->cast(); if (bo->decl() && bo->decl()->e()) { return eval_call(env, bo); } std::string v0 = eval_string(env, bo->lhs()); std::string v1 = eval_string(env, bo->rhs()); switch (bo->op()) { case BOT_PLUSPLUS: return v0 + v1; default: throw EvalError(env, e->loc(), "not a string expression", bo->opToString()); } } break; case Expression::E_UNOP: { UnOp* uo = e->cast(); if (uo->decl() && uo->decl()->e()) { return eval_call(env, uo); } throw EvalError(env, e->loc(), "not a string expression"); } break; case Expression::E_CALL: { Call* ce = e->cast(); if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); if (ce->decl()->_builtins.str) return ce->decl()->_builtins.str(env, ce); if (ce->decl()->_builtins.e) return eval_string(env, ce->decl()->_builtins.e(env, ce)); if (ce->decl()->e() == NULL) throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); return eval_call(env, ce); } break; case Expression::E_LET: { Let* l = e->cast(); l->pushbindings(); for (unsigned int i = 0; i < l->let().size(); i++) { // Evaluate all variable declarations if (VarDecl* vdi = l->let()[i]->dyn_cast()) { vdi->e(eval_par(env, vdi->e())); if (vdi->ti()->domain()) { if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { throw ResultUndefinedError(env, l->let()[i]->loc(), "domain constraint in let failed"); } } } else { // This is a constraint item. Since the let is par, // it can only be a par bool expression. If it evaluates // to false, it means that the value of this let is undefined. if (!eval_bool(env, l->let()[i])) { throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); } } } std::string ret = eval_string(env, l->in()); l->popbindings(); return ret; } break; default: assert(false); return NULL; } } Expression* eval_par(EnvI& env, Expression* e) { if (e == NULL) return NULL; switch (e->eid()) { case Expression::E_ANON: case Expression::E_TIID: { return e; } case Expression::E_COMP: if (e->cast()->set()) return EvalSetLit::e(env, e); // fall through case Expression::E_ARRAYLIT: { ArrayLit* al = eval_array_lit(env, e); std::vector args(al->size()); for (unsigned int i = al->size(); i--;) args[i] = eval_par(env, (*al)[i]); std::vector> dims(al->dims()); for (unsigned int i = al->dims(); i--;) { dims[i].first = al->min(i); dims[i].second = al->max(i); } ArrayLit* ret = new ArrayLit(al->loc(), args, dims); Type t = al->type(); if (t.isbot() && ret->size() > 0) { t.bt((*ret)[0]->type().bt()); } ret->type(t); return ret; } case Expression::E_VARDECL: { VarDecl* vd = e->cast(); throw EvalError(env, vd->loc(), "cannot evaluate variable declaration", vd->id()->v()); } case Expression::E_TI: { TypeInst* t = e->cast(); ASTExprVec r; if (t->ranges().size() > 0) { std::vector rv(t->ranges().size()); for (unsigned int i = t->ranges().size(); i--;) rv[i] = static_cast(eval_par(env, t->ranges()[i])); r = ASTExprVec(rv); } return new TypeInst(Location(), t->type(), r, eval_par(env, t->domain())); } case Expression::E_ID: { if (e == constants().absent) return e; Id* id = e->cast(); if (id->decl() == NULL) throw EvalError(env, e->loc(), "undefined identifier", id->v()); if (id->decl()->ti()->domain()) { if (BoolLit* bl = id->decl()->ti()->domain()->dyn_cast()) return bl; if (id->decl()->ti()->type().isint()) { if (SetLit* sl = id->decl()->ti()->domain()->dyn_cast()) { if (sl->isv() && sl->isv()->min() == sl->isv()->max()) { return IntLit::a(sl->isv()->min()); } } } else if (id->decl()->ti()->type().isfloat()) { if (id->decl()->ti()->domain()) { FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); if (fsv->min() == fsv->max()) { return FloatLit::a(fsv->min()); } } } } if (id->decl()->e() == NULL) { return id; } else { return eval_par(env, id->decl()->e()); } } case Expression::E_STRINGLIT: return e; default: { if (e->type().dim() != 0) { ArrayLit* al = eval_array_lit(env, e); std::vector args(al->size()); for (unsigned int i = al->size(); i--;) args[i] = eval_par(env, (*al)[i]); std::vector> dims(al->dims()); for (unsigned int i = al->dims(); i--;) { dims[i].first = al->min(i); dims[i].second = al->max(i); } ArrayLit* ret = new ArrayLit(al->loc(), args, dims); Type t = al->type(); if ((t.bt() == Type::BT_BOT || t.bt() == Type::BT_TOP) && ret->size() > 0) { t.bt((*ret)[0]->type().bt()); } ret->type(t); return ret; } if (e->type().ispar()) { if (e->type().isintset()) { return EvalSetLit::e(env, e); } if (e->type().isfloatset()) { return EvalFloatSetLit::e(env, e); } if (e->type().isboolset()) { return EvalBoolSetLit::e(env, e); } if (e->type() == Type::parint()) { return EvalIntLit::e(env, e); } if (e->type() == Type::parbool()) { return EvalBoolLit::e(env, e); } if (e->type() == Type::parfloat()) { return EvalFloatLit::e(env, e); } if (e->type() == Type::parstring()) { return EvalStringLit::e(env, e); } } switch (e->eid()) { case Expression::E_ITE: { ITE* ite = e->cast(); for (int i = 0; i < ite->size(); i++) { if (ite->e_if(i)->type() == Type::parbool()) { if (eval_bool(env, ite->e_if(i))) return eval_par(env, ite->e_then(i)); } else { std::vector e_ifthen(ite->size() * 2); for (int i = 0; i < ite->size(); i++) { e_ifthen[2 * i] = eval_par(env, ite->e_if(i)); e_ifthen[2 * i + 1] = eval_par(env, ite->e_then(i)); } ITE* n_ite = new ITE(ite->loc(), e_ifthen, eval_par(env, ite->e_else())); n_ite->type(ite->type()); return n_ite; } } return eval_par(env, ite->e_else()); } case Expression::E_CALL: { Call* c = e->cast(); if (c->decl()) { if (c->decl()->_builtins.e) { return eval_par(env, c->decl()->_builtins.e(env, c)); } else { if (c->decl()->e() == NULL) { if (c->id() == "deopt" && Expression::equal(c->arg(0), constants().absent)) throw ResultUndefinedError(env, e->loc(), "deopt(<>) is undefined"); return c; } return eval_call(env, c); } } else { std::vector args(c->n_args()); for (unsigned int i = 0; i < args.size(); i++) { args[i] = eval_par(env, c->arg(i)); } Call* nc = new Call(c->loc(), c->id(), args); nc->type(c->type()); return nc; } } case Expression::E_BINOP: { BinOp* bo = e->cast(); BinOp* nbo = new BinOp(e->loc(), eval_par(env, bo->lhs()), bo->op(), eval_par(env, bo->rhs())); nbo->type(bo->type()); return nbo; } case Expression::E_UNOP: { UnOp* uo = e->cast(); UnOp* nuo = new UnOp(e->loc(), uo->op(), eval_par(env, uo->e())); nuo->type(uo->type()); return nuo; } case Expression::E_ARRAYACCESS: { ArrayAccess* aa = e->cast(); for (unsigned int i = 0; i < aa->idx().size(); i++) { if (!aa->idx()[i]->type().ispar()) { std::vector idx(aa->idx().size()); for (unsigned int j = 0; j < aa->idx().size(); j++) { idx[j] = eval_par(env, aa->idx()[j]); } ArrayAccess* aa_new = new ArrayAccess(e->loc(), eval_par(env, aa->v()), idx); aa_new->type(aa->type()); return aa_new; } } return eval_par(env, eval_arrayaccess(env, aa)); } default: return e; } } } } class ComputeIntBounds : public EVisitor { public: typedef std::pair Bounds; std::vector _bounds; bool valid; EnvI& env; ComputeIntBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isann()) return false; if (e->isa()) return false; if (e->type().dim() > 0) return false; if (e->type().ispar()) { if (e->type().isint()) { IntVal v = eval_int(env, e); _bounds.push_back(Bounds(v, v)); } else { valid = false; } return false; } if (e->type().isint()) { if (ITE* ite = e->dyn_cast()) { Bounds itebounds(IntVal::infinity(), -IntVal::infinity()); for (int i = 0; i < ite->size(); i++) { if (ite->e_if(i)->type().ispar() && ite->e_if(i)->type().cv() == Type::CV_NO) { if (eval_bool(env, ite->e_if(i))) { BottomUpIterator cbi(*this); cbi.run(ite->e_then(i)); Bounds& back = _bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } } else { BottomUpIterator cbi(*this); cbi.run(ite->e_then(i)); Bounds back = _bounds.back(); _bounds.pop_back(); itebounds.first = std::min(itebounds.first, back.first); itebounds.second = std::max(itebounds.second, back.second); } } BottomUpIterator cbi(*this); cbi.run(ite->e_else()); Bounds& back = _bounds.back(); back.first = std::min(itebounds.first, back.first); back.second = std::max(itebounds.second, back.second); return false; } return true; } return false; } /// Visit integer literal void vIntLit(const IntLit& i) { _bounds.push_back(Bounds(i.v(), i.v())); } /// Visit floating point literal void vFloatLit(const FloatLit&) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit Boolean literal void vBoolLit(const BoolLit&) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit set literal void vSetLit(const SetLit&) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit string literal void vStringLit(const StringLit&) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit identifier void vId(const Id& id) { VarDecl* vd = id.decl(); while (vd->flat() && vd->flat() != vd) vd = vd->flat(); if (vd->ti()->domain()) { GCLock lock; IntSetVal* isv = eval_intset(env, vd->ti()->domain()); if (isv->size() == 0) { valid = false; _bounds.push_back(Bounds(0, 0)); } else { _bounds.push_back(Bounds(isv->min(0), isv->max(isv->size() - 1))); } } else { if (vd->e()) { BottomUpIterator cbi(*this); cbi.run(vd->e()); } else { _bounds.push_back(Bounds(-IntVal::infinity(), IntVal::infinity())); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& v) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit array literal void vArrayLit(const ArrayLit& al) {} /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); i--;) { _bounds.pop_back(); if (!aa.idx()[i]->type().ispar()) { parAccess = false; } } if (Id* id = aa.v()->dyn_cast()) { while (id->decl()->e() && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && id->decl()->e()) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain()) { GCLock lock; IntSetVal* isv = eval_intset(env, id->decl()->ti()->domain()); if (isv->size() > 0) { _bounds.push_back(Bounds(isv->min(0), isv->max(isv->size() - 1))); return; } } } valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit if-then-else void vITE(const ITE& ite) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit binary operator void vBinOp(const BinOp& bo) { Bounds b1 = _bounds.back(); _bounds.pop_back(); Bounds b0 = _bounds.back(); _bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; _bounds.push_back(Bounds(0, 0)); } else { switch (bo.op()) { case BOT_PLUS: _bounds.push_back(Bounds(b0.first + b1.first, b0.second + b1.second)); break; case BOT_MINUS: _bounds.push_back(Bounds(b0.first - b1.second, b0.second - b1.first)); break; case BOT_MULT: { IntVal x0 = b0.first * b1.first; IntVal x1 = b0.first * b1.second; IntVal x2 = b0.second * b1.first; IntVal x3 = b0.second * b1.second; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(Bounds(m, n)); } break; case BOT_IDIV: { IntVal b0f = b0.first == 0 ? 1 : b0.first; IntVal b0s = b0.second == 0 ? -1 : b0.second; IntVal b1f = b1.first == 0 ? 1 : b1.first; IntVal b1s = b1.second == 0 ? -1 : b1.second; IntVal x0 = b0f / b1f; IntVal x1 = b0f / b1s; IntVal x2 = b0s / b1f; IntVal x3 = b0s / b1s; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(Bounds(m, n)); } break; case BOT_MOD: { IntVal b0f = b0.first == 0 ? 1 : b0.first; IntVal b0s = b0.second == 0 ? -1 : b0.second; IntVal b1f = b1.first == 0 ? 1 : b1.first; IntVal b1s = b1.second == 0 ? -1 : b1.second; IntVal x0 = b0f % b1f; IntVal x1 = b0f % b1s; IntVal x2 = b0s % b1f; IntVal x3 = b0s % b1s; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(Bounds(m, n)); } break; case BOT_POW: { IntVal exp_min = std::min(0, b1.first); IntVal exp_max = std::min(0, b1.second); IntVal x0 = b0.first.pow(exp_min); IntVal x1 = b0.first.pow(exp_max); IntVal x2 = b0.second.pow(exp_min); IntVal x3 = b0.second.pow(exp_max); IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(Bounds(m, n)); } break; case BOT_DIV: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_UNION: case BOT_DIFF: case BOT_SYMDIFF: case BOT_INTERSECT: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; _bounds.push_back(Bounds(0, 0)); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { switch (uo.op()) { case UOT_PLUS: break; case UOT_MINUS: _bounds.back().first = -_bounds.back().first; _bounds.back().second = -_bounds.back().second; std::swap(_bounds.back().first, _bounds.back().second); break; case UOT_NOT: valid = false; } } /// Visit call void vCall(Call& c) { if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { bool le = c.id() == constants().ids.lin_exp; ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : NULL; if (c.arg(le ? 1 : 0)->type().isopt()) { valid = false; _bounds.push_back(Bounds(0, 0)); return; } ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); if (le) { _bounds.pop_back(); // remove constant (third arg) from stack } IntVal d = le ? c.arg(2)->cast()->v() : 0; int stacktop = static_cast(_bounds.size()); for (unsigned int i = al->size(); i--;) { BottomUpIterator cbi(*this); cbi.run((*al)[i]); if (!valid) { for (unsigned int j = al->size() - 1; j > i; j--) _bounds.pop_back(); return; } } assert(stacktop + al->size() == _bounds.size()); IntVal lb = d; IntVal ub = d; for (unsigned int i = 0; i < al->size(); i++) { Bounds b = _bounds.back(); _bounds.pop_back(); IntVal cv = le ? eval_int(env, (*coeff)[i]) : 1; if (cv > 0) { if (b.first.isFinite()) { if (lb.isFinite()) { lb += cv * b.first; } } else { lb = b.first; } if (b.second.isFinite()) { if (ub.isFinite()) { ub += cv * b.second; } } else { ub = b.second; } } else { if (b.second.isFinite()) { if (lb.isFinite()) { lb += cv * b.second; } } else { lb = -b.second; } if (b.first.isFinite()) { if (ub.isFinite()) { ub += cv * b.first; } } else { ub = -b.first; } } } _bounds.push_back(Bounds(lb, ub)); } else if (c.id() == "card") { if (IntSetVal* isv = compute_intset_bounds(env, c.arg(0))) { IntSetRanges isr(isv); _bounds.push_back(Bounds(0, Ranges::cardinality(isr))); } else { valid = false; _bounds.push_back(Bounds(0, 0)); } } else if (c.id() == "int_times") { Bounds b1 = _bounds.back(); _bounds.pop_back(); Bounds b0 = _bounds.back(); _bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; _bounds.push_back(Bounds(0, 0)); } else { IntVal x0 = b0.first * b1.first; IntVal x1 = b0.first * b1.second; IntVal x2 = b0.second * b1.first; IntVal x3 = b0.second * b1.second; IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(Bounds(m, n)); } } else if (c.id() == constants().ids.bool2int) { _bounds.push_back(Bounds(0, 1)); } else if (c.id() == "abs") { Bounds b0 = _bounds.back(); if (b0.first < 0) { _bounds.pop_back(); if (b0.second < 0) _bounds.push_back(Bounds(-b0.second, -b0.first)); else _bounds.push_back(Bounds(0, std::max(-b0.first, b0.second))); } } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.n_args(); i++) { if (c.arg(i)->type().isint()) { assert(_bounds.size() > 0); _bounds.pop_back(); } } IntSetVal* isv = eval_intset(env, c.decl()->ti()->domain()); _bounds.push_back(Bounds(isv->min(), isv->max())); } else { valid = false; _bounds.push_back(Bounds(0, 0)); } } /// Visit let void vLet(const Let& l) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; _bounds.push_back(Bounds(0, 0)); } /// Visit TIId void vTIId(const TIId& e) { valid = false; _bounds.push_back(Bounds(0, 0)); } }; IntBounds compute_int_bounds(EnvI& env, Expression* e) { try { ComputeIntBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) { assert(cb._bounds.size() == 1); return IntBounds(cb._bounds.back().first, cb._bounds.back().second, true); } else { return IntBounds(0, 0, false); } } catch (ResultUndefinedError&) { return IntBounds(0, 0, false); } } class ComputeFloatBounds : public EVisitor { protected: typedef std::pair FBounds; public: std::vector _bounds; bool valid; EnvI& env; ComputeFloatBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isann()) return false; if (e->isa()) return false; if (e->type().dim() > 0) return false; if (e->type().ispar()) { if (e->type().isfloat()) { FloatVal v = eval_float(env, e); _bounds.push_back(FBounds(v, v)); } return false; } else { return e->type().isfloat(); } } /// Visit integer literal void vIntLit(const IntLit& i) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit floating point literal void vFloatLit(const FloatLit& f) { _bounds.push_back(FBounds(f.v(), f.v())); } /// Visit Boolean literal void vBoolLit(const BoolLit&) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit set literal void vSetLit(const SetLit&) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit string literal void vStringLit(const StringLit&) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit identifier void vId(const Id& id) { VarDecl* vd = id.decl(); while (vd->flat() && vd->flat() != vd) vd = vd->flat(); if (vd->ti()->domain()) { FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); _bounds.push_back(FBounds(fsv->min(), fsv->max())); } else { if (vd->e()) { BottomUpIterator cbi(*this); cbi.run(vd->e()); } else { valid = false; _bounds.push_back(FBounds(0, 0)); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& v) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit array literal void vArrayLit(const ArrayLit& al) {} /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); i--;) { if (!aa.idx()[i]->type().ispar()) { parAccess = false; } } if (Id* id = aa.v()->dyn_cast()) { while (id->decl()->e() && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && id->decl()->e()) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain()) { FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); FBounds b(fsv->min(), fsv->max()); _bounds.push_back(b); return; } } valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit if-then-else void vITE(const ITE& ite) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit binary operator void vBinOp(const BinOp& bo) { FBounds b1 = _bounds.back(); _bounds.pop_back(); FBounds b0 = _bounds.back(); _bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } else { switch (bo.op()) { case BOT_PLUS: _bounds.push_back(FBounds(b0.first + b1.first, b0.second + b1.second)); break; case BOT_MINUS: _bounds.push_back(FBounds(b0.first - b1.second, b0.second - b1.first)); break; case BOT_MULT: { FloatVal x0 = b0.first * b1.first; FloatVal x1 = b0.first * b1.second; FloatVal x2 = b0.second * b1.first; FloatVal x3 = b0.second * b1.second; FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(FBounds(m, n)); } break; case BOT_POW: { FloatVal x0 = std::pow(b0.first.toDouble(), b1.first.toDouble()); FloatVal x1 = std::pow(b0.first.toDouble(), b1.second.toDouble()); FloatVal x2 = std::pow(b0.second.toDouble(), b1.first.toDouble()); FloatVal x3 = std::pow(b0.second.toDouble(), b1.second.toDouble()); FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(FBounds(m, n)); } break; case BOT_DIV: case BOT_IDIV: case BOT_MOD: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_UNION: case BOT_DIFF: case BOT_SYMDIFF: case BOT_INTERSECT: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { switch (uo.op()) { case UOT_PLUS: break; case UOT_MINUS: _bounds.back().first = -_bounds.back().first; _bounds.back().second = -_bounds.back().second; break; case UOT_NOT: valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } } /// Visit call void vCall(Call& c) { if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { bool le = c.id() == constants().ids.lin_exp; ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : NULL; if (le) { _bounds.pop_back(); // remove constant (third arg) from stack } if (c.arg(le ? 1 : 0)->type().isopt()) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); return; } ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); FloatVal d = le ? c.arg(2)->cast()->v() : 0.0; int stacktop = static_cast(_bounds.size()); for (unsigned int i = al->size(); i--;) { BottomUpIterator cbi(*this); cbi.run((*al)[i]); if (!valid) return; } assert(stacktop + al->size() == _bounds.size()); FloatVal lb = d; FloatVal ub = d; for (unsigned int i = 0; i < (*al).size(); i++) { FBounds b = _bounds.back(); _bounds.pop_back(); FloatVal cv = le ? eval_float(env, (*coeff)[i]) : 1.0; if (cv > 0) { if (b.first.isFinite()) { if (lb.isFinite()) { lb += cv * b.first; } } else { lb = b.first; } if (b.second.isFinite()) { if (ub.isFinite()) { ub += cv * b.second; } } else { ub = b.second; } } else { if (b.second.isFinite()) { if (lb.isFinite()) { lb += cv * b.second; } } else { lb = -b.second; } if (b.first.isFinite()) { if (ub.isFinite()) { ub += cv * b.first; } } else { ub = -b.first; } } } _bounds.push_back(FBounds(lb, ub)); } else if (c.id() == "float_times") { BottomUpIterator cbi(*this); cbi.run(c.arg(0)); cbi.run(c.arg(1)); FBounds b1 = _bounds.back(); _bounds.pop_back(); FBounds b0 = _bounds.back(); _bounds.pop_back(); if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || !b0.second.isFinite()) { valid = false; _bounds.push_back(FBounds(0, 0)); } else { FloatVal x0 = b0.first * b1.first; FloatVal x1 = b0.first * b1.second; FloatVal x2 = b0.second * b1.first; FloatVal x3 = b0.second * b1.second; FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); _bounds.push_back(FBounds(m, n)); } } else if (c.id() == "int2float") { ComputeIntBounds ib(env); BottomUpIterator cbi(ib); cbi.run(c.arg(0)); if (!ib.valid) valid = false; ComputeIntBounds::Bounds result = ib._bounds.back(); if (!result.first.isFinite() || !result.second.isFinite()) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } else { _bounds.push_back(FBounds(static_cast(result.first.toInt()), static_cast(result.second.toInt()))); } } else if (c.id() == "abs") { BottomUpIterator cbi(*this); cbi.run(c.arg(0)); FBounds b0 = _bounds.back(); if (b0.first < 0) { _bounds.pop_back(); if (b0.second < 0) _bounds.push_back(FBounds(-b0.second, -b0.first)); else _bounds.push_back(FBounds(0.0, std::max(-b0.first, b0.second))); } } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.n_args(); i++) { if (c.arg(i)->type().isfloat()) { assert(_bounds.size() > 0); _bounds.pop_back(); } } FloatSetVal* fsv = eval_floatset(env, c.decl()->ti()->domain()); _bounds.push_back(FBounds(fsv->min(), fsv->max())); } else { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } } /// Visit let void vLet(const Let& l) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } /// Visit TIId void vTIId(const TIId& e) { valid = false; _bounds.push_back(FBounds(0.0, 0.0)); } }; FloatBounds compute_float_bounds(EnvI& env, Expression* e) { try { ComputeFloatBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) { assert(cb._bounds.size() > 0); return FloatBounds(cb._bounds.back().first, cb._bounds.back().second, true); } else { return FloatBounds(0.0, 0.0, false); } } catch (ResultUndefinedError&) { return FloatBounds(0.0, 0.0, false); } } class ComputeIntSetBounds : public EVisitor { public: std::vector _bounds; bool valid; EnvI& env; ComputeIntSetBounds(EnvI& env0) : valid(true), env(env0) {} bool enter(Expression* e) { if (e->type().isann()) return false; if (e->isa()) return false; if (e->type().dim() > 0) return false; if (!e->type().isintset()) return false; if (e->type().ispar()) { _bounds.push_back(eval_intset(env, e)); return false; } else { return true; } } /// Visit set literal void vSetLit(const SetLit& sl) { assert(sl.type().isvar()); assert(sl.isv() == NULL); IntSetVal* isv = IntSetVal::a(); for (unsigned int i = 0; i < sl.v().size(); i++) { IntSetRanges i0(isv); IntBounds ib = compute_int_bounds(env, sl.v()[i]); if (!ib.valid || !ib.l.isFinite() || !ib.u.isFinite()) { valid = false; _bounds.push_back(NULL); return; } Ranges::Const cr(ib.l, ib.u); Ranges::Union> u(i0, cr); isv = IntSetVal::ai(u); } _bounds.push_back(isv); } /// Visit identifier void vId(const Id& id) { if (id.decl()->ti()->domain() && !id.decl()->ti()->domain()->isa()) { _bounds.push_back(eval_intset(env, id.decl()->ti()->domain())); } else { if (id.decl()->e()) { BottomUpIterator cbi(*this); cbi.run(id.decl()->e()); } else { valid = false; _bounds.push_back(NULL); } } } /// Visit anonymous variable void vAnonVar(const AnonVar& v) { valid = false; _bounds.push_back(NULL); } /// Visit array access void vArrayAccess(ArrayAccess& aa) { bool parAccess = true; for (unsigned int i = aa.idx().size(); i--;) { if (!aa.idx()[i]->type().ispar()) { parAccess = false; break; } } if (Id* id = aa.v()->dyn_cast()) { while (id->decl()->e() && id->decl()->e()->isa()) { id = id->decl()->e()->cast(); } if (parAccess && id->decl()->e()) { bool success; Expression* e = eval_arrayaccess(env, &aa, success); if (success) { BottomUpIterator cbi(*this); cbi.run(e); return; } } if (id->decl()->ti()->domain()) { _bounds.push_back(eval_intset(env, id->decl()->ti()->domain())); return; } } valid = false; _bounds.push_back(NULL); } /// Visit array comprehension void vComprehension(const Comprehension& c) { valid = false; _bounds.push_back(NULL); } /// Visit if-then-else void vITE(const ITE& ite) { valid = false; _bounds.push_back(NULL); } /// Visit binary operator void vBinOp(const BinOp& bo) { if (bo.op() == BOT_DOTDOT) { IntBounds lb = compute_int_bounds(env, bo.lhs()); IntBounds ub = compute_int_bounds(env, bo.rhs()); valid = valid && lb.valid && ub.valid; _bounds.push_back(IntSetVal::a(lb.l, ub.u)); } else { IntSetVal* b1 = _bounds.back(); _bounds.pop_back(); IntSetVal* b0 = _bounds.back(); _bounds.pop_back(); switch (bo.op()) { case BOT_INTERSECT: case BOT_UNION: { IntSetRanges b0r(b0); IntSetRanges b1r(b1); Ranges::Union u(b0r, b1r); _bounds.push_back(IntSetVal::ai(u)); } break; case BOT_DIFF: { _bounds.push_back(b0); } break; case BOT_SYMDIFF: valid = false; _bounds.push_back(NULL); break; case BOT_PLUS: case BOT_MINUS: case BOT_MULT: case BOT_POW: case BOT_DIV: case BOT_IDIV: case BOT_MOD: case BOT_LE: case BOT_LQ: case BOT_GR: case BOT_GQ: case BOT_EQ: case BOT_NQ: case BOT_IN: case BOT_SUBSET: case BOT_SUPERSET: case BOT_PLUSPLUS: case BOT_EQUIV: case BOT_IMPL: case BOT_RIMPL: case BOT_OR: case BOT_AND: case BOT_XOR: case BOT_DOTDOT: valid = false; _bounds.push_back(NULL); } } } /// Visit unary operator void vUnOp(const UnOp& uo) { valid = false; _bounds.push_back(NULL); } /// Visit call void vCall(Call& c) { if (valid && (c.id() == "set_intersect" || c.id() == "set_union")) { IntSetVal* b0 = _bounds.back(); _bounds.pop_back(); IntSetVal* b1 = _bounds.back(); _bounds.pop_back(); IntSetRanges b0r(b0); IntSetRanges b1r(b1); Ranges::Union u(b0r, b1r); _bounds.push_back(IntSetVal::ai(u)); } else if (valid && c.id() == "set_diff") { IntSetVal* b0 = _bounds.back(); _bounds.pop_back(); _bounds.pop_back(); // don't need bounds of right hand side _bounds.push_back(b0); } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { for (int i = 0; i < c.n_args(); i++) { if (c.arg(i)->type().isintset()) { assert(_bounds.size() > 0); _bounds.pop_back(); } } IntSetVal* fsv = eval_intset(env, c.decl()->ti()->domain()); _bounds.push_back(fsv); } else { valid = false; _bounds.push_back(NULL); } } /// Visit let void vLet(const Let& l) { valid = false; _bounds.push_back(NULL); } /// Visit variable declaration void vVarDecl(const VarDecl& vd) { valid = false; _bounds.push_back(NULL); } /// Visit annotation void vAnnotation(const Annotation& e) { valid = false; _bounds.push_back(NULL); } /// Visit type inst void vTypeInst(const TypeInst& e) { valid = false; _bounds.push_back(NULL); } /// Visit TIId void vTIId(const TIId& e) { valid = false; _bounds.push_back(NULL); } }; IntSetVal* compute_intset_bounds(EnvI& env, Expression* e) { try { ComputeIntSetBounds cb(env); BottomUpIterator cbi(cb); cbi.run(e); if (cb.valid) return cb._bounds.back(); else return NULL; } catch (ResultUndefinedError&) { return NULL; } } Expression* follow_id(Expression* e) { for (;;) { if (e == NULL) return NULL; if (e->eid() == Expression::E_ID && e != constants().absent) { e = e->cast()->decl()->e(); } else { return e; } } } Expression* follow_id_to_decl(Expression* e) { for (;;) { if (e == NULL) return NULL; if (e == constants().absent) return e; switch (e->eid()) { case Expression::E_ID: e = e->cast()->decl(); break; case Expression::E_VARDECL: if (e->cast()->e() && e->cast()->e()->isa()) e = e->cast()->e(); else return e; break; default: return e; } } } Expression* follow_id_to_value(Expression* e) { Expression* decl = follow_id_to_decl(e); if (VarDecl* vd = decl->dyn_cast()) { if (vd->e() && vd->e()->type().ispar()) return vd->e(); return vd->id(); } else { return decl; } } } // namespace MiniZinc