Tidy: modernize-use-nullptr
[alexxy/gromacs.git] / src / gromacs / selection / params.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements functions in selparam.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include <algorithm>
45 #include <string>
46
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"
56
57 #include "parsetree.h"
58 #include "scanner.h"
59 #include "selelem.h"
60 #include "selmethod.h"
61 #include "selparam.h"
62
63 using namespace gmx;
64
65 /*!
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.
71  *
72  * The comparison is case-sensitive.
73  */
74 gmx_ana_selparam_t *
75 gmx_ana_selparam_find(const char *name, int nparam, gmx_ana_selparam_t *param)
76 {
77     int                i;
78
79     if (nparam == 0)
80     {
81         return nullptr;
82     }
83     /* Find the first non-null parameter */
84     i = 0;
85     while (i < nparam && param[i].name == nullptr)
86     {
87         ++i;
88     }
89     /* Process the special case of a NULL parameter */
90     if (name == nullptr)
91     {
92         return (i == 0) ? nullptr : &param[i-1];
93     }
94     for (; i < nparam; ++i)
95     {
96         if (!strcmp(param[i].name, name))
97         {
98             return &param[i];
99         }
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))
104         {
105             return &param[i];
106         }
107     }
108     return nullptr;
109 }
110
111 /*! \brief
112  * Does a type conversion on a SelectionParserValue.
113  *
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.
118  */
119 static void
120 convert_value(SelectionParserValue *value, e_selvalue_t type,
121               ExceptionInitializer *errors, void *scanner)
122 {
123     if (value->type == type || type == NO_VALUE)
124     {
125         return;
126     }
127     if (value->hasExpressionValue())
128     {
129         /* Conversion from atom selection to position using default
130          * reference positions. */
131         if (value->type == GROUP_VALUE && type == POS_VALUE)
132         {
133             try
134             {
135                 SelectionTreeElementPointer expr =
136                     _gmx_sel_init_position(value->expr, nullptr, scanner);
137                 *value = SelectionParserValue::createExpr(expr);
138             }
139             catch (UserInputError &ex)
140             {
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();
145             }
146             return;
147         }
148     }
149     else
150     {
151         /* Integers to floating point are easy */
152         if (value->type == INT_VALUE && type == REAL_VALUE)
153         {
154             *value = SelectionParserValue::createRealRange(value->u.i.i1,
155                                                            value->u.i.i2,
156                                                            value->location());
157             return;
158         }
159         /* Reals that are integer-valued can also be converted */
160         if (value->type == REAL_VALUE && type == INT_VALUE)
161         {
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))
166             {
167                 *value = SelectionParserValue::createIntegerRange(i1, i2, value->location());
168                 return;
169             }
170         }
171     }
172     std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
173     std::string message(
174             formatString("Expression '%s' evaluates to a type is not valid in this context",
175                          text.c_str()));
176     InvalidInputError ex(message);
177     errors->addNested(ex);
178 }
179
180 /*! \brief
181  * Does a type conversion on a list of values.
182  *
183  * \param[in,out] values   Values to convert.
184  * \param[in]     type     Type to convert to.
185  * \param[in]     scanner  Scanner data structure.
186  */
187 static void
188 convert_values(SelectionParserValueList *values, e_selvalue_t type, void *scanner)
189 {
190     ExceptionInitializer               errors("");
191     SelectionParserValueList::iterator value;
192     for (value = values->begin(); value != values->end(); ++value)
193     {
194         convert_value(&*value, type, &errors, scanner);
195     }
196     if (errors.hasNestedExceptions())
197     {
198         GMX_THROW(InvalidInputError(errors));
199     }
200 }
201
202 /*! \brief
203  * Adds a child element for a parameter, keeping the parameter order.
204  *
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.
208  *
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.
211  */
212 static void
213 place_child(const SelectionTreeElementPointer &root,
214             const SelectionTreeElementPointer &child,
215             gmx_ana_selparam_t                *param)
216 {
217     gmx_ana_selparam_t *ps;
218     int                 n;
219
220     ps = root->u.expr.method->param;
221     n  = param - ps;
222     /* Put the child element in the correct place */
223     if (!root->child || n < root->child->u.param - ps)
224     {
225         child->next = root->child;
226         root->child = child;
227     }
228     else
229     {
230         SelectionTreeElementPointer prev = root->child;
231         while (prev->next && prev->next->u.param - ps >= n)
232         {
233             prev = prev->next;
234         }
235         child->next = prev->next;
236         prev->next  = child;
237     }
238 }
239
240 /*! \brief
241  * Comparison function for sorting integer ranges.
242  *
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.
246  *
247  * The ranges are primarily sorted based on their starting point, and
248  * secondarily based on length (longer ranges come first).
249  */
250 static int
251 cmp_int_range(const void *a, const void *b)
252 {
253     if (((int *)a)[0] < ((int *)b)[0])
254     {
255         return -1;
256     }
257     if (((int *)a)[0] > ((int *)b)[0])
258     {
259         return 1;
260     }
261     if (((int *)a)[1] > ((int *)b)[1])
262     {
263         return -1;
264     }
265     return 0;
266 }
267
268 /*! \brief
269  * Comparison function for sorting real ranges.
270  *
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.
274  *
275  * The ranges are primarily sorted based on their starting point, and
276  * secondarily based on length (longer ranges come first).
277  */
278 static int
279 cmp_real_range(const void *a, const void *b)
280 {
281     if (((real *)a)[0] < ((real *)b)[0])
282     {
283         return -1;
284     }
285     if (((real *)a)[0] > ((real *)b)[0])
286     {
287         return 1;
288     }
289     if (((real *)a)[1] > ((real *)b)[1])
290     {
291         return -1;
292     }
293     return 0;
294 }
295
296 /*! \brief
297  * Parses the values for a parameter that takes integer or real ranges.
298  *
299  * \param[in] values List of values.
300  * \param     param  Parameter to parse.
301  * \param[in] scanner Scanner data structure.
302  */
303 static void
304 parse_values_range(const SelectionParserValueList &values,
305                    gmx_ana_selparam_t *param, void *scanner)
306 {
307     int                 i, j, n;
308
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)
316     {
317         snew(idata, values.size()*2);
318         dataGuard.reset(idata);
319     }
320     else
321     {
322         snew(rdata, values.size()*2);
323         dataGuard.reset(rdata);
324     }
325     i = 0;
326     SelectionParserValueList::const_iterator value;
327     for (value = values.begin(); value != values.end(); ++value)
328     {
329         GMX_RELEASE_ASSERT(value->type == param->val.type,
330                            "Invalid range value type (should have been caught earlier)");
331         if (value->hasExpressionValue())
332         {
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()));
338             GMX_THROW(ex);
339         }
340         if (param->val.type == INT_VALUE)
341         {
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)
346             {
347                 idata[i-2] = std::min(idata[i-2], i1);
348                 idata[i-1] = std::max(idata[i-1], i2);
349             }
350             else
351             {
352                 idata[i++] = i1;
353                 idata[i++] = i2;
354             }
355         }
356         else
357         {
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])
362             {
363                 rdata[i-2] = std::min(rdata[i-2], r1);
364                 rdata[i-1] = std::max(rdata[i-1], r2);
365             }
366             else
367             {
368                 rdata[i++] = r1;
369                 rdata[i++] = r2;
370             }
371         }
372     }
373     n = i/2;
374     /* Sort the ranges and merge consequent ones */
375     if (param->val.type == INT_VALUE)
376     {
377         qsort(idata, n, 2*sizeof(int), &cmp_int_range);
378         for (i = j = 2; i < 2*n; i += 2)
379         {
380             if (idata[j-1]+1 >= idata[i])
381             {
382                 if (idata[i+1] > idata[j-1])
383                 {
384                     idata[j-1] = idata[i+1];
385                 }
386             }
387             else
388             {
389                 idata[j]   = idata[i];
390                 idata[j+1] = idata[i+1];
391                 j         += 2;
392             }
393         }
394     }
395     else
396     {
397         qsort(rdata, n, 2*sizeof(real), &cmp_real_range);
398         for (i = j = 2; i < 2*n; i += 2)
399         {
400             if (rdata[j-1] >= rdata[i])
401             {
402                 if (rdata[i+1] > rdata[j-1])
403                 {
404                     rdata[j-1] = rdata[i+1];
405                 }
406             }
407             else
408             {
409                 rdata[j]   = rdata[i];
410                 rdata[j+1] = rdata[i+1];
411                 j         += 2;
412             }
413         }
414     }
415     n = j/2;
416     /* Store the values */
417     if (param->flags & SPAR_VARNUM)
418     {
419         dataGuard.release();
420         param->val.nr  = n;
421         if (param->val.type == INT_VALUE)
422         {
423             srenew(idata, j);
424             _gmx_selvalue_setstore_alloc(&param->val, idata, j);
425         }
426         else
427         {
428             srenew(rdata, j);
429             _gmx_selvalue_setstore_alloc(&param->val, rdata, j);
430         }
431     }
432     else
433     {
434         if (n != param->val.nr)
435         {
436             GMX_ASSERT(n == 1,
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"));
440         }
441         if (param->val.type == INT_VALUE)
442         {
443             memcpy(param->val.u.i, idata, 2*n*sizeof(int));
444         }
445         else
446         {
447             memcpy(param->val.u.r, rdata, 2*n*sizeof(real));
448         }
449     }
450     if (param->nvalptr)
451     {
452         *param->nvalptr = param->val.nr;
453     }
454     param->nvalptr = nullptr;
455 }
456
457 /*! \brief
458  * Parses the values for a parameter that takes a variable number of values.
459  *
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.
464  *
465  * For integer ranges, the sequence of numbers from the first to second value
466  * is stored, each as a separate value.
467  */
468 static void
469 parse_values_varnum(const SelectionParserValueList    &values,
470                     gmx_ana_selparam_t                *param,
471                     const SelectionTreeElementPointer &root,
472                     void                              *scanner)
473 {
474     int                 i, j;
475
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)
480     {
481         SelectionParserValueList::const_iterator value;
482         for (value = values.begin(); value != values.end(); ++value)
483         {
484             if (value->type == INT_VALUE && !value->hasExpressionValue())
485             {
486                 valueCount += abs(value->u.i.i2 - value->u.i.i1);
487             }
488         }
489     }
490
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)
494     {
495         GMX_THROW(InternalError("Variable-count value type not implemented"));
496     }
497
498     /* Reserve appropriate amount of memory */
499     if (param->val.type == POS_VALUE)
500     {
501         gmx_ana_pos_reserve(param->val.u.p, valueCount, 0);
502         gmx_ana_indexmap_init(&param->val.u.p->m, nullptr, nullptr, INDEX_UNKNOWN);
503         gmx_ana_pos_set_nr(param->val.u.p, valueCount);
504     }
505     else
506     {
507         _gmx_selvalue_reserve(&param->val, valueCount);
508     }
509     /* Create a dummy child element to store the string values.
510      * This element is responsible for freeing the values, but carries no
511      * other function. */
512     if (param->val.type == STR_VALUE)
513     {
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);
527     }
528     param->val.nr = valueCount;
529
530     i     = 0;
531     SelectionParserValueList::const_iterator value;
532     for (value = values.begin(); value != values.end(); ++value)
533     {
534         GMX_RELEASE_ASSERT(value->type == param->val.type,
535                            "Invalid value type (should have been caught earlier)");
536         if (value->hasExpressionValue())
537         {
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()));
543             GMX_THROW(ex);
544         }
545         switch (param->val.type)
546         {
547             case INT_VALUE:
548                 if (value->u.i.i1 <= value->u.i.i2)
549                 {
550                     for (j = value->u.i.i1; j <= value->u.i.i2; ++j)
551                     {
552                         param->val.u.i[i++] = j;
553                     }
554                 }
555                 else
556                 {
557                     for (j = value->u.i.i1; j >= value->u.i.i2; --j)
558                     {
559                         param->val.u.i[i++] = j;
560                     }
561                 }
562                 break;
563             case REAL_VALUE:
564                 if (value->u.r.r1 != value->u.r.r2)
565                 {
566                     std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
567                     std::string message
568                         = formatString("Real range ('%s') is not supported in this context",
569                                        text.c_str());
570                     InvalidInputError ex(message);
571                     GMX_THROW(ex);
572                 }
573                 param->val.u.r[i++] = value->u.r.r1;
574                 break;
575             case STR_VALUE:
576                 param->val.u.s[i++] = gmx_strdup(value->stringValue().c_str());
577                 break;
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");
581         }
582     }
583     GMX_RELEASE_ASSERT(i == valueCount,
584                        "Inconsistent value count wrt. the actual value population");
585     if (param->nvalptr)
586     {
587         *param->nvalptr = param->val.nr;
588     }
589     param->nvalptr = nullptr;
590 }
591
592 /*! \brief
593  * Adds a new subexpression reference to a selection element.
594  *
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.
600  *
601  * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child
602  * list of \p root.
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.
605  */
606 static SelectionTreeElementPointer
607 add_child(const SelectionTreeElementPointer &root, gmx_ana_selparam_t *param,
608           const SelectionTreeElementPointer &expr, void *scanner)
609 {
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)
615     {
616         child = expr;
617     }
618     else
619     {
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);
623         child->child  = expr;
624     }
625     /* Setup the child element */
626     child->flags  &= ~SEL_ALLOCVAL;
627     child->u.param = param;
628     if (child->v.type != param->val.type)
629     {
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()));
632         std::string message
633             = formatString("Expression '%s' is not valid in this context "
634                            "(produces the wrong type of values)",
635                            text.c_str());
636         GMX_THROW(InvalidInputError(message));
637     }
638     _gmx_selelem_update_flags(child);
639     if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC))
640     {
641         std::string text(_gmx_sel_lexer_get_text(scanner, expr->location()));
642         std::string message
643             = formatString("Expression '%s' is dynamic, which is not "
644                            "valid in this context",
645                            text.c_str());
646         GMX_THROW(InvalidInputError(message));
647     }
648     if (!(child->flags & SEL_DYNAMIC))
649     {
650         param->flags &= ~SPAR_DYNAMIC;
651     }
652     /* Put the child element in the correct place */
653     place_child(root, child, param);
654     return child;
655 }
656
657 /*! \brief
658  * Parses an expression value for a parameter that takes a variable number of values.
659  *
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.
664  */
665 static void
666 parse_values_varnum_expr(const SelectionParserValueList    &values,
667                          gmx_ana_selparam_t                *param,
668                          const SelectionTreeElementPointer &root,
669                          void                              *scanner)
670 {
671     GMX_RELEASE_ASSERT(values.size() == 1 && values.front().hasExpressionValue(),
672                        "Called with an invalid type of value");
673
674     SelectionTreeElementPointer child
675         = add_child(root, param, values.front().expr, scanner);
676
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)
680     {
681         /* Set the value storage */
682         _gmx_selvalue_setstore(&child->v, param->val.u.ptr);
683         param->val.nr = 1;
684         if (param->nvalptr)
685         {
686             *param->nvalptr = param->val.nr;
687         }
688         param->nvalptr = nullptr;
689         return;
690     }
691
692     if (!(child->flags & SEL_VARNUMVAL))
693     {
694         std::string text(_gmx_sel_lexer_get_text(scanner, values.front().location()));
695         std::string message
696             = formatString("Expression '%s' is invalid in this context",
697                            text.c_str());
698         GMX_THROW(InvalidInputError(message));
699     }
700
701     child->flags   |= SEL_ALLOCVAL;
702     param->val.nr   = -1;
703     *param->nvalptr = param->val.nr;
704     /* Rest of the initialization is done during compilation in
705      * init_method(). */
706 }
707
708 /*! \brief
709  * Initializes the storage of an expression value.
710  *
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
714  *   \p param.
715  * \param[in]     scanner Scanner data structure.
716  *
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().
720  */
721 static void
722 set_expr_value_store(const SelectionTreeElementPointer &sel,
723                      gmx_ana_selparam_t *param, int i, void *scanner)
724 {
725     if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL))
726     {
727         std::string text(_gmx_sel_lexer_get_text(scanner, sel->location()));
728         std::string message
729             = formatString("Expression '%s' is invalid in this context",
730                            text.c_str());
731         GMX_THROW(InvalidInputError(message));
732     }
733     switch (sel->v.type)
734     {
735         case INT_VALUE:   sel->v.u.i = &param->val.u.i[i]; break;
736         case REAL_VALUE:  sel->v.u.r = &param->val.u.r[i]; break;
737         case STR_VALUE:   sel->v.u.s = &param->val.u.s[i]; break;
738         case POS_VALUE:   sel->v.u.p = &param->val.u.p[i]; break;
739         case GROUP_VALUE: sel->v.u.g = &param->val.u.g[i]; break;
740         default: /* Error */
741             GMX_THROW(InternalError("Invalid value type"));
742     }
743     sel->v.nr     = 1;
744     sel->v.nalloc = -1;
745 }
746
747 /*! \brief
748  * Parses the values for a parameter that takes a constant number of values.
749  *
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.
754  *
755  * For integer ranges, the sequence of numbers from the first to second value
756  * is stored, each as a separate value.
757  */
758 static void
759 parse_values_std(const SelectionParserValueList &values,
760                  gmx_ana_selparam_t *param,
761                  const SelectionTreeElementPointer &root, void *scanner)
762 {
763     int                i, j;
764     bool               bDynamic;
765
766     /* Handle atom-valued parameters */
767     if (param->flags & SPAR_ATOMVAL)
768     {
769         if (values.size() > 1)
770         {
771             GMX_THROW(InvalidInputError(
772                               "Only a single value or a single expression is "
773                               "supported in this context"));
774         }
775         if (values.front().hasExpressionValue())
776         {
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))
781             {
782                 /* Rest of the initialization is done during compilation in
783                  * init_method(). */
784                 /* TODO: Positions are not correctly handled */
785                 param->val.nr = -1;
786                 if (param->nvalptr)
787                 {
788                     *param->nvalptr = -1;
789                 }
790                 return;
791             }
792             param->flags  &= ~SPAR_ATOMVAL;
793             param->val.nr  = 1;
794             if (param->nvalptr)
795             {
796                 *param->nvalptr = 1;
797             }
798             param->nvalptr = nullptr;
799             if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
800                 || param->val.type == STR_VALUE)
801             {
802                 _gmx_selvalue_reserve(&param->val, 1);
803             }
804             set_expr_value_store(child, param, 0, scanner);
805             return;
806         }
807         /* If we reach here, proceed with normal parameter handling */
808         param->val.nr = 1;
809         if (param->val.type == INT_VALUE || param->val.type == REAL_VALUE
810             || param->val.type == STR_VALUE)
811         {
812             _gmx_selvalue_reserve(&param->val, 1);
813         }
814         param->flags &= ~SPAR_ATOMVAL;
815         param->flags &= ~SPAR_DYNAMIC;
816     }
817
818     i        = 0;
819     bDynamic = false;
820     SelectionParserValueList::const_iterator value;
821     for (value = values.begin(); value != values.end() && i < param->val.nr; ++value)
822     {
823         GMX_RELEASE_ASSERT(value->type == param->val.type,
824                            "Invalid value type (should have been caught earlier)");
825         if (value->hasExpressionValue())
826         {
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)
831             {
832                 bDynamic = true;
833             }
834         }
835         else
836         {
837             /* Value is not an expression */
838             switch (value->type)
839             {
840                 case INT_VALUE:
841                 {
842                     bool bTooManyValues;
843                     if (value->u.i.i1 <= value->u.i.i2)
844                     {
845                         for (j = value->u.i.i1; j <= value->u.i.i2 && i < param->val.nr; ++j)
846                         {
847                             param->val.u.i[i++] = j;
848                         }
849                         bTooManyValues = (j != value->u.i.i2 + 1);
850                     }
851                     else
852                     {
853                         for (j = value->u.i.i1; j >= value->u.i.i2 && i < param->val.nr; --j)
854                         {
855                             param->val.u.i[i++] = j;
856                         }
857                         bTooManyValues = (j != value->u.i.i2 - 1);
858                     }
859                     if (bTooManyValues)
860                     {
861                         std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
862                         std::string message
863                             = formatString("Range ('%s') produces more values than is "
864                                            "accepted in this context",
865                                            text.c_str());
866                         GMX_THROW(InvalidInputError(message));
867                     }
868                     --i;
869                     break;
870                 }
871                 case REAL_VALUE:
872                     if (value->u.r.r1 != value->u.r.r2)
873                     {
874                         std::string text(_gmx_sel_lexer_get_text(scanner, value->location()));
875                         std::string message
876                             = formatString("Real range ('%s') is not supported in this context",
877                                            text.c_str());
878                         GMX_THROW(InvalidInputError(message));
879                     }
880                     param->val.u.r[i] = value->u.r.r1;
881                     break;
882                 case STR_VALUE:
883                     param->val.u.s[i] = gmx_strdup(value->stringValue().c_str());
884                     break;
885                 case POS_VALUE:
886                     gmx_ana_pos_init_const(&param->val.u.p[i], value->u.x);
887                     break;
888                 case NO_VALUE:
889                 case GROUP_VALUE:
890                     GMX_THROW(InternalError("Invalid non-expression value type"));
891             }
892         }
893         ++i;
894     }
895     if (value != values.end())
896     {
897         std::string message
898             = formatString("Too many values provided, expected %d",
899                            param->val.nr);
900         GMX_THROW(InvalidInputError(message));
901     }
902     if (i < param->val.nr)
903     {
904         std::string message
905             = formatString("Too few values provided, expected %d",
906                            param->val.nr);
907         GMX_THROW(InvalidInputError(message));
908     }
909     if (!bDynamic)
910     {
911         param->flags &= ~SPAR_DYNAMIC;
912     }
913     if (param->nvalptr)
914     {
915         *param->nvalptr = param->val.nr;
916     }
917     param->nvalptr = nullptr;
918 }
919
920 /*! \brief
921  * Parses the values for a boolean parameter.
922  *
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.
927  */
928 static void
929 parse_values_bool(const std::string &name,
930                   const SelectionParserValueList &values,
931                   gmx_ana_selparam_t *param, void *scanner)
932 {
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))
937     {
938         std::string message
939             = formatString("'%s' only accepts yes/no/on/off/0/1 (and empty) as a value",
940                            param->name);
941         GMX_THROW(InvalidInputError(message));
942     }
943
944     bool bSetNo = false;
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)
948     {
949         bSetNo = true;
950     }
951     if (bSetNo && !values.empty())
952     {
953         std::string message
954             = formatString("'no%s' cannot be followed by any value",
955                            param->name);
956         GMX_THROW(InvalidInputError(message));
957     }
958     if (!values.empty() && values.front().u.i.i1 == 0)
959     {
960         bSetNo = true;
961     }
962
963     *param->val.u.b = bSetNo ? false : true;
964 }
965
966 /*! \brief
967  * Parses the values for an enumeration parameter.
968  *
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.
973  */
974 static void
975 parse_values_enum(const SelectionParserValueList &values,
976                   gmx_ana_selparam_t             *param,
977                   void                           *scanner)
978 {
979     GMX_ASSERT(param->val.type == STR_VALUE,
980                "Enum parser called for non-string parameter");
981     if (values.size() != 1)
982     {
983         GMX_THROW(InvalidInputError(
984                           "Only a single string value is supported in this context"));
985     }
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())
990     {
991         std::string text(_gmx_sel_lexer_get_text(scanner, value.location()));
992         std::string message
993             = formatString("Expression ('%s') is not supported in this context",
994                            text.c_str());
995         GMX_THROW(InvalidInputError(message));
996     }
997
998     const std::string &svalue = value.stringValue();
999     int                i      = 1;
1000     int                match  = 0;
1001     while (param->val.u.s[i] != nullptr)
1002     {
1003         if (startsWith(param->val.u.s[i], svalue))
1004         {
1005             /* Check if there is a duplicate match */
1006             if (match > 0)
1007             {
1008                 std::string message
1009                     = formatString("Value '%s' is ambiguous", svalue.c_str());
1010                 GMX_THROW(InvalidInputError(message));
1011             }
1012             match = i;
1013         }
1014         ++i;
1015     }
1016     if (match == 0)
1017     {
1018         std::string message
1019             = formatString("Value '%s' is not recognized", svalue.c_str());
1020         GMX_THROW(InvalidInputError(message));
1021     }
1022     param->val.u.s[0] = param->val.u.s[match];
1023 }
1024
1025 /*! \brief
1026  * Replaces constant expressions with their values.
1027  *
1028  * \param[in,out] values First element in the value list to process.
1029  */
1030 static void
1031 convert_const_values(SelectionParserValueList *values)
1032 {
1033     SelectionParserValueList::iterator value;
1034     for (value = values->begin(); value != values->end(); ++value)
1035     {
1036         if (value->hasExpressionValue() && value->expr->v.type != GROUP_VALUE &&
1037             value->expr->type == SEL_CONST)
1038         {
1039             SelectionTreeElementPointer expr     = value->expr;
1040             const SelectionLocation    &location = value->location();
1041             switch (expr->v.type)
1042             {
1043                 case INT_VALUE:
1044                     *value = SelectionParserValue::createInteger(expr->v.u.i[0], location);
1045                     break;
1046                 case REAL_VALUE:
1047                     *value = SelectionParserValue::createReal(expr->v.u.r[0], location);
1048                     break;
1049                 case STR_VALUE:
1050                     *value = SelectionParserValue::createString(expr->v.u.s[0], location);
1051                     break;
1052                 case POS_VALUE:
1053                     *value = SelectionParserValue::createPosition(expr->v.u.p->x[0], location);
1054                     break;
1055                 default:
1056                     GMX_RELEASE_ASSERT(false,
1057                                        "Unsupported constant expression value type");
1058             }
1059         }
1060     }
1061 }
1062
1063 /*!
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.
1069  *
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.
1073  *
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.
1076  */
1077 void
1078 _gmx_sel_parse_params(const gmx::SelectionParserParameterList &pparams,
1079                       int nparam, gmx_ana_selparam_t *params,
1080                       const gmx::SelectionTreeElementPointer &root,
1081                       void *scanner)
1082 {
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)
1087     {
1088         if (params[i].val.type != POS_VALUE
1089             && (params[i].flags & (SPAR_VARNUM | SPAR_ATOMVAL)))
1090         {
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");
1100         }
1101         else
1102         {
1103             GMX_RELEASE_ASSERT(params[i].val.u.ptr != nullptr,
1104                                "value pointer is NULL");
1105         }
1106     }
1107     /* Parse the parameters */
1108     int nullParamIndex = 0;
1109     SelectionParserParameterList::const_iterator pparam;
1110     for (pparam = pparams.begin(); pparam != pparams.end(); ++pparam)
1111     {
1112         try
1113         {
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())
1118             {
1119                 nullParamIndex = -1;
1120                 oparam
1121                     = gmx_ana_selparam_find(pparam->name().c_str(), nparam, params);
1122                 GMX_RELEASE_ASSERT(oparam != nullptr, "Inconsistent selection parameter");
1123             }
1124             else if (nullParamIndex >= 0)
1125             {
1126                 oparam = &params[nullParamIndex];
1127                 if (oparam->name != nullptr)
1128                 {
1129                     std::string text(_gmx_sel_lexer_get_text(scanner, pparam->location()));
1130                     std::string message
1131                         = formatString("Unexpected '%s'", text.c_str());
1132                     GMX_THROW(InvalidInputError(message));
1133                 }
1134                 ++nullParamIndex;
1135             }
1136             else
1137             {
1138                 GMX_RELEASE_ASSERT(false, "All NULL parameters should appear in "
1139                                    "the beginning of the list");
1140             }
1141             if (oparam->flags & SPAR_SET)
1142             {
1143                 std::string message
1144                     = formatString("'%s' appears multiple times",
1145                                    pparam->name().c_str());
1146                 GMX_THROW(InvalidInputError(message));
1147             }
1148             oparam->flags |= SPAR_SET;
1149             if (oparam->val.type != NO_VALUE && pparam->values().empty())
1150             {
1151                 std::string text;
1152                 if (pparam->name().empty())
1153                 {
1154                     text = root->name();
1155                 }
1156                 else
1157                 {
1158                     text = _gmx_sel_lexer_get_text(scanner, pparam->location());
1159                 }
1160                 std::string message
1161                     = formatString("'%s' should be followed by a value/expression",
1162                                    text.c_str());
1163                 GMX_THROW(InvalidInputError(message));
1164             }
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)
1169             {
1170                 parse_values_bool(pparam->name(), pparam->values(), oparam, scanner);
1171             }
1172             else if (oparam->flags & SPAR_RANGES)
1173             {
1174                 parse_values_range(pparam->values(), oparam, scanner);
1175             }
1176             else if (oparam->flags & SPAR_VARNUM)
1177             {
1178                 if (pparam->values().size() == 1
1179                     && pparam->values().front().hasExpressionValue())
1180                 {
1181                     parse_values_varnum_expr(pparam->values(), oparam, root, scanner);
1182                 }
1183                 else
1184                 {
1185                     parse_values_varnum(pparam->values(), oparam, root, scanner);
1186                 }
1187             }
1188             else if (oparam->flags & SPAR_ENUMVAL)
1189             {
1190                 parse_values_enum(pparam->values(), oparam, scanner);
1191             }
1192             else
1193             {
1194                 parse_values_std(pparam->values(), oparam, root, scanner);
1195             }
1196         }
1197         catch (UserInputError &ex)
1198         {
1199             if (!pparam->name().empty())
1200             {
1201                 std::string text(_gmx_sel_lexer_get_text(scanner, pparam->location()));
1202                 ex.prependContext(formatString("In '%s'", text.c_str()));
1203             }
1204             errors.addCurrentExceptionAsNested();
1205         }
1206     }
1207     /* Check that all required parameters are present */
1208     for (int i = 0; i < nparam; ++i)
1209     {
1210         if (!(params[i].flags & SPAR_OPTIONAL) && !(params[i].flags & SPAR_SET))
1211         {
1212             std::string message;
1213             if (params[i].name == nullptr)
1214             {
1215                 message = formatString("'%s' should be followed by a value/expression",
1216                                        root->name().c_str());
1217             }
1218             else
1219             {
1220                 message = formatString("'%s' is missing", params[i].name);
1221             }
1222             InvalidInputError ex(message);
1223             errors.addNested(ex);
1224         }
1225     }
1226     if (errors.hasNestedExceptions())
1227     {
1228         GMX_THROW(InvalidInputError(errors));
1229     }
1230 }