/*! * \param[in] sc Selection collection (used for position evaluation). * \param[in] method Method to use. * \param[in] nargs Number of arguments for keyword matching. * \param[in] args Pointer to the first argument. * \param[in] rpost Reference position type to use (NULL = default). * \returns The created selection element. * * This function handles the creation of a \c t_selelem object for * selection methods that do not take parameters. */ t_selelem * _gmx_sel_init_keyword(gmx_ana_selcollection_t *sc, gmx_ana_selmethod_t *method, int nargs, t_selexpr_value *args, const char *rpost) { t_selelem *root, *child; t_selexpr_param *params, *param; int rc; if (method->nparams > 0) { gmx_bug("internal error"); return NULL; } root = _gmx_selelem_create(SEL_EXPRESSION); child = root; set_method(sc, child, method); /* Initialize the evaluation of keyword matching if values are provided */ if (nargs > 0) { gmx_ana_selmethod_t *kwmethod; switch (method->type) { case INT_VALUE: kwmethod = &sm_keyword_int; break; case STR_VALUE: kwmethod = &sm_keyword_str; break; default: _gmx_selparser_error("unknown type for keyword selection"); goto on_error; } root = _gmx_selelem_create(SEL_EXPRESSION); set_method(sc, root, kwmethod); params = param = _gmx_selexpr_create_param(NULL); param->nval = 1; param->value = _gmx_selexpr_create_value_expr(child); param = _gmx_selexpr_create_param(NULL); param->nval = nargs; param->value = args; params->next = param; if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams, root->u.expr.method->param, root)) { _gmx_selparser_error("error in keyword selection initialization"); goto on_error; } } rc = set_refpos_type(sc->pcc, child, rpost); if (rc != 0) { goto on_error; } return root; /* On error, free all memory and return NULL. */ on_error: _gmx_selelem_free(root); return NULL; }
/*! * \param[in] sc Selection collection. * \param[in] left Selection element for the left hand side. * \param[in] right Selection element for the right hand side. * \param[in] cmpop String representation of the comparison operator. * \returns The created selection element. * * This function handles the creation of a \c t_selelem object for * comparison expressions. */ t_selelem * _gmx_sel_init_comparison(gmx_ana_selcollection_t *sc, t_selelem *left, t_selelem *right, char *cmpop) { t_selelem *sel; t_selexpr_param *params, *param; int rc; sel = _gmx_selelem_create(SEL_EXPRESSION); set_method(sc, sel, &sm_compare); /* Create the parameter for the left expression */ params = param = _gmx_selexpr_create_param(left->v.type == INT_VALUE ? "int1" : "real1"); param->nval = 1; param->value = _gmx_selexpr_create_value_expr(left); /* Create the parameter for the right expression */ param = _gmx_selexpr_create_param(right->v.type == INT_VALUE ? "int2" : "real2"); param->nval = 1; param->value = _gmx_selexpr_create_value_expr(right); params->next = param; /* Create the parameter for the operator */ param = _gmx_selexpr_create_param("op"); param->nval = 1; param->value = _gmx_selexpr_create_value(STR_VALUE); param->value->u.s = cmpop; params->next->next = param; if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams, sel->u.expr.method->param, sel)) { _gmx_selparser_error("error in comparison initialization"); _gmx_selelem_free(sel); return NULL; } return sel; }
/*! \brief * Parses the values for an enumeration parameter. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_enum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { int i, len, match; if (nval != 1) { _gmx_selparser_error("a single value is required for parameter '%s'", param->name); return FALSE; } if (values->type != STR_VALUE || param->val.type != STR_VALUE) { gmx_bug("internal error"); return FALSE; } if (values->bExpr) { _gmx_selparser_error("expression value for enumerated parameter '%s' not supported", param->name); return FALSE; } len = strlen(values->u.s); i = 1; match = 0; while (param->val.u.s[i] != NULL) { if (strncmp(values->u.s, param->val.u.s[i], len) == 0) { /* Check if there is a duplicate match */ if (match > 0) { _gmx_selparser_error("ambiguous value for parameter '%s'", param->name); return FALSE; } match = i; } ++i; } if (match == 0) { _gmx_selparser_error("invalid value for parameter '%s'", param->name); return FALSE; } param->val.u.s[0] = param->val.u.s[match]; return TRUE; }
/*! \brief * Parses an expression value for a parameter that takes a variable number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_varnum_expr(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; t_selelem *child; t_selelem *expr; if (nval != 1 || !values->bExpr) { gmx_bug("internal error"); return FALSE; } value = values; child = add_child(root, param, value->u.expr); value->u.expr = NULL; if (!child) { return FALSE; } /* Process single-valued expressions */ /* TODO: We should also handle SEL_SINGLEVAL expressions here */ if (child->v.type == POS_VALUE || child->v.type == GROUP_VALUE) { /* Set the value storage */ _gmx_selvalue_setstore(&child->v, param->val.u.ptr); param->val.nr = 1; if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; } if (!(child->flags & SEL_VARNUMVAL)) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); return FALSE; } child->flags |= SEL_ALLOCVAL; param->val.nr = -1; *param->nvalptr = param->val.nr; /* Rest of the initialization is done during compilation in * init_method(). */ return TRUE; }
/*! * \param[in,out] method The method to initialize. * \param[in,out] params Pointer to the first parameter. * \param[in] scanner Scanner data structure. * \returns 0 on success, a non-zero error code on error. * * If \p *method is not a \c same method, this function returns zero * immediately. */ int _gmx_selelem_custom_init_same(gmx_ana_selmethod_t **method, const gmx::SelectionParserParameterListPointer ¶ms, void *scanner) { /* Do nothing if this is not a same method. */ if (!*method || (*method)->name != sm_same.name || params->empty()) { return 0; } const gmx::SelectionParserValueList &kwvalues = params->front().values(); if (kwvalues.size() != 1 || !kwvalues.front().hasExpressionValue() || kwvalues.front().expr->type != SEL_EXPRESSION) { _gmx_selparser_error(scanner, "'same' should be followed by a single keyword"); return -1; } gmx_ana_selmethod_t *kwmethod = kwvalues.front().expr->u.expr.method; if (kwmethod->type == STR_VALUE) { *method = &sm_same_str; } /* We do custom processing for the "as" parameter. */ gmx::SelectionParserParameterList::iterator asparam = ++params->begin(); if (asparam != params->end() && asparam->name() == sm_same.param[1].name) { gmx::SelectionParserParameterList kwparams; gmx::SelectionParserValueListPointer values( new gmx::SelectionParserValueList(asparam->values())); kwparams.push_back( gmx::SelectionParserParameter::create(NULL, move(values))); /* Create a second keyword evaluation element for the keyword given as * the first parameter, evaluating the keyword in the group given by the * second parameter. */ gmx::SelectionTreeElementPointer kwelem = _gmx_sel_init_keyword_evaluator(kwmethod, kwparams, scanner); // FIXME: Use exceptions. if (!kwelem) { return -1; } /* Replace the second parameter with one with a value from \p kwelem. */ std::string pname = asparam->name(); *asparam = gmx::SelectionParserParameter::createFromExpression(pname, kwelem); } return 0; }
/*! \brief * Parses the values for a gmx_boolean parameter. * * \param[in] name Name by which the parameter was given. * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_gmx_bool(const char *name, int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { gmx_bool bSetNo; int len; if (param->val.type != NO_VALUE) { gmx_bug("internal error"); return FALSE; } if (nval > 1 || (values && values->type != INT_VALUE)) { _gmx_selparser_error("gmx_boolean parameter '%s' takes only a yes/no/on/off/0/1 value", param->name); return FALSE; } bSetNo = FALSE; /* Check if the parameter name is given with a 'no' prefix */ len = strlen(name); if (len > 2 && name[0] == 'n' && name[1] == 'o' && strncmp(name+2, param->name, len-2) == 0) { bSetNo = TRUE; } if (bSetNo && nval > 0) { _gmx_selparser_error("gmx_boolean parameter 'no%s' should not have a value", param->name); return FALSE; } if (values && values->u.i.i1 == 0) { bSetNo = TRUE; } *param->val.u.b = bSetNo ? FALSE : TRUE; return TRUE; }
/*! \brief * Initializes the reference position calculation for a \ref SEL_EXPRESSION * element. * * \param[in,out] pcc Position calculation collection to use. * \param[in,out] sel Selection element to initialize. * \param[in] rpost Reference position type to use (NULL = default). * \returns 0 on success, a non-zero error code on error. */ static int set_refpos_type(gmx_ana_poscalc_coll_t *pcc, t_selelem *sel, const char *rpost) { int rc; if (!rpost) { return 0; } rc = 0; if (sel->u.expr.method->pupdate) { /* By default, use whole residues/molecules. */ rc = gmx_ana_poscalc_create_enum(&sel->u.expr.pc, pcc, rpost, POS_COMPLWHOLE); } else { _gmx_selparser_error("warning: '%d' modifier for '%s' ignored", rpost, sel->u.expr.method->name); } return rc; }
/*! \brief * Initializes the storage of an expression value. * * \param[in,out] sel Selection element that evaluates the value. * \param[in] param Parameter to receive the value. * \param[in] i The value of \p sel evaluates the value \p i for * \p param. * * Initializes the data pointer of \p sel such that the result is stored * as the value \p i of \p param. * This function is used internally by parse_values_std(). */ static gmx_bool set_expr_value_store(t_selelem *sel, gmx_ana_selparam_t *param, int i) { if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL)) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); return FALSE; } switch (sel->v.type) { case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break; case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break; case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break; case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break; case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break; default: /* Error */ gmx_bug("internal error"); return FALSE; } sel->v.nr = 1; sel->v.nalloc = -1; return TRUE; }
/*! * \param[in,out] sel Root of the selection element tree to initialize. * \returns 0 on success, an error code on error. * * Propagates the \ref SEL_DYNAMIC flag from the children of \p sel to \p sel * (if any child of \p sel is dynamic, \p sel is also marked as such). * The \ref SEL_DYNAMIC flag is also set for \ref SEL_EXPRESSION elements with * a dynamic method. * Also, sets one of the \ref SEL_SINGLEVAL, \ref SEL_ATOMVAL, or * \ref SEL_VARNUMVAL flags, either based on the children or on the type of * the selection method. * If the types of the children conflict, an error is returned. * * The flags of the children of \p sel are also updated if not done earlier. * The flags are initialized only once for any element; if \ref SEL_FLAGSSET * is set for an element, the function returns immediately, and the recursive * operation does not descend beyond such elements. */ int _gmx_selelem_update_flags(t_selelem *sel) { t_selelem *child; int rc; bool bUseChildType; /* Return if the flags have already been set */ if (sel->flags & SEL_FLAGSSET) { return 0; } /* Set the flags based on the current element type */ switch (sel->type) { case SEL_CONST: sel->flags |= SEL_SINGLEVAL; bUseChildType = FALSE; break; case SEL_EXPRESSION: if (sel->u.expr.method->flags & SMETH_DYNAMIC) { sel->flags |= SEL_DYNAMIC; } if (sel->u.expr.method->flags & SMETH_SINGLEVAL) { sel->flags |= SEL_SINGLEVAL; } else if (sel->u.expr.method->flags & SMETH_VARNUMVAL) { sel->flags |= SEL_VARNUMVAL; } else { sel->flags |= SEL_ATOMVAL; } bUseChildType = FALSE; break; case SEL_MODIFIER: if (sel->v.type != NO_VALUE) { sel->flags |= SEL_VARNUMVAL; } bUseChildType = FALSE; break; case SEL_ROOT: bUseChildType = FALSE; break; default: bUseChildType = TRUE; break; } /* Loop through children to propagate their flags upwards */ child = sel->child; while (child) { /* Update the child */ rc = _gmx_selelem_update_flags(child); if (rc != 0) { return rc; } /* Propagate the dynamic flag */ sel->flags |= (child->flags & SEL_DYNAMIC); /* Propagate the type flag if necessary and check for problems */ if (bUseChildType) { if ((sel->flags & SEL_VALTYPEMASK) && !(sel->flags & child->flags & SEL_VALTYPEMASK)) { _gmx_selparser_error("invalid combination of selection expressions"); return EINVAL; } sel->flags |= (child->flags & SEL_VALTYPEMASK); } child = child->next; } /* Mark that the flags are set */ sel->flags |= SEL_FLAGSSET; /* For root elements, the type should be propagated here, after the * children have been updated. */ if (sel->type == SEL_ROOT) { sel->flags |= (sel->child->flags & SEL_VALTYPEMASK); } return 0; }
/*! \brief * Parses the values for a parameter that takes a constant number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static gmx_bool parse_values_std(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; t_selelem *child; int i, j; gmx_bool bDynamic; /* Handle atom-valued parameters */ if (param->flags & SPAR_ATOMVAL) { if (nval > 1) { _gmx_selparser_error("extra values for parameter '%s'", param->name); return FALSE; } value = values; if (value->bExpr) { child = add_child(root, param, value->u.expr); value->u.expr = NULL; if (!child) { return FALSE; } child->flags |= SEL_ALLOCVAL; if (child->v.type != GROUP_VALUE && (child->flags & SEL_ATOMVAL)) { /* Rest of the initialization is done during compilation in * init_method(). */ /* TODO: Positions are not correctly handled */ param->val.nr = -1; if (param->nvalptr) { *param->nvalptr = -1; } return TRUE; } param->flags &= ~SPAR_ATOMVAL; param->val.nr = 1; if (param->nvalptr) { *param->nvalptr = 1; } param->nvalptr = NULL; if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE || param->val.type == STR_VALUE) { _gmx_selvalue_reserve(¶m->val, 1); } return set_expr_value_store(child, param, 0); } /* If we reach here, proceed with normal parameter handling */ param->val.nr = 1; if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE || param->val.type == STR_VALUE) { _gmx_selvalue_reserve(¶m->val, 1); } param->flags &= ~SPAR_ATOMVAL; param->flags &= ~SPAR_DYNAMIC; } value = values; i = 0; bDynamic = FALSE; while (value && i < param->val.nr) { if (value->type != param->val.type) { _gmx_selparser_error("incorrect value for parameter '%s' skipped", param->name); value = value->next; continue; } if (value->bExpr) { child = add_child(root, param, value->u.expr); /* Clear the expression from the value once it is stored */ value->u.expr = NULL; /* Check that the expression is valid */ if (!child) { return FALSE; } if (!set_expr_value_store(child, param, i)) { return FALSE; } if (child->flags & SEL_DYNAMIC) { bDynamic = TRUE; } } else { /* Value is not an expression */ switch (value->type) { case INT_VALUE: if (value->u.i.i1 <= value->u.i.i2) { for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j) { param->val.u.i[i++] = j; } if (j != value->u.i.i2 + 1) { _gmx_selparser_error("extra values for parameter '%s' skipped", param->name); } } else { for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j) { param->val.u.i[i++] = j; } if (j != value->u.i.i2 - 1) { _gmx_selparser_error("extra values for parameter '%s' skipped", param->name); } } --i; break; case REAL_VALUE: if (value->u.r.r1 != value->u.r.r2) { _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name); return FALSE; } param->val.u.r[i] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i] = strdup(value->u.s); break; case POS_VALUE: gmx_ana_pos_init_const(¶m->val.u.p[i], value->u.x); break; case NO_VALUE: case GROUP_VALUE: gmx_bug("internal error"); return FALSE; } } ++i; value = value->next; } if (value) { _gmx_selparser_error("extra values for parameter '%s'", param->name); return FALSE; } if (i < param->val.nr) { _gmx_selparser_error("not enough values for parameter '%s'", param->name); return FALSE; } if (!bDynamic) { param->flags &= ~SPAR_DYNAMIC; } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; }
/*! \brief * Adds a new subexpression reference to a selection element. * * \param[in,out] root Root element to which the subexpression is added. * \param[in] param Parameter for which this expression is a value. * \param[in] expr Expression to add. * \returns The created child element. * * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child * list of \p root. * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is. * \ref SEL_ALLOCVAL is cleared for the returned element. */ static t_selelem * add_child(t_selelem *root, gmx_ana_selparam_t *param, t_selelem *expr) { t_selelem *child; int rc; if (root->type != SEL_EXPRESSION && root->type != SEL_MODIFIER) { gmx_bug("unsupported root element for selection parameter parser"); return NULL; } /* Create a subexpression reference element if necessary */ if (expr->type == SEL_SUBEXPRREF) { child = expr; } else { child = _gmx_selelem_create(SEL_SUBEXPRREF); if (!child) { return NULL; } _gmx_selelem_set_vtype(child, expr->v.type); child->child = expr; } /* Setup the child element */ child->flags &= ~SEL_ALLOCVAL; child->u.param = param; if (child->v.type != param->val.type) { _gmx_selparser_error("invalid expression value for parameter '%s'", param->name); goto on_error; } rc = _gmx_selelem_update_flags(child); if (rc != 0) { goto on_error; } if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC)) { _gmx_selparser_error("parameter '%s' does not support dynamic values", param->name); goto on_error; } if (!(child->flags & SEL_DYNAMIC)) { param->flags &= ~SPAR_DYNAMIC; } /* Put the child element in the correct place */ place_child(root, child, param); return child; on_error: if (child != expr) { _gmx_selelem_free(child); } return NULL; }
/*! \brief * Parses the values for a parameter that takes a variable number of values. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \returns TRUE if the values were parsed successfully, FALSE otherwise. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static gmx_bool parse_values_varnum(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param, t_selelem *root) { t_selexpr_value *value; int i, j; param->flags &= ~SPAR_DYNAMIC; /* Update nval if there are integer ranges. */ if (param->val.type == INT_VALUE) { value = values; while (value) { if (value->type == INT_VALUE && !value->bExpr) { nval += abs(value->u.i.i2 - value->u.i.i1); } value = value->next; } } /* Check that the value type is actually implemented */ if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE && param->val.type != STR_VALUE && param->val.type != POS_VALUE) { gmx_bug("internal error"); return FALSE; } /* Reserve appropriate amount of memory */ if (param->val.type == POS_VALUE) { gmx_ana_pos_reserve(param->val.u.p, nval, 0); gmx_ana_pos_set_nr(param->val.u.p, nval); gmx_ana_indexmap_init(¶m->val.u.p->m, NULL, NULL, INDEX_UNKNOWN); } else { _gmx_selvalue_reserve(¶m->val, nval); } value = values; i = 0; while (value) { if (value->bExpr) { _gmx_selparser_error("expressions not supported within value lists"); return FALSE; } if (value->type != param->val.type) { gmx_bug("internal error"); return FALSE; } switch (param->val.type) { case INT_VALUE: if (value->u.i.i1 <= value->u.i.i2) { for (j = value->u.i.i1; j <= value->u.i.i2; ++j) { param->val.u.i[i++] = j; } } else { for (j = value->u.i.i1; j >= value->u.i.i2; --j) { param->val.u.i[i++] = j; } } break; case REAL_VALUE: if (value->u.r.r1 != value->u.r.r2) { _gmx_selparser_error("real ranges not supported for parameter '%s'", param->name); return FALSE; } param->val.u.r[i++] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i++] = strdup(value->u.s); break; case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break; default: /* Should not be reached */ gmx_bug("internal error"); return FALSE; } value = value->next; } param->val.nr = i; if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; /* Create a dummy child element to store the string values. * This element is responsible for freeing the values, but carries no * other function. */ if (param->val.type == STR_VALUE) { t_selelem *child; child = _gmx_selelem_create(SEL_CONST); _gmx_selelem_set_vtype(child, STR_VALUE); child->name = param->name; child->flags &= ~SEL_ALLOCVAL; child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA; child->v.nr = param->val.nr; _gmx_selvalue_setstore(&child->v, param->val.u.s); /* Because the child is not group-valued, the u union is not used * for anything, so we can abuse it by storing the parameter value * as place_child() expects, but this is really ugly... */ child->u.param = param; place_child(root, child, param); } return TRUE; }
/*! \brief * Parses the values for a parameter that takes integer or real ranges. * * \param[in] nval Number of values in \p values. * \param[in] values Pointer to the list of values. * \param param Parameter to parse. * \returns TRUE if the values were parsed successfully, FALSE otherwise. */ static gmx_bool parse_values_range(int nval, t_selexpr_value *values, gmx_ana_selparam_t *param) { t_selexpr_value *value; int *idata; real *rdata; int i, j, n; param->flags &= ~SPAR_DYNAMIC; if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE) { gmx_bug("internal error"); return FALSE; } idata = NULL; rdata = NULL; if (param->val.type == INT_VALUE) { snew(idata, nval*2); } else { snew(rdata, nval*2); } value = values; i = 0; while (value) { if (value->bExpr) { _gmx_selparser_error("expressions not supported within range parameters"); return FALSE; } if (value->type != param->val.type) { gmx_bug("internal error"); return FALSE; } if (param->val.type == INT_VALUE) { /* Make sure the input range is in increasing order */ if (value->u.i.i1 > value->u.i.i2) { int tmp = value->u.i.i1; value->u.i.i1 = value->u.i.i2; value->u.i.i2 = tmp; } /* Check if the new range overlaps or extends the previous one */ if (i > 0 && value->u.i.i1 <= idata[i-1]+1 && value->u.i.i2 >= idata[i-2]-1) { idata[i-2] = min(idata[i-2], value->u.i.i1); idata[i-1] = max(idata[i-1], value->u.i.i2); } else { idata[i++] = value->u.i.i1; idata[i++] = value->u.i.i2; } } else { /* Make sure the input range is in increasing order */ if (value->u.r.r1 > value->u.r.r2) { real tmp = value->u.r.r1; value->u.r.r1 = value->u.r.r2; value->u.r.r2 = tmp; } /* Check if the new range overlaps or extends the previous one */ if (i > 0 && value->u.r.r1 <= rdata[i-1] && value->u.r.r2 >= rdata[i-2]) { rdata[i-2] = min(rdata[i-2], value->u.r.r1); rdata[i-1] = max(rdata[i-1], value->u.r.r2); } else { rdata[i++] = value->u.r.r1; rdata[i++] = value->u.r.r2; } } value = value->next; } n = i/2; /* Sort the ranges and merge consequent ones */ if (param->val.type == INT_VALUE) { qsort(idata, n, 2*sizeof(int), &cmp_int_range); for (i = j = 2; i < 2*n; i += 2) { if (idata[j-1]+1 >= idata[i]) { if (idata[i+1] > idata[j-1]) { idata[j-1] = idata[i+1]; } } else { idata[j] = idata[i]; idata[j+1] = idata[i+1]; j += 2; } } } else { qsort(rdata, n, 2*sizeof(real), &cmp_real_range); for (i = j = 2; i < 2*n; i += 2) { if (rdata[j-1]+1 >= rdata[i]) { if (rdata[i+1] > rdata[j-1]) { rdata[j-1] = rdata[i+1]; } } else { rdata[j] = rdata[i]; rdata[j+1] = rdata[i+1]; j += 2; } } } n = j/2; /* Store the values */ if (param->flags & SPAR_VARNUM) { param->val.nr = n; if (param->val.type == INT_VALUE) { srenew(idata, j); _gmx_selvalue_setstore_alloc(¶m->val, idata, j); } else { srenew(rdata, j); _gmx_selvalue_setstore_alloc(¶m->val, rdata, j); } } else { if (n != param->val.nr) { _gmx_selparser_error("the value of parameter '%s' should consist of exactly one range", param->name); sfree(idata); sfree(rdata); return FALSE; } if (param->val.type == INT_VALUE) { memcpy(param->val.u.i, idata, 2*n*sizeof(int)); sfree(idata); } else { memcpy(param->val.u.r, rdata, 2*n*sizeof(real)); sfree(rdata); } } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; return TRUE; }
/*! * \param pparams List of parameters from the selection parser. * \param[in] nparam Number of parameters in \p params. * \param params Array of parameters to parse. * \param root Selection element to which child expressions are added. * \param[in] scanner Scanner data structure. * \returns TRUE if the parameters were parsed successfully, FALSE otherwise. * * Initializes the \p params array based on the parameters in \p pparams. * See the documentation of \c gmx_ana_selparam_t for different options * available for parsing. * * The list \p pparams and any associated values are freed after the parameters * have been processed, no matter is there was an error or not. */ gmx_bool _gmx_sel_parse_params(t_selexpr_param *pparams, int nparam, gmx_ana_selparam_t *params, t_selelem *root, void *scanner) { t_selexpr_param *pparam; gmx_ana_selparam_t *oparam; gmx_bool bOk, rc; int i; /* Check that the value pointers of SPAR_VARNUM parameters are NULL and * that they are not NULL for other parameters */ bOk = TRUE; for (i = 0; i < nparam; ++i) { if (params[i].val.type != POS_VALUE && (params[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL))) { if (params[i].val.u.ptr != NULL) { _gmx_selparser_error("warning: value pointer of parameter '%s' is not NULL\n" " although it should be for SPAR_VARNUM and SPAR_ATOMVAL parameters\n", params[i].name); } if ((params[i].flags & SPAR_VARNUM) && (params[i].flags & SPAR_DYNAMIC) && !params[i].nvalptr) { _gmx_selparser_error("error: nvalptr of parameter '%s' is NULL\n" " but both SPAR_VARNUM and SPAR_DYNAMIC are specified\n", params[i].name); bOk = FALSE; } } else { if (params[i].val.u.ptr == NULL) { _gmx_selparser_error("error: value pointer of parameter '%s' is NULL\n", params[i].name); bOk = FALSE; } } } if (!bOk) { _gmx_selexpr_free_params(pparams); return FALSE; } /* Parse the parameters */ pparam = pparams; i = 0; while (pparam) { /* Find the parameter and make some checks */ if (pparam->name != NULL) { i = -1; oparam = gmx_ana_selparam_find(pparam->name, nparam, params); } else if (i >= 0) { oparam = ¶ms[i]; if (oparam->name != NULL) { oparam = NULL; _gmx_selparser_error("too many NULL parameters provided"); bOk = FALSE; goto next_param; } ++i; } else { _gmx_selparser_error("all NULL parameters should appear in the beginning of the list"); bOk = FALSE; pparam = pparam->next; continue; } if (!oparam) { _gmx_selparser_error("unknown parameter '%s' skipped", pparam->name); bOk = FALSE; goto next_param; } if (oparam->flags & SPAR_SET) { _gmx_selparser_error("parameter '%s' set multiple times, extra values skipped", pparam->name); bOk = FALSE; goto next_param; } oparam->flags |= SPAR_SET; /* Process the values for the parameter */ convert_const_values(pparam->value); if (convert_values(pparam->value, oparam->val.type, scanner) != 0) { _gmx_selparser_error("invalid value for parameter '%s'", pparam->name); bOk = FALSE; goto next_param; } if (oparam->val.type == NO_VALUE) { rc = parse_values_gmx_bool(pparam->name, pparam->nval, pparam->value, oparam); } else if (oparam->flags & SPAR_RANGES) { rc = parse_values_range(pparam->nval, pparam->value, oparam); } else if (oparam->flags & SPAR_VARNUM) { if (pparam->nval == 1 && pparam->value->bExpr) { rc = parse_values_varnum_expr(pparam->nval, pparam->value, oparam, root); } else { rc = parse_values_varnum(pparam->nval, pparam->value, oparam, root); } } else if (oparam->flags & SPAR_ENUMVAL) { rc = parse_values_enum(pparam->nval, pparam->value, oparam); } else { rc = parse_values_std(pparam->nval, pparam->value, oparam, root); } if (!rc) { bOk = FALSE; } /* Advance to the next parameter */ next_param: pparam = pparam->next; } /* Check that all required parameters are present */ for (i = 0; i < nparam; ++i) { if (!(params[i].flags & SPAR_OPTIONAL) && !(params[i].flags & SPAR_SET)) { _gmx_selparser_error("required parameter '%s' not specified", params[i].name); bOk = FALSE; } } _gmx_selexpr_free_params(pparams); return bOk; }