2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, 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
48 #include "gromacs/math/units.h"
49 #include "gromacs/math/vec.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"
61 #include "selmethod.h"
67 * \param[in] name Name of the parameter to search.
68 * \param[in] nparam Number of parameters in the \p param array.
69 * \param[in] param Parameter array to search.
70 * \returns Pointer to the parameter in the \p param
71 * or NULL if no parameter with name \p name was found.
73 * The comparison is case-sensitive.
76 gmx_ana_selparam_find(const char *name
, int nparam
, gmx_ana_selparam_t
*param
)
84 /* Find the first non-null parameter */
86 while (i
< nparam
&& param
[i
].name
== nullptr)
90 /* Process the special case of a NULL parameter */
93 return (i
== 0) ? nullptr : ¶m
[i
-1];
95 for (; i
< nparam
; ++i
)
97 if (!strcmp(param
[i
].name
, name
))
101 /* Check for 'no' prefix on boolean parameters */
102 if (param
[i
].val
.type
== NO_VALUE
103 && strlen(name
) > 2 && name
[0] == 'n' && name
[1] == 'o'
104 && !strcmp(param
[i
].name
, name
+2))
113 * Does a type conversion on a SelectionParserValue.
115 * \param[in,out] value Value to convert.
116 * \param[in] type Type to convert to.
117 * \param[in] errors Errors will be reported into this as nested exceptions.
118 * \param[in] scanner Scanner data structure.
121 convert_value(SelectionParserValue
*value
, e_selvalue_t type
,
122 ExceptionInitializer
*errors
, void *scanner
)
124 if (value
->type
== type
|| type
== NO_VALUE
)
128 if (value
->hasExpressionValue())
130 /* Conversion from atom selection to position using default
131 * reference positions. */
132 if (value
->type
== GROUP_VALUE
&& type
== POS_VALUE
)
136 SelectionTreeElementPointer expr
=
137 _gmx_sel_init_position(value
->expr
, nullptr, scanner
);
138 *value
= SelectionParserValue::createExpr(expr
);
140 catch (UserInputError
&ex
)
142 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
143 std::string
context(formatString("In '%s'", text
.c_str()));
144 ex
.prependContext(context
);
145 errors
->addCurrentExceptionAsNested();
152 /* Integers to floating point are easy */
153 if (value
->type
== INT_VALUE
&& type
== REAL_VALUE
)
155 *value
= SelectionParserValue::createRealRange(value
->u
.i
.i1
,
160 /* Reals that are integer-valued can also be converted */
161 if (value
->type
== REAL_VALUE
&& type
== INT_VALUE
)
163 int i1
= static_cast<int>(value
->u
.r
.r1
);
164 int i2
= static_cast<int>(value
->u
.r
.r2
);
165 if (gmx_within_tol(value
->u
.r
.r1
, i1
, GMX_REAL_EPS
)
166 && gmx_within_tol(value
->u
.r
.r2
, i2
, GMX_REAL_EPS
))
168 *value
= SelectionParserValue::createIntegerRange(i1
, i2
, value
->location());
173 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
175 formatString("Expression '%s' evaluates to a type is not valid in this context",
177 InvalidInputError
ex(message
);
178 errors
->addNested(ex
);
182 * Does a type conversion on a list of values.
184 * \param[in,out] values Values to convert.
185 * \param[in] type Type to convert to.
186 * \param[in] scanner Scanner data structure.
189 convert_values(SelectionParserValueList
*values
, e_selvalue_t type
, void *scanner
)
191 ExceptionInitializer
errors("");
192 SelectionParserValueList::iterator value
;
193 for (value
= values
->begin(); value
!= values
->end(); ++value
)
195 convert_value(&*value
, type
, &errors
, scanner
);
197 if (errors
.hasNestedExceptions())
199 GMX_THROW(InvalidInputError(errors
));
204 * Adds a child element for a parameter, keeping the parameter order.
206 * \param[in,out] root Root element to which the child is added.
207 * \param[in] child Child to add.
208 * \param[in] param Parameter for which this child is a value.
210 * Puts \p child in the child list of \p root such that the list remains
211 * in the same order as the corresponding parameters.
214 place_child(const SelectionTreeElementPointer
&root
,
215 const SelectionTreeElementPointer
&child
,
216 gmx_ana_selparam_t
*param
)
218 gmx_ana_selparam_t
*ps
;
221 ps
= root
->u
.expr
.method
->param
;
223 /* Put the child element in the correct place */
224 if (!root
->child
|| n
< root
->child
->u
.param
- ps
)
226 child
->next
= root
->child
;
231 SelectionTreeElementPointer prev
= root
->child
;
232 while (prev
->next
&& prev
->next
->u
.param
- ps
>= n
)
236 child
->next
= prev
->next
;
242 * Comparison function for sorting ranges.
244 * \param[in] a First range.
245 * \param[in] b Second range.
246 * \returns return true if a < b
248 * The ranges are primarily sorted based on their starting point, and
249 * secondarily based on length (longer ranges come first).
252 static bool cmp_range(const std::array
<T
, 2> &a
, const std::array
<T
, 2> &b
)
254 return a
[0] < b
[0] || (a
[0] == b
[0] && a
[1] > b
[1]);
258 * Parses the values for a parameter that takes integer or real ranges.
260 * \param[in] values List of values.
261 * \param param Parameter to parse.
262 * \param[in] scanner Scanner data structure.
265 parse_values_range(const SelectionParserValueList
&values
,
266 gmx_ana_selparam_t
*param
, void *scanner
)
270 param
->flags
&= ~SPAR_DYNAMIC
;
271 GMX_RELEASE_ASSERT(param
->val
.type
== INT_VALUE
|| param
->val
.type
== REAL_VALUE
,
272 "Invalid range parameter type");
273 int *idata
= nullptr;
274 real
*rdata
= nullptr;
275 sfree_guard dataGuard
;
276 if (param
->val
.type
== INT_VALUE
)
278 snew(idata
, values
.size()*2);
279 dataGuard
.reset(idata
);
283 snew(rdata
, values
.size()*2);
284 dataGuard
.reset(rdata
);
287 SelectionParserValueList::const_iterator value
;
288 for (value
= values
.begin(); value
!= values
.end(); ++value
)
290 GMX_RELEASE_ASSERT(value
->type
== param
->val
.type
,
291 "Invalid range value type (should have been caught earlier)");
292 if (value
->hasExpressionValue())
294 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
295 std::string
message("Only simple values or 'A to B' ranges are "
296 "supported in this context");
297 InvalidInputError
ex(message
);
298 ex
.prependContext(formatString("Invalid expression '%s'", text
.c_str()));
301 if (param
->val
.type
== INT_VALUE
)
303 int i1
= std::min(value
->u
.i
.i1
, value
->u
.i
.i2
);
304 int i2
= std::max(value
->u
.i
.i1
, value
->u
.i
.i2
);
305 /* Check if the new range overlaps or extends the previous one */
306 if (i
> 0 && i1
<= idata
[i
-1]+1 && i2
>= idata
[i
-2]-1)
308 idata
[i
-2] = std::min(idata
[i
-2], i1
);
309 idata
[i
-1] = std::max(idata
[i
-1], i2
);
319 real r1
= std::min(value
->u
.r
.r1
, value
->u
.r
.r2
);
320 real r2
= std::max(value
->u
.r
.r1
, value
->u
.r
.r2
);
321 /* Check if the new range overlaps or extends the previous one */
322 if (i
> 0 && r1
<= rdata
[i
-1] && r2
>= rdata
[i
-2])
324 rdata
[i
-2] = std::min(rdata
[i
-2], r1
);
325 rdata
[i
-1] = std::max(rdata
[i
-1], r2
);
335 /* Sort the ranges and merge consequent ones */
336 if (param
->val
.type
== INT_VALUE
)
338 const auto range_data
= reinterpret_cast<std::array
<int, 2>*>(idata
);
339 sort(range_data
, range_data
+n
, cmp_range
<int>);
340 for (i
= j
= 2; i
< 2*n
; i
+= 2)
342 if (idata
[j
-1]+1 >= idata
[i
])
344 if (idata
[i
+1] > idata
[j
-1])
346 idata
[j
-1] = idata
[i
+1];
352 idata
[j
+1] = idata
[i
+1];
359 const auto range_data
= reinterpret_cast<std::array
<real
, 2>*>(rdata
);
360 sort(range_data
, range_data
+n
, cmp_range
<real
>);
361 for (i
= j
= 2; i
< 2*n
; i
+= 2)
363 if (rdata
[j
-1] >= rdata
[i
])
365 if (rdata
[i
+1] > rdata
[j
-1])
367 rdata
[j
-1] = rdata
[i
+1];
373 rdata
[j
+1] = rdata
[i
+1];
379 /* Store the values */
380 if (param
->flags
& SPAR_VARNUM
)
382 (void)dataGuard
.release();
384 if (param
->val
.type
== INT_VALUE
)
387 _gmx_selvalue_setstore_alloc(¶m
->val
, idata
, j
);
392 _gmx_selvalue_setstore_alloc(¶m
->val
, rdata
, j
);
397 if (n
!= param
->val
.nr
)
400 "Range parameters with a fixed count > 1 do not make sense");
401 GMX_THROW(InvalidInputError("Only one value or 'A to B' range is "
402 "supported in this context"));
404 if (param
->val
.type
== INT_VALUE
)
406 memcpy(param
->val
.u
.i
, idata
, 2*n
*sizeof(int));
410 memcpy(param
->val
.u
.r
, rdata
, 2*n
*sizeof(real
));
415 *param
->nvalptr
= param
->val
.nr
;
417 param
->nvalptr
= nullptr;
421 * Parses the values for a parameter that takes a variable number of values.
423 * \param[in] values List of values.
424 * \param param Parameter to parse.
425 * \param root Selection element to which child expressions are added.
426 * \param[in] scanner Scanner data structure.
428 * For integer ranges, the sequence of numbers from the first to second value
429 * is stored, each as a separate value.
432 parse_values_varnum(const SelectionParserValueList
&values
,
433 gmx_ana_selparam_t
*param
,
434 const SelectionTreeElementPointer
&root
,
439 param
->flags
&= ~SPAR_DYNAMIC
;
440 /* Compute number of values, considering also integer ranges. */
441 int valueCount
= ssize(values
);
442 if (param
->val
.type
== INT_VALUE
)
444 SelectionParserValueList::const_iterator value
;
445 for (value
= values
.begin(); value
!= values
.end(); ++value
)
447 if (value
->type
== INT_VALUE
&& !value
->hasExpressionValue())
449 valueCount
+= abs(value
->u
.i
.i2
- value
->u
.i
.i1
);
454 /* Check that the value type is actually implemented */
455 if (param
->val
.type
!= INT_VALUE
&& param
->val
.type
!= REAL_VALUE
456 && param
->val
.type
!= STR_VALUE
&& param
->val
.type
!= POS_VALUE
)
458 GMX_THROW(InternalError("Variable-count value type not implemented"));
461 /* Reserve appropriate amount of memory */
462 if (param
->val
.type
== POS_VALUE
)
464 gmx_ana_pos_reserve(param
->val
.u
.p
, valueCount
, 0);
465 gmx_ana_indexmap_init(¶m
->val
.u
.p
->m
, nullptr, nullptr, INDEX_UNKNOWN
);
466 gmx_ana_pos_set_nr(param
->val
.u
.p
, valueCount
);
470 _gmx_selvalue_reserve(¶m
->val
, valueCount
);
472 /* Create a dummy child element to store the string values.
473 * This element is responsible for freeing the values, but carries no
475 if (param
->val
.type
== STR_VALUE
)
477 SelectionTreeElementPointer
child(
478 new SelectionTreeElement(SEL_CONST
, SelectionLocation::createEmpty()));
479 _gmx_selelem_set_vtype(child
, STR_VALUE
);
480 child
->setName(param
->name
);
481 child
->flags
&= ~SEL_ALLOCVAL
;
482 child
->flags
|= SEL_FLAGSSET
| SEL_VARNUMVAL
| SEL_ALLOCDATA
;
483 child
->v
.nr
= valueCount
;
484 _gmx_selvalue_setstore(&child
->v
, param
->val
.u
.s
);
485 /* Because the child is not group-valued, the u union is not used
486 * for anything, so we can abuse it by storing the parameter value
487 * as place_child() expects, but this is really ugly... */
488 child
->u
.param
= param
;
489 place_child(root
, child
, param
);
491 param
->val
.nr
= valueCount
;
494 SelectionParserValueList::const_iterator value
;
495 for (value
= values
.begin(); value
!= values
.end(); ++value
)
497 GMX_RELEASE_ASSERT(value
->type
== param
->val
.type
,
498 "Invalid value type (should have been caught earlier)");
499 if (value
->hasExpressionValue())
501 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
502 std::string
message("Selection expressions are not supported in this "
503 "context when multiple values are provided");
504 InvalidInputError
ex(message
);
505 ex
.prependContext(formatString("Invalid expression '%s'", text
.c_str()));
508 switch (param
->val
.type
)
511 if (value
->u
.i
.i1
<= value
->u
.i
.i2
)
513 for (j
= value
->u
.i
.i1
; j
<= value
->u
.i
.i2
; ++j
)
515 param
->val
.u
.i
[i
++] = j
;
520 for (j
= value
->u
.i
.i1
; j
>= value
->u
.i
.i2
; --j
)
522 param
->val
.u
.i
[i
++] = j
;
527 if (value
->u
.r
.r1
!= value
->u
.r
.r2
)
529 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
531 = formatString("Real range ('%s') is not supported in this context",
533 InvalidInputError
ex(message
);
536 param
->val
.u
.r
[i
++] = value
->u
.r
.r1
;
539 param
->val
.u
.s
[i
++] = gmx_strdup(value
->stringValue().c_str());
541 case POS_VALUE
: copy_rvec(value
->u
.x
, param
->val
.u
.p
->x
[i
++]); break;
542 default: /* Should not be reached */
543 GMX_RELEASE_ASSERT(false, "Variable-count value type not implemented");
546 GMX_RELEASE_ASSERT(i
== valueCount
,
547 "Inconsistent value count wrt. the actual value population");
550 *param
->nvalptr
= param
->val
.nr
;
552 param
->nvalptr
= nullptr;
556 * Adds a new subexpression reference to a selection element.
558 * \param[in,out] root Root element to which the subexpression is added.
559 * \param[in] param Parameter for which this expression is a value.
560 * \param[in] expr Expression to add.
561 * \param[in] scanner Scanner data structure.
562 * \returns The created child element.
564 * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child
566 * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is.
567 * \ref SEL_ALLOCVAL is cleared for the returned element.
569 static SelectionTreeElementPointer
570 add_child(const SelectionTreeElementPointer
&root
, gmx_ana_selparam_t
*param
,
571 const SelectionTreeElementPointer
&expr
, void *scanner
)
573 GMX_RELEASE_ASSERT(root
->type
== SEL_EXPRESSION
|| root
->type
== SEL_MODIFIER
,
574 "Unsupported root element for selection parameter parser");
575 SelectionTreeElementPointer child
;
576 /* Create a subexpression reference element if necessary */
577 if (expr
->type
== SEL_SUBEXPRREF
)
583 // TODO: Initialize such that it includes the parameter.
584 child
= std::make_shared
<SelectionTreeElement
>(SEL_SUBEXPRREF
, expr
->location());
585 _gmx_selelem_set_vtype(child
, expr
->v
.type
);
588 /* Setup the child element */
589 child
->flags
&= ~SEL_ALLOCVAL
;
590 child
->u
.param
= param
;
591 if (child
->v
.type
!= param
->val
.type
)
593 // TODO: It would be nice to say what is the expected type.
594 std::string
text(_gmx_sel_lexer_get_text(scanner
, expr
->location()));
596 = formatString("Expression '%s' is not valid in this context "
597 "(produces the wrong type of values)",
599 GMX_THROW(InvalidInputError(message
));
601 _gmx_selelem_update_flags(child
);
602 if ((child
->flags
& SEL_DYNAMIC
) && !(param
->flags
& SPAR_DYNAMIC
))
604 std::string
text(_gmx_sel_lexer_get_text(scanner
, expr
->location()));
606 = formatString("Expression '%s' is dynamic, which is not "
607 "valid in this context",
609 GMX_THROW(InvalidInputError(message
));
611 if (!(child
->flags
& SEL_DYNAMIC
))
613 param
->flags
&= ~SPAR_DYNAMIC
;
615 /* Put the child element in the correct place */
616 place_child(root
, child
, param
);
621 * Parses an expression value for a parameter that takes a variable number of values.
623 * \param[in] values List of values.
624 * \param param Parameter to parse.
625 * \param root Selection element to which child expressions are added.
626 * \param[in] scanner Scanner data structure.
629 parse_values_varnum_expr(const SelectionParserValueList
&values
,
630 gmx_ana_selparam_t
*param
,
631 const SelectionTreeElementPointer
&root
,
634 GMX_RELEASE_ASSERT(values
.size() == 1 && values
.front().hasExpressionValue(),
635 "Called with an invalid type of value");
637 SelectionTreeElementPointer child
638 = add_child(root
, param
, values
.front().expr
, scanner
);
640 /* Process single-valued expressions */
641 /* TODO: We should also handle SEL_SINGLEVAL expressions here */
642 if (child
->v
.type
== POS_VALUE
|| child
->v
.type
== GROUP_VALUE
)
644 /* Set the value storage */
645 _gmx_selvalue_setstore(&child
->v
, param
->val
.u
.ptr
);
649 *param
->nvalptr
= param
->val
.nr
;
651 param
->nvalptr
= nullptr;
655 if (!(child
->flags
& SEL_VARNUMVAL
))
657 std::string
text(_gmx_sel_lexer_get_text(scanner
, values
.front().location()));
659 = formatString("Expression '%s' is invalid in this context",
661 GMX_THROW(InvalidInputError(message
));
664 child
->flags
|= SEL_ALLOCVAL
;
666 *param
->nvalptr
= param
->val
.nr
;
667 /* Rest of the initialization is done during compilation in
672 * Initializes the storage of an expression value.
674 * \param[in,out] sel Selection element that evaluates the value.
675 * \param[in] param Parameter to receive the value.
676 * \param[in] i The value of \p sel evaluates the value \p i for
678 * \param[in] scanner Scanner data structure.
680 * Initializes the data pointer of \p sel such that the result is stored
681 * as the value \p i of \p param.
682 * This function is used internally by parse_values_std().
685 set_expr_value_store(const SelectionTreeElementPointer
&sel
,
686 gmx_ana_selparam_t
*param
, int i
, void *scanner
)
688 if (sel
->v
.type
!= GROUP_VALUE
&& !(sel
->flags
& SEL_SINGLEVAL
))
690 std::string
text(_gmx_sel_lexer_get_text(scanner
, sel
->location()));
692 = formatString("Expression '%s' is invalid in this context",
694 GMX_THROW(InvalidInputError(message
));
698 case INT_VALUE
: sel
->v
.u
.i
= ¶m
->val
.u
.i
[i
]; break;
699 case REAL_VALUE
: sel
->v
.u
.r
= ¶m
->val
.u
.r
[i
]; break;
700 case STR_VALUE
: sel
->v
.u
.s
= ¶m
->val
.u
.s
[i
]; break;
701 case POS_VALUE
: sel
->v
.u
.p
= ¶m
->val
.u
.p
[i
]; break;
702 case GROUP_VALUE
: sel
->v
.u
.g
= ¶m
->val
.u
.g
[i
]; break;
704 GMX_THROW(InternalError("Invalid value type"));
711 * Parses the values for a parameter that takes a constant number of values.
713 * \param[in] values List of values.
714 * \param param Parameter to parse.
715 * \param root Selection element to which child expressions are added.
716 * \param[in] scanner Scanner data structure.
718 * For integer ranges, the sequence of numbers from the first to second value
719 * is stored, each as a separate value.
722 parse_values_std(const SelectionParserValueList
&values
,
723 gmx_ana_selparam_t
*param
,
724 const SelectionTreeElementPointer
&root
, void *scanner
)
729 /* Handle atom-valued parameters */
730 if (param
->flags
& SPAR_ATOMVAL
)
732 if (values
.size() > 1)
734 GMX_THROW(InvalidInputError(
735 "Only a single value or a single expression is "
736 "supported in this context"));
738 if (values
.front().hasExpressionValue())
740 SelectionTreeElementPointer child
741 = add_child(root
, param
, values
.front().expr
, scanner
);
742 child
->flags
|= SEL_ALLOCVAL
;
743 if (child
->v
.type
!= GROUP_VALUE
&& (child
->flags
& SEL_ATOMVAL
))
745 /* Rest of the initialization is done during compilation in
747 /* TODO: Positions are not correctly handled */
751 *param
->nvalptr
= -1;
755 param
->flags
&= ~SPAR_ATOMVAL
;
761 param
->nvalptr
= nullptr;
762 if (param
->val
.type
== INT_VALUE
|| param
->val
.type
== REAL_VALUE
763 || param
->val
.type
== STR_VALUE
)
765 _gmx_selvalue_reserve(¶m
->val
, 1);
767 set_expr_value_store(child
, param
, 0, scanner
);
770 /* If we reach here, proceed with normal parameter handling */
772 if (param
->val
.type
== INT_VALUE
|| param
->val
.type
== REAL_VALUE
773 || param
->val
.type
== STR_VALUE
)
775 _gmx_selvalue_reserve(¶m
->val
, 1);
777 param
->flags
&= ~SPAR_ATOMVAL
;
778 param
->flags
&= ~SPAR_DYNAMIC
;
783 SelectionParserValueList::const_iterator value
;
784 for (value
= values
.begin(); value
!= values
.end() && i
< param
->val
.nr
; ++value
)
786 GMX_RELEASE_ASSERT(value
->type
== param
->val
.type
,
787 "Invalid value type (should have been caught earlier)");
788 if (value
->hasExpressionValue())
790 SelectionTreeElementPointer child
791 = add_child(root
, param
, value
->expr
, scanner
);
792 set_expr_value_store(child
, param
, i
, scanner
);
793 if (child
->flags
& SEL_DYNAMIC
)
800 /* Value is not an expression */
806 if (value
->u
.i
.i1
<= value
->u
.i
.i2
)
808 for (j
= value
->u
.i
.i1
; j
<= value
->u
.i
.i2
&& i
< param
->val
.nr
; ++j
)
810 param
->val
.u
.i
[i
++] = j
;
812 bTooManyValues
= (j
!= value
->u
.i
.i2
+ 1);
816 for (j
= value
->u
.i
.i1
; j
>= value
->u
.i
.i2
&& i
< param
->val
.nr
; --j
)
818 param
->val
.u
.i
[i
++] = j
;
820 bTooManyValues
= (j
!= value
->u
.i
.i2
- 1);
824 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
826 = formatString("Range ('%s') produces more values than is "
827 "accepted in this context",
829 GMX_THROW(InvalidInputError(message
));
835 if (value
->u
.r
.r1
!= value
->u
.r
.r2
)
837 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
->location()));
839 = formatString("Real range ('%s') is not supported in this context",
841 GMX_THROW(InvalidInputError(message
));
843 param
->val
.u
.r
[i
] = value
->u
.r
.r1
;
846 param
->val
.u
.s
[i
] = gmx_strdup(value
->stringValue().c_str());
849 gmx_ana_pos_init_const(¶m
->val
.u
.p
[i
], value
->u
.x
);
853 GMX_THROW(InternalError("Invalid non-expression value type"));
858 if (value
!= values
.end())
861 = formatString("Too many values provided, expected %d",
863 GMX_THROW(InvalidInputError(message
));
865 if (i
< param
->val
.nr
)
868 = formatString("Too few values provided, expected %d",
870 GMX_THROW(InvalidInputError(message
));
874 param
->flags
&= ~SPAR_DYNAMIC
;
878 *param
->nvalptr
= param
->val
.nr
;
880 param
->nvalptr
= nullptr;
884 * Parses the values for a boolean parameter.
886 * \param[in] name Name by which the parameter was given.
887 * \param[in] values List of values.
888 * \param param Parameter to parse.
889 * \param[in] scanner Scanner data structure.
892 parse_values_bool(const std::string
&name
,
893 const SelectionParserValueList
&values
,
894 gmx_ana_selparam_t
*param
, void *scanner
)
896 GMX_UNUSED_VALUE(scanner
);
897 GMX_ASSERT(param
->val
.type
== NO_VALUE
,
898 "Boolean parser called for non-boolean parameter");
899 if (values
.size() > 1 || (!values
.empty() && values
.front().type
!= INT_VALUE
))
902 = formatString("'%s' only accepts yes/no/on/off/0/1 (and empty) as a value",
904 GMX_THROW(InvalidInputError(message
));
908 /* Check if the parameter name is given with a 'no' prefix */
909 if (name
.length() > 2 && name
[0] == 'n' && name
[1] == 'o'
910 && name
.compare(2, name
.length() - 2, param
->name
) == 0)
914 if (bSetNo
&& !values
.empty())
917 = formatString("'no%s' cannot be followed by any value",
919 GMX_THROW(InvalidInputError(message
));
921 if (!values
.empty() && values
.front().u
.i
.i1
== 0)
926 *param
->val
.u
.b
= !bSetNo
;
930 * Parses the values for an enumeration parameter.
932 * \param[in] values List of values.
933 * \param param Parameter to parse.
934 * \param[in] scanner Scanner data structure.
935 * \returns true if the values were parsed successfully, false otherwise.
938 parse_values_enum(const SelectionParserValueList
&values
,
939 gmx_ana_selparam_t
*param
,
942 GMX_ASSERT(param
->val
.type
== STR_VALUE
,
943 "Enum parser called for non-string parameter");
944 if (values
.size() != 1)
946 GMX_THROW(InvalidInputError(
947 "Only a single string value is supported in this context"));
949 const SelectionParserValue
&value
= values
.front();
950 GMX_RELEASE_ASSERT(value
.type
== param
->val
.type
,
951 "Invalid value type (should have been caught earlier)");
952 if (value
.hasExpressionValue())
954 std::string
text(_gmx_sel_lexer_get_text(scanner
, value
.location()));
956 = formatString("Expression ('%s') is not supported in this context",
958 GMX_THROW(InvalidInputError(message
));
961 const std::string
&svalue
= value
.stringValue();
964 while (param
->val
.u
.s
[i
] != nullptr)
966 if (startsWith(param
->val
.u
.s
[i
], svalue
))
968 /* Check if there is a duplicate match */
972 = formatString("Value '%s' is ambiguous", svalue
.c_str());
973 GMX_THROW(InvalidInputError(message
));
982 = formatString("Value '%s' is not recognized", svalue
.c_str());
983 GMX_THROW(InvalidInputError(message
));
985 param
->val
.u
.s
[0] = param
->val
.u
.s
[match
];
989 * Replaces constant expressions with their values.
991 * \param[in,out] values First element in the value list to process.
994 convert_const_values(SelectionParserValueList
*values
)
996 SelectionParserValueList::iterator value
;
997 for (value
= values
->begin(); value
!= values
->end(); ++value
)
999 if (value
->hasExpressionValue() && value
->expr
->v
.type
!= GROUP_VALUE
&&
1000 value
->expr
->type
== SEL_CONST
)
1002 SelectionTreeElementPointer expr
= value
->expr
;
1003 const SelectionLocation
&location
= value
->location();
1004 switch (expr
->v
.type
)
1007 *value
= SelectionParserValue::createInteger(expr
->v
.u
.i
[0], location
);
1010 *value
= SelectionParserValue::createReal(expr
->v
.u
.r
[0], location
);
1013 *value
= SelectionParserValue::createString(expr
->v
.u
.s
[0], location
);
1016 *value
= SelectionParserValue::createPosition(expr
->v
.u
.p
->x
[0], location
);
1019 GMX_RELEASE_ASSERT(false,
1020 "Unsupported constant expression value type");
1027 * \param pparams List of parameters from the selection parser.
1028 * \param[in] nparam Number of parameters in \p params.
1029 * \param params Array of parameters to parse.
1030 * \param root Selection element to which child expressions are added.
1031 * \param[in] scanner Scanner data structure.
1033 * Initializes the \p params array based on the parameters in \p pparams.
1034 * See the documentation of \c gmx_ana_selparam_t for different options
1035 * available for parsing.
1037 * The list \p pparams and any associated values are freed after the parameters
1038 * have been processed, no matter is there was an error or not.
1041 _gmx_sel_parse_params(const gmx::SelectionParserParameterList
&pparams
,
1042 int nparam
, gmx_ana_selparam_t
*params
,
1043 const gmx::SelectionTreeElementPointer
&root
,
1046 ExceptionInitializer
errors("");
1047 /* Check that the value pointers of SPAR_VARNUM parameters are NULL and
1048 * that they are not NULL for other parameters */
1049 for (int i
= 0; i
< nparam
; ++i
)
1051 if (params
[i
].val
.type
!= POS_VALUE
1052 && (params
[i
].flags
& (SPAR_VARNUM
| SPAR_ATOMVAL
)))
1054 GMX_RELEASE_ASSERT(params
[i
].val
.u
.ptr
== nullptr,
1055 "value pointer is not NULL "
1056 "although it should be for SPAR_VARNUM "
1057 "and SPAR_ATOMVAL parameters");
1058 GMX_RELEASE_ASSERT(!((params
[i
].flags
& SPAR_VARNUM
)
1059 && (params
[i
].flags
& SPAR_DYNAMIC
))
1060 || params
[i
].nvalptr
!= nullptr,
1061 "nvalptr is NULL but both "
1062 "SPAR_VARNUM and SPAR_DYNAMIC are specified");
1066 GMX_RELEASE_ASSERT(params
[i
].val
.u
.ptr
!= nullptr,
1067 "value pointer is NULL");
1070 /* Parse the parameters */
1071 int nullParamIndex
= 0;
1072 SelectionParserParameterList::const_iterator pparam
;
1073 for (pparam
= pparams
.begin(); pparam
!= pparams
.end(); ++pparam
)
1077 // Always assigned afterwards, but clang does not see that.
1078 gmx_ana_selparam_t
*oparam
= nullptr;
1079 /* Find the parameter and make some checks */
1080 if (!pparam
->name().empty())
1082 nullParamIndex
= -1;
1084 = gmx_ana_selparam_find(pparam
->name().c_str(), nparam
, params
);
1085 GMX_RELEASE_ASSERT(oparam
!= nullptr, "Inconsistent selection parameter");
1087 else if (nullParamIndex
>= 0)
1089 oparam
= ¶ms
[nullParamIndex
];
1090 if (oparam
->name
!= nullptr)
1092 std::string
text(_gmx_sel_lexer_get_text(scanner
, pparam
->location()));
1094 = formatString("Unexpected '%s'", text
.c_str());
1095 GMX_THROW(InvalidInputError(message
));
1101 GMX_RELEASE_ASSERT(false, "All NULL parameters should appear in "
1102 "the beginning of the list");
1104 if (oparam
->flags
& SPAR_SET
)
1107 = formatString("'%s' appears multiple times",
1108 pparam
->name().c_str());
1109 GMX_THROW(InvalidInputError(message
));
1111 oparam
->flags
|= SPAR_SET
;
1112 if (oparam
->val
.type
!= NO_VALUE
&& pparam
->values().empty())
1115 if (pparam
->name().empty())
1117 text
= root
->name();
1121 text
= _gmx_sel_lexer_get_text(scanner
, pparam
->location());
1124 = formatString("'%s' should be followed by a value/expression",
1126 GMX_THROW(InvalidInputError(message
));
1128 /* Process the values for the parameter */
1129 convert_const_values(pparam
->values_
.get());
1130 convert_values(pparam
->values_
.get(), oparam
->val
.type
, scanner
);
1131 if (oparam
->val
.type
== NO_VALUE
)
1133 parse_values_bool(pparam
->name(), pparam
->values(), oparam
, scanner
);
1135 else if (oparam
->flags
& SPAR_RANGES
)
1137 parse_values_range(pparam
->values(), oparam
, scanner
);
1139 else if (oparam
->flags
& SPAR_VARNUM
)
1141 if (pparam
->values().size() == 1
1142 && pparam
->values().front().hasExpressionValue())
1144 parse_values_varnum_expr(pparam
->values(), oparam
, root
, scanner
);
1148 parse_values_varnum(pparam
->values(), oparam
, root
, scanner
);
1151 else if (oparam
->flags
& SPAR_ENUMVAL
)
1153 parse_values_enum(pparam
->values(), oparam
, scanner
);
1157 parse_values_std(pparam
->values(), oparam
, root
, scanner
);
1160 catch (UserInputError
&ex
)
1162 if (!pparam
->name().empty())
1164 std::string
text(_gmx_sel_lexer_get_text(scanner
, pparam
->location()));
1165 ex
.prependContext(formatString("In '%s'", text
.c_str()));
1167 errors
.addCurrentExceptionAsNested();
1170 /* Check that all required parameters are present */
1171 for (int i
= 0; i
< nparam
; ++i
)
1173 if (!(params
[i
].flags
& SPAR_OPTIONAL
) && !(params
[i
].flags
& SPAR_SET
))
1175 std::string message
;
1176 if (params
[i
].name
== nullptr)
1178 message
= formatString("'%s' should be followed by a value/expression",
1179 root
->name().c_str());
1183 message
= formatString("'%s' is missing", params
[i
].name
);
1185 InvalidInputError
ex(message
);
1186 errors
.addNested(ex
);
1189 if (errors
.hasNestedExceptions())
1191 GMX_THROW(InvalidInputError(errors
));