コード例 #1
0
ファイル: Environment.cpp プロジェクト: wrightaprilm/revbayes
/** Add variable to frame. */
void Environment::addVariable( const std::string& name, const RevPtr<RevVariable>& theVar )
{
    
    /* Throw an error if the name string is empty. */
    if ( name == "" )
    {
        throw RbException("Invalid attempt to add unnamed variable to frame.");
    }
    
    /* Throw an error if the variable exists. Note that we cannot use the function
        existsVariable because that function looks recursively in parent frames, which
        would make it impossible to hide global variables. */
    if ( variableTable.find( name ) != variableTable.end() )
    {
        throw RbException( "Variable " + name + " already exists in frame" );
    }
    
    /* Insert new RevVariable in variable table */
    variableTable.insert( std::pair<std::string, RevPtr<RevVariable> >( name, theVar ) );
    theVar->setName( name );

#ifdef DEBUG_WORKSPACE
    printf("Inserted \"%s\" in frame\n", name.c_str());
#endif

}
コード例 #2
0
/**
 * @brief Process arguments
 *
 * This function processes arguments based on argument rules. First it deletes
 * any previously stored arguments. If the matching of the new arguments
 * succeeds, the processedArguments will be set to the new vector of processed
 * arguments and the function returns true. Any subsequent calls to execute()
 * will then use the processed arguments. You can also call the function with
 * the arguments directly, in which case processArguments will be called first
 * before the operation is actually performed.
 *
 * In matching arguments to argument rules, we use the same rules as in R with
 * the addition that types are also used in the matching process, after arguments
 * have been reordered as in R. The FunctionTable ensure that all argument rules
 * are distinct. However, several functions can nevertheless match the same
 * arguments because of the inheritance hierarchy. In these clases, the closest
 * match is chosen based on the first argument, then on the second, etc.
 *
 * The function returns a match score based on how closely the arguments match the
 * rules, with 0 being perfect match, 1 being a match to an immediate base class type,
 * 2 a match to a grandparent class, etc. A large number is used for arguments that
 * need type conversion.
 *
 * The evaluateOnce parameter MemberObject whether the function is to be evaluated once,
 * immediately after the call to processArguments, or whether the processed arguments
 * will be used in repeated function calls in a function node. Argument matching is
 * based on current values in the first case, but on the wrapper type in the second.
 *
 * @todo Labels need to be stored for ellipsis arguments.
 *
 * These are the argument matching rules:
 *
 *  1. If the last argument rule is an ellipsis, and it is the kth argument passed
 *     in, then all arguments passed in, from position k to the end, are wrapped
 *     in a single ContainerNode object. These arguments are not matched to any
 *     rules.
 *  2. The remaining arguments are matched to labels using exact matching. If the
 *     type does not match the type of the rule, it is an error.
 *  3. The remaining arguments are matched to any remaining slots using partial
 *     matching. If there is ambiguity or the types do not match, it is an error.
 *  4. The remaining arguments are used for the empty slots in the order they were
 *     passed in. If the types do not match, it is an error.
 *  5. Any remaining empty slots are filled with default values stored in the argument
 *     rules (we use copies of the values, of course).
 *  6. If there are still empty slots, the arguments do not match the rules.
 *
 * @todo Fredrik: The code and the logic has been changed without changing the comments, so these
 *       are out of date. Also note that the argument matching is problematic for unlabeled
 *       arguments (order can be changed based on argument types, which may cause unintended
 *       consequences). Furthermore, there is redundant code left from the old implementation.
 *       Finally, the ellipsis arguments no longer have to be last among the rules, but they
 *       are still the last arguments after processing.
 *
 * @todo Fredrik: Static and dynamic type conversion added, but partly hack-ish, so the implementation
 *       needs to be revised
 */
