/*! \brief * Parses the values for a parameter that takes a variable number of values. * * \param[in] values List of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \param[in] scanner Scanner data structure. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static void parse_values_varnum(const SelectionParserValueList &values, gmx_ana_selparam_t *param, const SelectionTreeElementPointer &root, void *scanner) { int i, j; param->flags &= ~SPAR_DYNAMIC; /* Compute number of values, considering also integer ranges. */ int valueCount = static_cast<int>(values.size()); if (param->val.type == INT_VALUE) { SelectionParserValueList::const_iterator value; for (value = values.begin(); value != values.end(); ++value) { if (value->type == INT_VALUE && !value->hasExpressionValue()) { valueCount += abs(value->u.i.i2 - value->u.i.i1); } } } /* 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_THROW(InternalError("Variable-count value type not implemented")); } /* Reserve appropriate amount of memory */ if (param->val.type == POS_VALUE) { gmx_ana_pos_reserve(param->val.u.p, valueCount, 0); gmx_ana_indexmap_init(¶m->val.u.p->m, NULL, NULL, INDEX_UNKNOWN); gmx_ana_pos_set_nr(param->val.u.p, valueCount); } else { _gmx_selvalue_reserve(¶m->val, valueCount); } /* 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) { SelectionTreeElementPointer child( new SelectionTreeElement(SEL_CONST, SelectionLocation::createEmpty())); _gmx_selelem_set_vtype(child, STR_VALUE); child->setName(param->name); child->flags &= ~SEL_ALLOCVAL; child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA; child->v.nr = valueCount; _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); } param->val.nr = valueCount; i = 0; SelectionParserValueList::const_iterator value; for (value = values.begin(); value != values.end(); ++value) { GMX_RELEASE_ASSERT(value->type == param->val.type, "Invalid value type (should have been caught earlier)"); if (value->hasExpressionValue()) { std::string text(_gmx_sel_lexer_get_text(scanner, value->location())); std::string message("Selection expressions are not supported in this " "context when multiple values are provided"); InvalidInputError ex(message); ex.prependContext(formatString("Invalid expression '%s'", text.c_str())); GMX_THROW(ex); } 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) { std::string text(_gmx_sel_lexer_get_text(scanner, value->location())); std::string message = formatString("Real range ('%s') is not supported in this context", text.c_str()); InvalidInputError ex(message); GMX_THROW(ex); } param->val.u.r[i++] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i++] = gmx_strdup(value->stringValue().c_str()); break; case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break; default: /* Should not be reached */ GMX_RELEASE_ASSERT(false, "Variable-count value type not implemented"); } } GMX_RELEASE_ASSERT(i == valueCount, "Inconsistent value count wrt. the actual value population"); if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; }
/*! \brief * Parses the values for a parameter that takes a constant number of values. * * \param[in] values List of values. * \param param Parameter to parse. * \param root Selection element to which child expressions are added. * \param[in] scanner Scanner data structure. * * For integer ranges, the sequence of numbers from the first to second value * is stored, each as a separate value. */ static void parse_values_std(const SelectionParserValueList &values, gmx_ana_selparam_t *param, const SelectionTreeElementPointer &root, void *scanner) { int i, j; bool bDynamic; /* Handle atom-valued parameters */ if (param->flags & SPAR_ATOMVAL) { if (values.size() > 1) { GMX_THROW(InvalidInputError( "Only a single value or a single expression is " "supported in this context")); } if (values.front().hasExpressionValue()) { SelectionTreeElementPointer child = add_child(root, param, values.front().expr, scanner); 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; } 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); } set_expr_value_store(child, param, 0, scanner); return; } /* 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; } i = 0; bDynamic = false; SelectionParserValueList::const_iterator value; for (value = values.begin(); value != values.end() && i < param->val.nr; ++value) { GMX_RELEASE_ASSERT(value->type == param->val.type, "Invalid value type (should have been caught earlier)"); if (value->hasExpressionValue()) { SelectionTreeElementPointer child = add_child(root, param, value->expr, scanner); set_expr_value_store(child, param, i, scanner); if (child->flags & SEL_DYNAMIC) { bDynamic = true; } } else { /* Value is not an expression */ switch (value->type) { case INT_VALUE: { bool bTooManyValues; 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; } bTooManyValues = (j != value->u.i.i2 + 1); } else { for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j) { param->val.u.i[i++] = j; } bTooManyValues = (j != value->u.i.i2 - 1); } if (bTooManyValues) { std::string text(_gmx_sel_lexer_get_text(scanner, value->location())); std::string message = formatString("Range ('%s') produces more values than is " "accepted in this context", text.c_str()); GMX_THROW(InvalidInputError(message)); } --i; break; } case REAL_VALUE: if (value->u.r.r1 != value->u.r.r2) { std::string text(_gmx_sel_lexer_get_text(scanner, value->location())); std::string message = formatString("Real range ('%s') is not supported in this context", text.c_str()); GMX_THROW(InvalidInputError(message)); } param->val.u.r[i] = value->u.r.r1; break; case STR_VALUE: param->val.u.s[i] = gmx_strdup(value->stringValue().c_str()); 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_THROW(InternalError("Invalid non-expression value type")); } } ++i; } if (value != values.end()) { std::string message = formatString("Too many values provided, expected %d", param->val.nr); GMX_THROW(InvalidInputError(message)); } if (i < param->val.nr) { std::string message = formatString("Too few values provided, expected %d", param->val.nr); GMX_THROW(InvalidInputError(message)); } if (!bDynamic) { param->flags &= ~SPAR_DYNAMIC; } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; }
/*! \brief * Parses the values for a parameter that takes integer or real ranges. * * \param[in] values List of values. * \param param Parameter to parse. * \param[in] scanner Scanner data structure. */ static void parse_values_range(const SelectionParserValueList &values, gmx_ana_selparam_t *param, void *scanner) { int i, j, n; param->flags &= ~SPAR_DYNAMIC; GMX_RELEASE_ASSERT(param->val.type == INT_VALUE || param->val.type == REAL_VALUE, "Invalid range parameter type"); int *idata = NULL; real *rdata = NULL; scoped_guard_sfree dataGuard; if (param->val.type == INT_VALUE) { snew(idata, values.size()*2); dataGuard.reset(idata); } else { snew(rdata, values.size()*2); dataGuard.reset(rdata); } i = 0; SelectionParserValueList::const_iterator value; for (value = values.begin(); value != values.end(); ++value) { GMX_RELEASE_ASSERT(value->type == param->val.type, "Invalid range value type (should have been caught earlier)"); if (value->hasExpressionValue()) { std::string text(_gmx_sel_lexer_get_text(scanner, value->location())); std::string message("Only simple values or 'A to B' ranges are " "supported in this context"); InvalidInputError ex(message); ex.prependContext(formatString("Invalid expression '%s'", text.c_str())); GMX_THROW(ex); } if (param->val.type == INT_VALUE) { int i1 = std::min(value->u.i.i1, value->u.i.i2); int i2 = std::max(value->u.i.i1, value->u.i.i2); /* Check if the new range overlaps or extends the previous one */ if (i > 0 && i1 <= idata[i-1]+1 && i2 >= idata[i-2]-1) { idata[i-2] = std::min(idata[i-2], i1); idata[i-1] = std::max(idata[i-1], i2); } else { idata[i++] = i1; idata[i++] = i2; } } else { real r1 = std::min(value->u.r.r1, value->u.r.r2); real r2 = std::max(value->u.r.r1, value->u.r.r2); /* Check if the new range overlaps or extends the previous one */ if (i > 0 && r1 <= rdata[i-1] && r2 >= rdata[i-2]) { rdata[i-2] = std::min(rdata[i-2], r1); rdata[i-1] = std::max(rdata[i-1], r2); } else { rdata[i++] = r1; rdata[i++] = r2; } } } 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] >= 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) { dataGuard.release(); 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_ASSERT(n == 1, "Range parameters with a fixed count > 1 do not make sense"); GMX_THROW(InvalidInputError("Only one value or 'A to B' range is " "supported in this context")); } if (param->val.type == INT_VALUE) { memcpy(param->val.u.i, idata, 2*n*sizeof(int)); } else { memcpy(param->val.u.r, rdata, 2*n*sizeof(real)); } } if (param->nvalptr) { *param->nvalptr = param->val.nr; } param->nvalptr = NULL; }