/** * Test if argument is valid. The boolean flag 'once' is used to signal whether the argument matching * is done in a static or a dynamic context. If the rule is constant, then the argument matching * is done in a static context (evaluate-x§once context) regardless of the setting of the once flag. * If the argument is constant, we try type promotion if permitted by the variable required type. * * @todo See the TODOs for fitArgument(...) */ bool ArgumentRule::isArgumentValid(const RevPtr<const Variable> &var, bool once) const { if ( var == NULL ) { return false; } // TODO: Use this code when the constant flag in ArgumentRule is used correctly // if ( isConstant() || !var->isAssignable() ) // if ( isConstant() ) if ( evalType == BY_VALUE ) { once = true; } for ( std::vector<TypeSpec>::const_iterator it = argTypeSpecs.begin(); it != argTypeSpecs.end(); ++it ) { if ( var->getRevObject().isTypeSpec( *it ) ) { return true; } else if ( var->getRevObject().isConvertibleTo( *it, once ) ) { return true; } else if ( once == false && !var->isAssignable() && var->getRevObject().isConvertibleTo( *it, true ) && (*it).isDerivedOf( var->getRevObjectTypeSpec() ) ) { return true; } } return false; }
/** * Fit a variable into an argument according to the argument rule. If necessary and * appropriate, we do type conversion or type promotion. * * @todo The constant flag is currently not used correctly in ArgumentRule. Therefore, * we ignore it here for now. This needs to be changed. * * @todo We need to check whether workspace objects with member variables are * modifiable by the user. * * @todo To conform to the old code we change the required type of the incoming * variable wrapper here. We need to change this so that we do not change * the wrapper here, but make sure that if the argument variable is inserted * in a member variable or container element slot, that the slot variable * wrapper, which should be unique (not the same as the incoming variable * wrapper), has the right required type. */ Argument ArgumentRule::fitArgument( Argument& arg, bool once ) const { // TODO: Use this code when the constant flag in ArgumentRule is used correctly // if ( isConstant() || !theVar->isAssignable() ) if ( evalType == BY_VALUE ) { once = true; } RevPtr<Variable> theVar = arg.getVariable(); for ( std::vector<TypeSpec>::const_iterator it = argTypeSpecs.begin(); it != argTypeSpecs.end(); ++it ) { if ( theVar->getRevObject().isTypeSpec( *it ) ) { // For now, change the required type of the incoming variable wrapper theVar->setRevObjectTypeSpec( *it ); if ( !isEllipsis() ) return Argument( theVar, getArgumentLabel(), evalType == BY_CONSTANT_REFERENCE ); else return Argument( theVar, arg.getLabel(), true ); } else if ( once == false && !theVar->isAssignable() && theVar->getRevObject().isConvertibleTo( *it, true ) && (*it).isDerivedOf( theVar->getRevObjectTypeSpec() ) ) { // Fit by type promotion. For now, we also modify the type of the incoming variable wrapper. RevObject* convertedObject = theVar->getRevObject().convertTo( *it ); theVar->setRevObject( convertedObject ); theVar->setRevObjectTypeSpec( *it ); if ( !isEllipsis() ) return Argument( theVar, getArgumentLabel(), evalType == BY_CONSTANT_REFERENCE ); else return Argument( theVar, arg.getLabel(), true ); } else if ( theVar->getRevObject().isConvertibleTo( *it, once ) ) { // Fit by type conversion if ( once || !theVar->getRevObject().hasDagNode() ) { RevObject* convertedObject = theVar->getRevObject().convertTo( *it ); Variable* convertedVar = new Variable( convertedObject ); convertedVar->setRevObjectTypeSpec( *it ); if ( !isEllipsis() ) return Argument( convertedVar, getArgumentLabel(), evalType == BY_CONSTANT_REFERENCE ); else return Argument( convertedVar, arg.getLabel(), true ); } else { RevObject* conversionObject = theVar->getRevObject().convertTo( *it ); conversionObject->makeConversionValue( theVar ); Variable* conversionVar = new Variable( conversionObject ); conversionVar->setRevObjectTypeSpec( *it ); if ( !isEllipsis() ) return Argument( conversionVar, getArgumentLabel(), evalType == BY_CONSTANT_REFERENCE ); else return Argument( conversionVar, arg.getLabel(), true ); } } } throw RbException( "Argument type mismatch fitting a " + theVar->getRevObject().getType() + " argument to formal " + getArgumentTypeSpec()[0].getType() + " " + getArgumentLabel() ); }
/** * @brief Evaluate dynamic rhs content * * This function returns the semantic value of the variable expression * when it is part of a dynamic expression, that is, the right-hand side * of an equation (deterministic) or tilde (stochastic) assignment. * * It differs from the standard evaluateContent() in several ways. First, * control variables need to return clones of themselves (temporary * variables) rather than themselves, so that they are not included in * the DAG. Second, we cannot compute a static index for indexed variables. * Instead, we need to deliver an index conisting of variables resulting * from dynamic evaluation of the index variables. These need to be put * in a dynamic lookup variable. */ RevPtr<Variable> SyntaxVariable::evaluateDynamicContent( Environment& env) { RevPtr<Variable> theVar; if ( baseVariable == NULL ) { if ( functionCall != NULL ) { // Get the dynamic return variable of the function call theVar = functionCall->evaluateDynamicContent( env ); } else if ( expression != NULL ) { // Get the dynamic return variable of the expression theVar = expression->evaluateDynamicContent( env ); } else { // Get variable from the environment (no dynamic version of identifier) theVar = env.getVariable( identifier ); } } else { // Note that the function call is always NULL if there is // a base variable, because any variables that are base to // the function call are handled by the function call. Note // also that generic expressions can only occur in base // variables, so we need not worry about any expression if // we are not a base variable. // Get the base variable theVar = baseVariable->evaluateDynamicContent( env ); // Find member variable (no dynamic version of identifier) theVar = theVar->getRevObject().getMember( identifier ); } // Get dynamic index std::vector< RevPtr<Variable> > oneOffsetIndexVars = computeDynamicIndex( env ); // Check if we need a dynamic lookup bool dynamicLookup = false; for ( std::vector< RevPtr<Variable> >::iterator it = oneOffsetIndexVars.begin(); it != oneOffsetIndexVars.end(); ++it ) { if ( (*it)->getRevObject().hasDagNode() && (*it)->isAssignable() ) { dynamicLookup = true; break; } } // If the variable we are looking up things in does not have a DAG node, we do not need a dynamic lookup regardless // If it does have a DAG node, we need a dynamic lookup if it is named, regardless of whether we have constant indices if ( theVar->getRevObject().hasDagNode() == false ) dynamicLookup = false; else if ( theVar->isAssignable() ) dynamicLookup = true; // Get dynamic element from container or subscript operator while ( !oneOffsetIndexVars.empty() ) { // Get the element... if ( theVar->getRevObject().isTypeSpec( Container::RevObject::getClassTypeSpec() ) ) { // ... from a container // Get the container index variables std::vector< RevPtr<Variable> > containerOneOffsetIndexVars; for ( size_t i = 0; i < theVar->getRevObject().getDim(); ++i ) { if ( !oneOffsetIndexVars.empty() ) { containerOneOffsetIndexVars.push_back( oneOffsetIndexVars[0] ); oneOffsetIndexVars.erase( oneOffsetIndexVars.begin() ); } else containerOneOffsetIndexVars.push_back( new Variable( new Natural( 0 ) ) ); } if ( dynamicLookup ) { // Make a dynamic element lookup theVar = new Variable( theVar->getRevObject().makeElementLookup( theVar, containerOneOffsetIndexVars ) ); } else { // We want a static element lookup // Get the container indices statically std::vector<size_t> containerOneOffsetIndices; for ( size_t i = 0; i < containerOneOffsetIndexVars.size(); ++i ) { containerOneOffsetIndices.push_back( getIndex( containerOneOffsetIndexVars[i], env ) ); } // Get the element using the getElement function theVar = theVar->getRevObject().getElement( containerOneOffsetIndices ); } } else { // ... or from a subscript operator // Note that we do not name the element here; either the member object gives out // a variable it names itself, or it gives out a temporary variable copy, which // should not be named // Create the single argument for the index operator (statically always for now) std::vector<Argument> args; args.push_back( Argument( new Variable( new Natural( getIndex( oneOffsetIndexVars[0], env ) ) ) ) ); // Get the variable using the subscript operator function // TODO: This needs to be made generic for user-defined member objects // TODO: This needs to check that there is a subscript operator function and not procedure, // and then return a dynamic element lookup theVar = theVar->getRevObject().executeMethod( "[]", args ); // Erase the index oneOffsetIndexVars.erase( oneOffsetIndexVars.begin() ); } } // Check whether we have a control variable and make a clone in that case if ( theVar->isControlVar() ) theVar = new Variable( theVar->getRevObject().clone() ); // Return the variable for assignment return theVar; }