void Function::processArguments( const std::vector<Argument>& passedArgs, bool once )
{

    /*********************  0. Initialization  **********************/
    /* Get my own copy of the argument vector */
    std::vector<Argument> pArgs = passedArgs;
    
    /* Get the argument rules */
    const ArgumentRules& theRules = getArgumentRules();

    /* Get the number of argument rules */
    size_t nRules = theRules.size();

    /* Clear previously processed arguments */
    args.clear();
    
    /* Keep track of which arguments we have used, and which argument slots we have filled, and with what passed arguments */
    std::vector<bool>   taken           = std::vector<bool>( passedArgs.size(), false );
    std::vector<bool>   filled          = std::vector<bool>( nRules, false );
    std::vector<size_t> passedArgIndex  = std::vector<size_t>( nRules, 1000 );
    std::vector<Argument> ellipsisArgs;

    /*********************  1. Do exact matching  **********************/

    /* Do exact matching of labels */
    for(size_t i=0; i<passedArgs.size(); i++)
    {

        /* Test if swallowed by ellipsis; if so, we can quit because the remaining passedArgs will also be swallowed */
        if ( taken[i] )
        {
            break;
        }
        
        /* Skip if no label */
        if ( passedArgs[i].getLabel().size() == 0 )
        {
            continue;
        }
        
        /* Check for matches in all regular rules (we assume that all labels are unique; this is checked by FunctionTable) */
        for (size_t j=0; j<nRules; j++)
        {
            std::vector<std::string> aliases = theRules[j].getArgumentAliases();

            for(size_t k=0; k < aliases.size(); k++)
            {
                if ( passedArgs[i].getLabel() == aliases[k] )
                {

                    if ( filled[j] )
                    {
                        throw RbException( "Duplicate argument labels '" + passedArgs[i].getLabel() );
                    }

                    pArgs[i]            = theRules[j].fitArgument( pArgs[i], once );
                    taken[i]            = true;
                    filled[j]           = true;
                    passedArgIndex[j]   = static_cast<int>( i );

                    // We got an exact match -> we can skip the other labels for checking
                    j = nRules;
                    break;
                }
            }
        }
    }

 
    /*********************  2. Do partial matching  **********************/

    /* Do partial matching of labels */
    for(size_t i=0; i<passedArgs.size(); i++) 
    {

        /* Skip if already matched */
        if ( taken[i] )
        {
            continue;
        }
        
        /* Skip if no label */
        if ( passedArgs[i].getLabel().size() == 0 )
        {
            continue;
        }
        
        /* Initialize match index and number of matches */
        int nMatches = 0;
        int matchRule = -1;
        std::string label;

        /* Try all rules */
        for (size_t j=0; j<nRules; j++) 
        {
            std::vector<std::string> aliases = theRules[j].getArgumentAliases();

            for(size_t k=0; k < aliases.size(); k++)
            {
                if ( !filled[j] && aliases[k].compare(0, passedArgs[i].getLabel().size(), passedArgs[i].getLabel()) == 0 )
                {
                    ++nMatches;
                    matchRule = static_cast<int>( j );
                    label = aliases[k];
                }
            }
        }

        if (nMatches > 1)
        {
            throw RbException( "Argument label '" + passedArgs[i].getLabel() + "' matches mutliple parameter labels." );
        }
        else if (nMatches < 1)
        {
            throw RbException( "Argument label '" + passedArgs[i].getLabel() + "' matches no untaken parameter labels." );
        }
        
        if ( nMatches == 1)
        {
            pArgs[i].setLabel(label);
            pArgs[i]                    = theRules[matchRule].fitArgument( pArgs[i], once );
            taken[i]                    = true;
            filled[matchRule]           = true;
            passedArgIndex[matchRule]   = static_cast<int>( i );
        }
        
    }


    /*********************  3. Fill with unused passedArgs  **********************/

    /* Fill in empty slots using the remaining args in order */
    for(size_t i=0; i<passedArgs.size(); i++) 
    {

        /* Skip if already matched */
        if ( taken[i] )
        {
            continue;
        }
        
        /* Find first empty slot and try to fit argument there */
        for (size_t j=0; j<nRules; j++) 
        {

            if ( filled[j] == false &&
                 ( (!theRules[j].isEllipsis() && passedArgs[i].getLabel().size() == 0) || (theRules[j].isEllipsis()) ) )
            {
                
                Argument &arg = const_cast<Argument&>(passedArgs[i]);
                double penalty = theRules[j].isArgumentValid( arg, once );
                if ( penalty != -1 )
                {
                    if(theRules[j].getArgumentAliases().size() > 1)
                    {
                        throw RbException("Could not determine argument label for parameter '" + theRules[j].getArgumentLabel() + "'.");
                    }
                    pArgs[i].setLabel( theRules[j].getArgumentLabel() );
                    pArgs[i]          = theRules[j].fitArgument( pArgs[i], once );
                    taken[i]          = true;
                    if ( !theRules[j].isEllipsis() ) 
                    {
                        filled[j]     = true;
                        passedArgIndex[j] = static_cast<int>( i );
                    }
                    else 
                    {
                        ellipsisArgs.push_back( pArgs[i] );
                    }
                    
                    break;
                }
            }
        }
        
        /* Final test if we found a match */
        if ( !taken[i] )
        {
            throw RbException("Superfluous argument of type '" + passedArgs[i].getVariable()->getRevObject().getType() + "' and name '" + passedArgs[i].getLabel() + "' passed to function '" + getType() + "'.");
        }
    }

    /*********************  4. Fill with default values  **********************/

    /* Fill in empty slots using default values */
    for(size_t i=0; i<nRules; i++) 
    {

        if ( filled[i] == true || theRules[i].isEllipsis())
        {
            continue;
        }
        
        // we just leave the optional arguments empty
        if ( !theRules[i].hasDefault() )
        {
            throw RbException("No argument found for parameter '" + theRules[i].getArgumentLabel() + "'.");
        }
        
        const ArgumentRule& theRule = theRules[i];
        RevPtr<RevVariable> theVar = theRule.getDefaultVariable().clone();
        theVar->setName( "." + theRule.getArgumentLabel() );
        theVar->setRequiredTypeSpec( theRule.getDefaultVariable().getRequiredTypeSpec() );
        size_t idx = pArgs.size();
        passedArgIndex[i] = idx;
        pArgs.push_back( Argument( theVar, theRule.getArgumentLabel(), theRule.getEvaluationType() != ArgumentRule::BY_CONSTANT_REFERENCE ) );
    }

    argsProcessed = true;
    
    /*********************  5. Insert arguments into argument list  **********************/
    for (size_t j=0; j<nRules; j++) 
    {
        if ( passedArgIndex[j] < 1000 )
        {
            args.push_back( pArgs[ passedArgIndex[j] ] );
        }
        
    }
    
    /*********************  6. Insert ellipsis arguments  **********************/
    for (std::vector<Argument>::iterator i = ellipsisArgs.begin(); i != ellipsisArgs.end(); i++) 
    {
        args.push_back( *i );
    }

}