2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements functions in selparam.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_selection
47 #include "gromacs/math/units.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/selection/position.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/gmxassert.h"
53 #include "gromacs/utility/smalloc.h"
54 #include "gromacs/utility/stringutil.h"
55 #include "gromacs/utility/unique_cptr.h"
57 #include "parsetree.h"
60 #include "selmethod.h"
66 * \param[in] name Name of the parameter to search.
67 * \param[in] nparam Number of parameters in the \p param array.
68 * \param[in] param Parameter array to search.
69 * \returns Pointer to the parameter in the \p param
70 * or NULL if no parameter with name \p name was found.
72 * The comparison is case-sensitive.
75 gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param)
83 /* Find the first non-null parameter */
85 while (i < nparam && param[i].name == nullptr)
89 /* Process the special case of a NULL parameter */
92 return (i == 0) ? nullptr : ¶m[i-1];
94 for (; i < nparam; ++i)
96 if (!strcmp(param[i].name, name))
100 /* Check for 'no' prefix on boolean parameters */
101 if (param[i].val.type == NO_VALUE
102 && strlen(name) > 2 && name[0] == 'n' && name[1] == 'o'
103 && !strcmp(param[i].name, name+2))
112 * Does a type conversion on a SelectionParserValue.
114 * \param[in,out] value Value to convert.
115 * \param[in] type Type to convert to.
116 * \param[in] errors Errors will be reported into this as nested exceptions.
117 * \param[in] scanner Scanner data structure.
120 convert_value(SelectionParserValue *value, e_selvalue_t type,
121 ExceptionInitializer *errors, void *scanner)
123 if (value->type == type || type == NO_VALUE)
127 if (value->hasExpressionValue())
129 /* Conversion from atom selection to position using default
130 * reference positions. */
131 if (value->type == GROUP_VALUE && type == POS_VALUE)
135 SelectionTreeElementPointer expr =
136 _gmx_sel_init_position(value->expr, nullptr, scanner);
137 *value = SelectionParserValue::createExpr(expr);
139 catch (UserInputError &ex)
141 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
142 std::string context(formatString("In '%s'", text.c_str()));
143 ex.prependContext(context);
144 errors->addCurrentExceptionAsNested();
151 /* Integers to floating point are easy */
152 if (value->type == INT_VALUE && type == REAL_VALUE)
154 *value = SelectionParserValue::createRealRange(value->u.i.i1,
159 /* Reals that are integer-valued can also be converted */
160 if (value->type == REAL_VALUE && type == INT_VALUE)
162 int i1 = static_cast<int>(value->u.r.r1);
163 int i2 = static_cast<int>(value->u.r.r2);
164 if (gmx_within_tol(value->u.r.r1, i1, GMX_REAL_EPS)
165 && gmx_within_tol(value->u.r.r2, i2, GMX_REAL_EPS))
167 *value = SelectionParserValue::createIntegerRange(i1, i2, value->location());
172 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
174 formatString("Expression '%s' evaluates to a type is not valid in this context",
176 InvalidInputError ex(message);
177 errors->addNested(ex);
181 * Does a type conversion on a list of values.
183 * \param[in,out] values Values to convert.
184 * \param[in] type Type to convert to.
185 * \param[in] scanner Scanner data structure.
188 convert_values(SelectionParserValueList *values, e_selvalue_t type, void *scanner)
190 ExceptionInitializer errors("");
191 SelectionParserValueList::iterator value;
192 for (value = values->begin(); value != values->end(); ++value)
194 convert_value(&*value, type, &errors, scanner);
196 if (errors.hasNestedExceptions())
198 GMX_THROW(InvalidInputError(errors));
203 * Adds a child element for a parameter, keeping the parameter order.
205 * \param[in,out] root Root element to which the child is added.
206 * \param[in] child Child to add.
207 * \param[in] param Parameter for which this child is a value.
209 * Puts \p child in the child list of \p root such that the list remains
210 * in the same order as the corresponding parameters.
213 place_child(const SelectionTreeElementPointer &root,
214 const SelectionTreeElementPointer &child,
215 gmx_ana_selparam_t *param)
217 gmx_ana_selparam_t *ps;
220 ps = root->u.expr.method->param;
222 /* Put the child element in the correct place */
223 if (!root->child || n < root->child->u.param - ps)
225 child->next = root->child;
230 SelectionTreeElementPointer prev = root->child;
231 while (prev->next && prev->next->u.param - ps >= n)
235 child->next = prev->next;
241 * Comparison function for sorting integer ranges.
243 * \param[in] a Pointer to the first range.
244 * \param[in] b Pointer to the second range.
245 * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
247 * The ranges are primarily sorted based on their starting point, and
248 * secondarily based on length (longer ranges come first).
251 cmp_int_range(const void *a, const void *b)
253 if (((int *)a)[0] < ((int *)b)[0])
257 if (((int *)a)[0] > ((int *)b)[0])
261 if (((int *)a)[1] > ((int *)b)[1])
269 * Comparison function for sorting real ranges.
271 * \param[in] a Pointer to the first range.
272 * \param[in] b Pointer to the second range.
273 * \returns -1, 0, or 1 depending on the relative order of \p a and \p b.
275 * The ranges are primarily sorted based on their starting point, and
276 * secondarily based on length (longer ranges come first).
279 cmp_real_range(const void *a, const void *b)
281 if (((real *)a)[0] < ((real *)b)[0])
285 if (((real *)a)[0] > ((real *)b)[0])
289 if (((real *)a)[1] > ((real *)b)[1])
297 * Parses the values for a parameter that takes integer or real ranges.
299 * \param[in] values List of values.
300 * \param param Parameter to parse.
301 * \param[in] scanner Scanner data structure.
304 parse_values_range(const SelectionParserValueList &values,
305 gmx_ana_selparam_t *param, void *scanner)
309 param->flags &= ~SPAR_DYNAMIC;
310 GMX_RELEASE_ASSERT(param->val.type == INT_VALUE || param->val.type == REAL_VALUE,
311 "Invalid range parameter type");
312 int *idata = nullptr;
313 real *rdata = nullptr;
314 sfree_guard dataGuard;
315 if (param->val.type == INT_VALUE)
317 snew(idata, values.size()*2);
318 dataGuard.reset(idata);
322 snew(rdata, values.size()*2);
323 dataGuard.reset(rdata);
326 SelectionParserValueList::const_iterator value;
327 for (value = values.begin(); value != values.end(); ++value)
329 GMX_RELEASE_ASSERT(value->type == param->val.type,
330 "Invalid range value type (should have been caught earlier)");
331 if (value->hasExpressionValue())
333 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
334 std::string message("Only simple values or 'A to B' ranges are "
335 "supported in this context");
336 InvalidInputError ex(message);
337 ex.prependContext(formatString("Invalid expression '%s'", text.c_str()));
340 if (param->val.type == INT_VALUE)
342 int i1 = std::min(value->u.i.i1, value->u.i.i2);
343 int i2 = std::max(value->u.i.i1, value->u.i.i2);
344 /* Check if the new range overlaps or extends the previous one */
345 if (i > 0 && i1 <= idata[i-1]+1 && i2 >= idata[i-2]-1)
347 idata[i-2] = std::min(idata[i-2], i1);
348 idata[i-1] = std::max(idata[i-1], i2);
358 real r1 = std::min(value->u.r.r1, value->u.r.r2);
359 real r2 = std::max(value->u.r.r1, value->u.r.r2);
360 /* Check if the new range overlaps or extends the previous one */
361 if (i > 0 && r1 <= rdata[i-1] && r2 >= rdata[i-2])
363 rdata[i-2] = std::min(rdata[i-2], r1);
364 rdata[i-1] = std::max(rdata[i-1], r2);
374 /* Sort the ranges and merge consequent ones */
375 if (param->val.type == INT_VALUE)
377 qsort(idata, n, 2*sizeof(int), &cmp_int_range);
378 for (i = j = 2; i < 2*n; i += 2)
380 if (idata[j-1]+1 >= idata[i])
382 if (idata[i+1] > idata[j-1])
384 idata[j-1] = idata[i+1];
390 idata[j+1] = idata[i+1];
397 qsort(rdata, n, 2*sizeof(real), &cmp_real_range);
398 for (i = j = 2; i < 2*n; i += 2)
400 if (rdata[j-1] >= rdata[i])
402 if (rdata[i+1] > rdata[j-1])
404 rdata[j-1] = rdata[i+1];
410 rdata[j+1] = rdata[i+1];
416 /* Store the values */
417 if (param->flags & SPAR_VARNUM)
421 if (param->val.type == INT_VALUE)
424 _gmx_selvalue_setstore_alloc(¶m->val, idata, j);
429 _gmx_selvalue_setstore_alloc(¶m->val, rdata, j);
434 if (n != param->val.nr)
437 "Range parameters with a fixed count > 1 do not make sense");
438 GMX_THROW(InvalidInputError("Only one value or 'A to B' range is "
439 "supported in this context"));
441 if (param->val.type == INT_VALUE)
443 memcpy(param->val.u.i, idata, 2*n*sizeof(int));
447 memcpy(param->val.u.r, rdata, 2*n*sizeof(real));
452 *param->nvalptr = param->val.nr;
454 param->nvalptr = nullptr;
458 * Parses the values for a parameter that takes a variable number of values.
460 * \param[in] values List of values.
461 * \param param Parameter to parse.
462 * \param root Selection element to which child expressions are added.
463 * \param[in] scanner Scanner data structure.
465 * For integer ranges, the sequence of numbers from the first to second value
466 * is stored, each as a separate value.
469 parse_values_varnum(const SelectionParserValueList &values,
470 gmx_ana_selparam_t *param,
471 const SelectionTreeElementPointer &root,
476 param->flags &= ~SPAR_DYNAMIC;
477 /* Compute number of values, considering also integer ranges. */
478 int valueCount = static_cast<int>(values.size());
479 if (param->val.type == INT_VALUE)
481 SelectionParserValueList::const_iterator value;
482 for (value = values.begin(); value != values.end(); ++value)
484 if (value->type == INT_VALUE && !value->hasExpressionValue())
486 valueCount += abs(value->u.i.i2 - value->u.i.i1);
491 /* Check that the value type is actually implemented */
492 if (param->val.type != INT_VALUE && param->val.type != REAL_VALUE
493 && param->val.type != STR_VALUE && param->val.type != POS_VALUE)
495 GMX_THROW(InternalError("Variable-count value type not implemented"));
498 /* Reserve appropriate amount of memory */
499 if (param->val.type == POS_VALUE)
501 gmx_ana_pos_reserve(param->val.u.p, valueCount, 0);
502 gmx_ana_indexmap_init(¶m->val.u.p->m, nullptr, nullptr, INDEX_UNKNOWN);
503 gmx_ana_pos_set_nr(param->val.u.p, valueCount);
507 _gmx_selvalue_reserve(¶m->val, valueCount);
509 /* Create a dummy child element to store the string values.
510 * This element is responsible for freeing the values, but carries no
512 if (param->val.type == STR_VALUE)
514 SelectionTreeElementPointer child(
515 new SelectionTreeElement(SEL_CONST, SelectionLocation::createEmpty()));
516 _gmx_selelem_set_vtype(child, STR_VALUE);
517 child->setName(param->name);
518 child->flags &= ~SEL_ALLOCVAL;
519 child->flags |= SEL_FLAGSSET | SEL_VARNUMVAL | SEL_ALLOCDATA;
520 child->v.nr = valueCount;
521 _gmx_selvalue_setstore(&child->v, param->val.u.s);
522 /* Because the child is not group-valued, the u union is not used
523 * for anything, so we can abuse it by storing the parameter value
524 * as place_child() expects, but this is really ugly... */
525 child->u.param = param;
526 place_child(root, child, param);
528 param->val.nr = valueCount;
531 SelectionParserValueList::const_iterator value;
532 for (value = values.begin(); value != values.end(); ++value)
534 GMX_RELEASE_ASSERT(value->type == param->val.type,
535 "Invalid value type (should have been caught earlier)");
536 if (value->hasExpressionValue())
538 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
539 std::string message("Selection expressions are not supported in this "
540 "context when multiple values are provided");
541 InvalidInputError ex(message);
542 ex.prependContext(formatString("Invalid expression '%s'", text.c_str()));
545 switch (param->val.type)
548 if (value->u.i.i1 <= value->u.i.i2)
550 for (j = value->u.i.i1; j <= value->u.i.i2; ++j)
552 param->val.u.i[i++] = j;
557 for (j = value->u.i.i1; j >= value->u.i.i2; --j)
559 param->val.u.i[i++] = j;
564 if (value->u.r.r1 != value->u.r.r2)
566 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
568 = formatString("Real range ('%s') is not supported in this context",
570 InvalidInputError ex(message);
573 param->val.u.r[i++] = value->u.r.r1;
576 param->val.u.s[i++] = gmx_strdup(value->stringValue().c_str());
578 case POS_VALUE: copy_rvec(value->u.x, param->val.u.p->x[i++]); break;
579 default: /* Should not be reached */
580 GMX_RELEASE_ASSERT(false, "Variable-count value type not implemented");
583 GMX_RELEASE_ASSERT(i == valueCount,
584 "Inconsistent value count wrt. the actual value population");
587 *param->nvalptr = param->val.nr;
589 param->nvalptr = nullptr;
593 * Adds a new subexpression reference to a selection element.
595 * \param[in,out] root Root element to which the subexpression is added.
596 * \param[in] param Parameter for which this expression is a value.
597 * \param[in] expr Expression to add.
598 * \param[in] scanner Scanner data structure.
599 * \returns The created child element.
601 * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child
603 * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is.
604 * \ref SEL_ALLOCVAL is cleared for the returned element.
606 static SelectionTreeElementPointer
607 add_child(const SelectionTreeElementPointer &root, gmx_ana_selparam_t *param,
608 const SelectionTreeElementPointer &expr, void *scanner)
610 GMX_RELEASE_ASSERT(root->type == SEL_EXPRESSION || root->type == SEL_MODIFIER,
611 "Unsupported root element for selection parameter parser");
612 SelectionTreeElementPointer child;
613 /* Create a subexpression reference element if necessary */
614 if (expr->type == SEL_SUBEXPRREF)
620 // TODO: Initialize such that it includes the parameter.
621 child.reset(new SelectionTreeElement(SEL_SUBEXPRREF, expr->location()));
622 _gmx_selelem_set_vtype(child, expr->v.type);
625 /* Setup the child element */
626 child->flags &= ~SEL_ALLOCVAL;
627 child->u.param = param;
628 if (child->v.type != param->val.type)
630 // TODO: It would be nice to say what is the expected type.
631 std::string text(_gmx_sel_lexer_get_text(scanner, expr->location()));
633 = formatString("Expression '%s' is not valid in this context "
634 "(produces the wrong type of values)",
636 GMX_THROW(InvalidInputError(message));
638 _gmx_selelem_update_flags(child);
639 if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC))
641 std::string text(_gmx_sel_lexer_get_text(scanner, expr->location()));
643 = formatString("Expression '%s' is dynamic, which is not "
644 "valid in this context",
646 GMX_THROW(InvalidInputError(message));
648 if (!(child->flags & SEL_DYNAMIC))
650 param->flags &= ~SPAR_DYNAMIC;
652 /* Put the child element in the correct place */
653 place_child(root, child, param);
658 * Parses an expression value for a parameter that takes a variable number of values.
660 * \param[in] values List of values.
661 * \param param Parameter to parse.
662 * \param root Selection element to which child expressions are added.
663 * \param[in] scanner Scanner data structure.
666 parse_values_varnum_expr(const SelectionParserValueList &values,
667 gmx_ana_selparam_t *param,
668 const SelectionTreeElementPointer &root,
671 GMX_RELEASE_ASSERT(values.size() == 1 && values.front().hasExpressionValue(),
672 "Called with an invalid type of value");
674 SelectionTreeElementPointer child
675 = add_child(root, param, values.front().expr, scanner);
677 /* Process single-valued expressions */
678 /* TODO: We should also handle SEL_SINGLEVAL expressions here */
679 if (child->v.type == POS_VALUE || child->v.type == GROUP_VALUE)
681 /* Set the value storage */
682 _gmx_selvalue_setstore(&child->v, param->val.u.ptr);
686 *param->nvalptr = param->val.nr;
688 param->nvalptr = nullptr;
692 if (!(child->flags & SEL_VARNUMVAL))
694 std::string text(_gmx_sel_lexer_get_text(scanner, values.front().location()));
696 = formatString("Expression '%s' is invalid in this context",
698 GMX_THROW(InvalidInputError(message));
701 child->flags |= SEL_ALLOCVAL;
703 *param->nvalptr = param->val.nr;
704 /* Rest of the initialization is done during compilation in
709 * Initializes the storage of an expression value.
711 * \param[in,out] sel Selection element that evaluates the value.
712 * \param[in] param Parameter to receive the value.
713 * \param[in] i The value of \p sel evaluates the value \p i for
715 * \param[in] scanner Scanner data structure.
717 * Initializes the data pointer of \p sel such that the result is stored
718 * as the value \p i of \p param.
719 * This function is used internally by parse_values_std().
722 set_expr_value_store(const SelectionTreeElementPointer &sel,
723 gmx_ana_selparam_t *param, int i, void *scanner)
725 if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL))
727 std::string text(_gmx_sel_lexer_get_text(scanner, sel->location()));
729 = formatString("Expression '%s' is invalid in this context",
731 GMX_THROW(InvalidInputError(message));
735 case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break;
736 case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break;
737 case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break;
738 case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break;
739 case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break;
741 GMX_THROW(InternalError("Invalid value type"));
748 * Parses the values for a parameter that takes a constant number of values.
750 * \param[in] values List of values.
751 * \param param Parameter to parse.
752 * \param root Selection element to which child expressions are added.
753 * \param[in] scanner Scanner data structure.
755 * For integer ranges, the sequence of numbers from the first to second value
756 * is stored, each as a separate value.
759 parse_values_std(const SelectionParserValueList &values,
760 gmx_ana_selparam_t *param,
761 const SelectionTreeElementPointer &root, void *scanner)
766 /* Handle atom-valued parameters */
767 if (param->flags & SPAR_ATOMVAL)
769 if (values.size() > 1)
771 GMX_THROW(InvalidInputError(
772 "Only a single value or a single expression is "
773 "supported in this context"));
775 if (values.front().hasExpressionValue())
777 SelectionTreeElementPointer child
778 = add_child(root, param, values.front().expr, scanner);
779 child->flags |= SEL_ALLOCVAL;
780 if (child->v.type != GROUP_VALUE && (child->flags & SEL_ATOMVAL))
782 /* Rest of the initialization is done during compilation in
784 /* TODO: Positions are not correctly handled */
788 *param->nvalptr = -1;
792 param->flags &= ~SPAR_ATOMVAL;
798 param->nvalptr = nullptr;
799 if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
800 || param->val.type == STR_VALUE)
802 _gmx_selvalue_reserve(¶m->val, 1);
804 set_expr_value_store(child, param, 0, scanner);
807 /* If we reach here, proceed with normal parameter handling */
809 if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
810 || param->val.type == STR_VALUE)
812 _gmx_selvalue_reserve(¶m->val, 1);
814 param->flags &= ~SPAR_ATOMVAL;
815 param->flags &= ~SPAR_DYNAMIC;
820 SelectionParserValueList::const_iterator value;
821 for (value = values.begin(); value != values.end() && i < param->val.nr; ++value)
823 GMX_RELEASE_ASSERT(value->type == param->val.type,
824 "Invalid value type (should have been caught earlier)");
825 if (value->hasExpressionValue())
827 SelectionTreeElementPointer child
828 = add_child(root, param, value->expr, scanner);
829 set_expr_value_store(child, param, i, scanner);
830 if (child->flags & SEL_DYNAMIC)
837 /* Value is not an expression */
843 if (value->u.i.i1 <= value->u.i.i2)
845 for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j)
847 param->val.u.i[i++] = j;
849 bTooManyValues = (j != value->u.i.i2 + 1);
853 for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j)
855 param->val.u.i[i++] = j;
857 bTooManyValues = (j != value->u.i.i2 - 1);
861 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
863 = formatString("Range ('%s') produces more values than is "
864 "accepted in this context",
866 GMX_THROW(InvalidInputError(message));
872 if (value->u.r.r1 != value->u.r.r2)
874 std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
876 = formatString("Real range ('%s') is not supported in this context",
878 GMX_THROW(InvalidInputError(message));
880 param->val.u.r[i] = value->u.r.r1;
883 param->val.u.s[i] = gmx_strdup(value->stringValue().c_str());
886 gmx_ana_pos_init_const(¶m->val.u.p[i], value->u.x);
890 GMX_THROW(InternalError("Invalid non-expression value type"));
895 if (value != values.end())
898 = formatString("Too many values provided, expected %d",
900 GMX_THROW(InvalidInputError(message));
902 if (i < param->val.nr)
905 = formatString("Too few values provided, expected %d",
907 GMX_THROW(InvalidInputError(message));
911 param->flags &= ~SPAR_DYNAMIC;
915 *param->nvalptr = param->val.nr;
917 param->nvalptr = nullptr;
921 * Parses the values for a boolean parameter.
923 * \param[in] name Name by which the parameter was given.
924 * \param[in] values List of values.
925 * \param param Parameter to parse.
926 * \param[in] scanner Scanner data structure.
929 parse_values_bool(const std::string &name,
930 const SelectionParserValueList &values,
931 gmx_ana_selparam_t *param, void *scanner)
933 GMX_UNUSED_VALUE(scanner);
934 GMX_ASSERT(param->val.type == NO_VALUE,
935 "Boolean parser called for non-boolean parameter");
936 if (values.size() > 1 || (!values.empty() && values.front().type != INT_VALUE))
939 = formatString("'%s' only accepts yes/no/on/off/0/1 (and empty) as a value",
941 GMX_THROW(InvalidInputError(message));
945 /* Check if the parameter name is given with a 'no' prefix */
946 if (name.length() > 2 && name[0] == 'n' && name[1] == 'o'
947 && name.compare(2, name.length() - 2, param->name) == 0)
951 if (bSetNo && !values.empty())
954 = formatString("'no%s' cannot be followed by any value",
956 GMX_THROW(InvalidInputError(message));
958 if (!values.empty() && values.front().u.i.i1 == 0)
963 *param->val.u.b = !bSetNo;
967 * Parses the values for an enumeration parameter.
969 * \param[in] values List of values.
970 * \param param Parameter to parse.
971 * \param[in] scanner Scanner data structure.
972 * \returns true if the values were parsed successfully, false otherwise.
975 parse_values_enum(const SelectionParserValueList &values,
976 gmx_ana_selparam_t *param,
979 GMX_ASSERT(param->val.type == STR_VALUE,
980 "Enum parser called for non-string parameter");
981 if (values.size() != 1)
983 GMX_THROW(InvalidInputError(
984 "Only a single string value is supported in this context"));
986 const SelectionParserValue &value = values.front();
987 GMX_RELEASE_ASSERT(value.type == param->val.type,
988 "Invalid value type (should have been caught earlier)");
989 if (value.hasExpressionValue())
991 std::string text(_gmx_sel_lexer_get_text(scanner, value.location()));
993 = formatString("Expression ('%s') is not supported in this context",
995 GMX_THROW(InvalidInputError(message));
998 const std::string &svalue = value.stringValue();
1001 while (param->val.u.s[i] != nullptr)
1003 if (startsWith(param->val.u.s[i], svalue))
1005 /* Check if there is a duplicate match */
1009 = formatString("Value '%s' is ambiguous", svalue.c_str());
1010 GMX_THROW(InvalidInputError(message));
1019 = formatString("Value '%s' is not recognized", svalue.c_str());
1020 GMX_THROW(InvalidInputError(message));
1022 param->val.u.s[0] = param->val.u.s[match];
1026 * Replaces constant expressions with their values.
1028 * \param[in,out] values First element in the value list to process.
1031 convert_const_values(SelectionParserValueList *values)
1033 SelectionParserValueList::iterator value;
1034 for (value = values->begin(); value != values->end(); ++value)
1036 if (value->hasExpressionValue() && value->expr->v.type != GROUP_VALUE &&
1037 value->expr->type == SEL_CONST)
1039 SelectionTreeElementPointer expr = value->expr;
1040 const SelectionLocation &location = value->location();
1041 switch (expr->v.type)
1044 *value = SelectionParserValue::createInteger(expr->v.u.i[0], location);
1047 *value = SelectionParserValue::createReal(expr->v.u.r[0], location);
1050 *value = SelectionParserValue::createString(expr->v.u.s[0], location);
1053 *value = SelectionParserValue::createPosition(expr->v.u.p->x[0], location);
1056 GMX_RELEASE_ASSERT(false,
1057 "Unsupported constant expression value type");
1064 * \param pparams List of parameters from the selection parser.
1065 * \param[in] nparam Number of parameters in \p params.
1066 * \param params Array of parameters to parse.
1067 * \param root Selection element to which child expressions are added.
1068 * \param[in] scanner Scanner data structure.
1070 * Initializes the \p params array based on the parameters in \p pparams.
1071 * See the documentation of \c gmx_ana_selparam_t for different options
1072 * available for parsing.
1074 * The list \p pparams and any associated values are freed after the parameters
1075 * have been processed, no matter is there was an error or not.
1078 _gmx_sel_parse_params(const gmx::SelectionParserParameterList &pparams,
1079 int nparam, gmx_ana_selparam_t *params,
1080 const gmx::SelectionTreeElementPointer &root,
1083 ExceptionInitializer errors("");
1084 /* Check that the value pointers of SPAR_VARNUM parameters are NULL and
1085 * that they are not NULL for other parameters */
1086 for (int i = 0; i < nparam; ++i)
1088 if (params[i].val.type != POS_VALUE
1089 && (params[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
1091 GMX_RELEASE_ASSERT(params[i].val.u.ptr == nullptr,
1092 "value pointer is not NULL "
1093 "although it should be for SPAR_VARNUM "
1094 "and SPAR_ATOMVAL parameters");
1095 GMX_RELEASE_ASSERT(!((params[i].flags & SPAR_VARNUM)
1096 && (params[i].flags & SPAR_DYNAMIC))
1097 || params[i].nvalptr != nullptr,
1098 "nvalptr is NULL but both "
1099 "SPAR_VARNUM and SPAR_DYNAMIC are specified");
1103 GMX_RELEASE_ASSERT(params[i].val.u.ptr != nullptr,
1104 "value pointer is NULL");
1107 /* Parse the parameters */
1108 int nullParamIndex = 0;
1109 SelectionParserParameterList::const_iterator pparam;
1110 for (pparam = pparams.begin(); pparam != pparams.end(); ++pparam)
1114 // Always assigned afterwards, but cppcheck does not see that.
1115 gmx_ana_selparam_t *oparam = nullptr;
1116 /* Find the parameter and make some checks */
1117 if (!pparam->name().empty())
1119 nullParamIndex = -1;
1121 = gmx_ana_selparam_find(pparam->name().c_str(), nparam, params);
1122 GMX_RELEASE_ASSERT(oparam != nullptr, "Inconsistent selection parameter");
1124 else if (nullParamIndex >= 0)
1126 oparam = ¶ms[nullParamIndex];
1127 if (oparam->name != nullptr)
1129 std::string text(_gmx_sel_lexer_get_text(scanner, pparam->location()));
1131 = formatString("Unexpected '%s'", text.c_str());
1132 GMX_THROW(InvalidInputError(message));
1138 GMX_RELEASE_ASSERT(false, "All NULL parameters should appear in "
1139 "the beginning of the list");
1141 if (oparam->flags & SPAR_SET)
1144 = formatString("'%s' appears multiple times",
1145 pparam->name().c_str());
1146 GMX_THROW(InvalidInputError(message));
1148 oparam->flags |= SPAR_SET;
1149 if (oparam->val.type != NO_VALUE && pparam->values().empty())
1152 if (pparam->name().empty())
1154 text = root->name();
1158 text = _gmx_sel_lexer_get_text(scanner, pparam->location());
1161 = formatString("'%s' should be followed by a value/expression",
1163 GMX_THROW(InvalidInputError(message));
1165 /* Process the values for the parameter */
1166 convert_const_values(pparam->values_.get());
1167 convert_values(pparam->values_.get(), oparam->val.type, scanner);
1168 if (oparam->val.type == NO_VALUE)
1170 parse_values_bool(pparam->name(), pparam->values(), oparam, scanner);
1172 else if (oparam->flags & SPAR_RANGES)
1174 parse_values_range(pparam->values(), oparam, scanner);
1176 else if (oparam->flags & SPAR_VARNUM)
1178 if (pparam->values().size() == 1
1179 && pparam->values().front().hasExpressionValue())
1181 parse_values_varnum_expr(pparam->values(), oparam, root, scanner);
1185 parse_values_varnum(pparam->values(), oparam, root, scanner);
1188 else if (oparam->flags & SPAR_ENUMVAL)
1190 parse_values_enum(pparam->values(), oparam, scanner);
1194 parse_values_std(pparam->values(), oparam, root, scanner);
1197 catch (UserInputError &ex)
1199 if (!pparam->name().empty())
1201 std::string text(_gmx_sel_lexer_get_text(scanner, pparam->location()));
1202 ex.prependContext(formatString("In '%s'", text.c_str()));
1204 errors.addCurrentExceptionAsNested();
1207 /* Check that all required parameters are present */
1208 for (int i = 0; i < nparam; ++i)
1210 if (!(params[i].flags & SPAR_OPTIONAL) && !(params[i].flags & SPAR_SET))
1212 std::string message;
1213 if (params[i].name == nullptr)
1215 message = formatString("'%s' should be followed by a value/expression",
1216 root->name().c_str());
1220 message = formatString("'%s' is missing", params[i].name);
1222 InvalidInputError ex(message);
1223 errors.addNested(ex);
1226 if (errors.hasNestedExceptions())
1228 GMX_THROW(InvalidInputError(errors));