📄 expr.cxx
字号:
CYG_PRECONDITION_CLASSC(expr); CYG_PRECONDITIONC((CdlExprOp_Invalid == token) || (CdlExprOp_And == token)); int current_subexpr = expr->first_subexpression; initialise_tokenisation(data, index); parse_expression(expr); if (CdlExprOp_And == token) { CdlSubexpression subexpr; subexpr.op = CdlExprOp_And; subexpr.lhs_index = current_subexpr; subexpr.rhs_index = expr->first_subexpression; push_subexpression(expr, subexpr); } token = token_to_expr_op(); index = token_start; token_end = current_index; CYG_REPORT_RETURN();}//}}}//}}}//{{{ Expression Evaluation // ----------------------------------------------------------------------------// Expression evaluation. This always happens in the context of a// particular toplevel. The parsed expression is held in what amounts// to a simple tree, so evaluation involves some recursion and a big// switch statement.static voidevaluate_subexpr(CdlEvalContext& context, CdlExpression expr, int subexpr_index, CdlSimpleValue& result) throw(CdlEvalException, std::bad_alloc){ CYG_REPORT_FUNCNAME("evaluate_subexpr"); CYG_REPORT_FUNCARG2XV(expr, subexpr_index); CYG_ASSERTC((subexpr_index >= 0) && ((unsigned int)subexpr_index < expr->sub_expressions.size())); const CdlSubexpression& subexpr = expr->sub_expressions[subexpr_index]; switch(subexpr.op) { case CdlExprOp_StringConstant : case CdlExprOp_IntegerConstant : case CdlExprOp_DoubleConstant : { result = subexpr.constants; break; } case CdlExprOp_Reference : { // This expression may be happening in the context of a particular // property. If so then the destination may or may not be resolved, // and this is significant in the context of loading and unloading. // Alternatively this expression may be being evaluated inside // some Tcl code, with no particular context. CdlNode destination = 0; if (0 != context.property) { // There is a property, use the bound/unbound reference. destination = expr->references[subexpr.reference_index].get_destination(); } else { // The destination name can be retrieved, but we still need some // way of resolving it. if (0 != context.toplevel) { std::string destination_name = expr->references[subexpr.reference_index].get_destination_name(); destination = context.toplevel->lookup(destination_name); } } if (0 == destination) { // There are two ways of handling this. // 1) throw an eval exception, which will usually result // in a new conflict object // 2) substitute a value of 0. // There should already be a conflict object for an // unresolved reference, and having two conflicts for // essentially the same error is not useful. Using a value // of 0 allows things to continue for a bit longer. It is // consistent with active vs. inactive values, gives // basically the right result for "requires" properties, // and so on. // // For now option (2) has it, but this decision may be // reversed in future. result = false; } else { CdlValuable valuable = dynamic_cast<CdlValuable>(destination); if (0 == valuable) { // This is a serious problem, an exception is warranted. throw CdlEvalException("The expression references `" + destination->get_class_name() + " " + destination->get_name() + "' which does not have a value."); } else { CdlSimpleValue::eval_valuable(context, valuable, result); } } break; } case CdlExprOp_Negate : { // Unary -. Evaluate the target. If it is numeric, fine. Otherwise // an error is warranted. evaluate_subexpr(context, expr, subexpr.lhs_index, result); if (result.has_integer_value()) { result.set_integer_value(-1 * result.get_integer_value()); } else if (result.has_double_value()) { result.set_double_value(-1.0 * result.get_double_value()); } else { throw CdlEvalException("Attempt to negate non-numeric value `" + result.get_value() + "'."); } break; } case CdlExprOp_Plus : { // Unary +. Essentially this just checks that the current value is numeric. evaluate_subexpr(context, expr, subexpr.lhs_index, result); if ((!result.has_integer_value()) && (!result.has_double_value())) { throw CdlEvalException("Attempt to apply unary + operator to non-numeric value `" + result.get_value() + "'."); } break; } case CdlExprOp_LogicalNot : { // !x evaluate_subexpr(context, expr, subexpr.lhs_index, result); if (result.get_bool_value()) { result = false;; } else { result = true; } result.set_value_format(CdlValueFormat_Default); break; } case CdlExprOp_BitNot : { // ~x. The operand must be an integer value. evaluate_subexpr(context, expr, subexpr.lhs_index, result); if (result.has_integer_value()) { cdl_int tmp = result.get_integer_value(); result = ~tmp; } else { throw CdlEvalException("Attempt to apply unary ~ operator to non-integer value `" + result.get_value() + "'."); } break; } case CdlExprOp_Indirect : { // *x. The operand must evaluate to a string, and that string should be // the name of a CdlValuable object. CdlNode destination = 0; evaluate_subexpr(context, expr, subexpr.lhs_index, result); std::string name = result.get_value(); if (0 != context.toplevel) { destination = context.toplevel->lookup(name); } else { CYG_FAIL("This situation should probably never happen."); } if (0 == destination) { throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name + "', which is not the name of a known CDL entity."); } else { CdlValuable valuable = dynamic_cast<CdlValuable>(destination); if (0 == valuable) { throw CdlEvalException("Attempt to apply unary indirection operator * to `" + name + "', which does not have a value."); } else { CdlSimpleValue::eval_valuable(context, valuable, result); } } break; } case CdlExprOp_Active : { // ?x. If x is currently unresolved then default to 0. // See the CdlExprOp_Reference code above for a similar case. CdlNode destination = 0; if (0 != context.property) { destination = expr->references[subexpr.reference_index].get_destination(); } else { if (0 != context.toplevel) { std::string destination_name = expr->references[subexpr.reference_index].get_destination_name(); destination = context.toplevel->lookup(destination_name); } } bool active = false; if ((0 != destination) && context.transaction->is_active(destination)) { active = true; } if (active) { result = true; } else { result = false; } break; } case CdlExprOp_Multiply : { // x * y. For now this only makes sense for numerical data, // but it is possible to mix and match integer and double // precision data. // // Strictly speaking the rhs need only be evaluated if it // is known that the lhs is numeric. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if ((!(lhs.has_integer_value() || lhs.has_double_value())) || (!(rhs.has_integer_value() || rhs.has_double_value()))) { throw CdlEvalException("Attempt to multiply non-numerical values: `" + lhs.get_value() + "' * `" + rhs.get_value() + "'."); } if (lhs.has_integer_value() && rhs.has_integer_value()) { result = lhs.get_integer_value() * rhs.get_integer_value(); } else { result = lhs.get_double_value() * rhs.get_double_value(); } result.set_value_format(lhs, rhs); break; } case CdlExprOp_Divide : { // x / y. Basically the same as multiplication, apart from a check for // division by zero. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if ((!(lhs.has_integer_value() || lhs.has_double_value())) || (!(rhs.has_integer_value() || rhs.has_double_value()))) { throw CdlEvalException("Attempt to divide non-numerical values: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'."); } if (lhs.has_integer_value() && rhs.has_integer_value()) { cdl_int rhs_val = rhs.get_integer_value(); if (0 == rhs_val) { throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'."); } else { result = lhs.get_integer_value() / rhs_val; } } else { double rhs_val = rhs.get_double_value(); if (0.0 == rhs_val) { throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' / `" + rhs.get_value() + "'."); } result = lhs.get_double_value() / rhs_val; } result.set_value_format(lhs, rhs); break; } case CdlExprOp_Remainder : { // x % y. Both operands must be integral. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if (!(lhs.has_integer_value() && rhs.has_integer_value())) { throw CdlEvalException("Attempt to use the remainder operator on non integral data: `" + lhs.get_value() + "' % `" + rhs.get_value() + "'."); } cdl_int rhs_val = rhs.get_integer_value(); if (0 == rhs_val) { throw CdlEvalException("Division by zero error: `" + lhs.get_value() + "' % `" + rhs.get_value() + "'."); } result = lhs.get_integer_value() % rhs_val; result.set_value_format(lhs, rhs); break; } case CdlExprOp_Add : { // x + y. For now this only makes sense for numerical data, // but it is possible to mix and match integer and double // precision data. Arguably for string data this operator // should mean concatenation, but it would probably be // safer to have a separate operator for that. // // Strictly speaking the rhs need only be evaluated if it // is known that the lhs is numeric. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if ((!(lhs.has_integer_value() || lhs.has_double_value())) || (!(rhs.has_integer_value() || rhs.has_double_value()))) { throw CdlEvalException("Attempt to add non-numerical values: `" + lhs.get_value() + "' + `" + rhs.get_value() + "'."); } if (lhs.has_integer_value() && rhs.has_integer_value()) { result = lhs.get_integer_value() + rhs.get_integer_value(); } else { result = lhs.get_double_value() + rhs.get_double_value(); } result.set_value_format(lhs, rhs); break; } case CdlExprOp_Subtract : { // x - y. Again only numerical data is supported for now. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if ((!(lhs.has_integer_value() || lhs.has_double_value())) || (!(rhs.has_integer_value() || rhs.has_double_value()))) { throw CdlEvalException("Attempt to subtract non-numerical values: `" + lhs.get_value() + "' - `" + rhs.get_value() + "'."); } if (lhs.has_integer_value() && rhs.has_integer_value()) { result = lhs.get_integer_value() - rhs.get_integer_value(); } else { result = lhs.get_double_value() - rhs.get_double_value(); } result.set_value_format(lhs, rhs); break; } case CdlExprOp_LeftShift : { // x << y. Both operands must be integral. For now there is no // check on the value of y. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if (!(lhs.has_integer_value() && rhs.has_integer_value())) { throw CdlEvalException("Attempt to use the left-shift operator on non integral data: `" + lhs.get_value() + "' << `" + rhs.get_value() + "'."); } result = lhs.get_integer_value() << rhs.get_integer_value(); result.set_value_format(lhs, rhs); break; } case CdlExprOp_RightShift : { // x >> y. Both operands must be integral. For now there is no // check on the value of y. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if (!(lhs.has_integer_value() && rhs.has_integer_value())) { throw CdlEvalException("Attempt to use the right-shift operator on non integral data: `" + lhs.get_value() + "' >> `" + rhs.get_value() + "'."); } result = lhs.get_integer_value() >> rhs.get_integer_value(); result.set_value_format(lhs, rhs); break; } case CdlExprOp_LessThan : case CdlExprOp_LessEqual : case CdlExprOp_GreaterThan : case CdlExprOp_GreaterEqual : { // x < y, and similar comparison operators. These share // sufficient code to warrant a common implementation. Only // numerical data is supported for now. These operator could // be interpreted as e.g. substring operations, but arguably // separate operators would be better for that. CdlSimpleValue lhs; CdlSimpleValue rhs; evaluate_subexpr(context, expr, subexpr.lhs_index, lhs); evaluate_subexpr(context, expr, subexpr.rhs_index, rhs); if ((!(lhs.has_integer_value() || lhs.has_double_value())) || (!(rhs.has_integer_value() || rhs.has_double_value()))) { std::string op_str = (CdlExprOp_LessThan == subexpr.op) ? "<" : (CdlExprOp_LessEqual == subexpr.op) ? "<=" : (CdlExprOp_GreaterThan == subexpr.op) ? ">" : ">="; throw CdlEvalException("Attempt to compare non-numerical values: `" + lhs.get_value() + "' " + op_str + " `" + rhs.get_value() + "'."); } bool val = false; if (lhs.has_integer_value() && rhs.has_integer_value()) { cdl_int lhs_val = lhs.get_integer_value(); cdl_int rhs_val = rhs.get_i
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -