Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / selection / sm_keywords.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,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.
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 internal selection methods for numeric and string keyword
38  * evaluation.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_selection
42  */
43 #include "gmxpre.h"
44
45 #include <cctype>
46 #include <cstring>
47
48 #include <regex>
49 #include <string>
50
51 #include "gromacs/utility/arraysize.h"
52 #include "gromacs/utility/cstringutil.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/gmxassert.h"
55 #include "gromacs/utility/smalloc.h"
56 #include "gromacs/utility/stringutil.h"
57
58 #include "keywords.h"
59 #include "parsetree.h"
60 #include "position.h"
61 #include "scanner.h"
62 #include "selelem.h"
63 #include "selmethod.h"
64
65 /*! \brief
66  * Allocates data for integer keyword evaluation.
67  *
68  * \param[in] npar  Not used.
69  * \param     param Not used.
70  * \returns   Pointer to the allocated data (\ref t_methoddata_kwint).
71  *
72  * Allocates memory for a \ref t_methoddata_kwint structure.
73  */
74 static void* init_data_kwint(int npar, gmx_ana_selparam_t* param);
75 /*! \brief
76  * Allocates data for real keyword evaluation.
77  *
78  * \param[in] npar  Not used.
79  * \param     param Not used.
80  * \returns   Pointer to the allocated data (\ref t_methoddata_kwreal).
81  *
82  * Allocates memory for a \ref t_methoddata_kwreal structure.
83  */
84 static void* init_data_kwreal(int npar, gmx_ana_selparam_t* param);
85 /*! \brief
86  * Allocates data for string keyword evaluation.
87  *
88  * \param[in] npar  Not used.
89  * \param     param Not used.
90  * \returns Pointer to the allocated data (t_methoddata_kwstr).
91  *
92  * Allocates memory for a t_methoddata_kwstr structure.
93  */
94 static void* init_data_kwstr(int npar, gmx_ana_selparam_t* param);
95 /** /brief Initializes data for integer keyword evaluation.
96  *
97  * \param[in] top   Not used.
98  * \param[in] npar  Not used (should be 2).
99  * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
100  * \param[in] data  Should point to \ref t_methoddata_kwint.
101  */
102 static void init_kwint(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
103 /*! \brief
104  * Initializes data for real keyword evaluation.
105  *
106  * \param[in] top   Not used.
107  * \param[in] npar  Not used (should be 2).
108  * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
109  * \param[in] data  Should point to \ref t_methoddata_kwreal.
110  * \returns   0 (the initialization always succeeds).
111  */
112 static void init_kwreal(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
113 /*! \brief
114  * Initializes data for string keyword evaluation.
115  *
116  * \param[in] top   Not used.
117  * \param[in] npar  Not used (should be 2).
118  * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
119  * \param[in] data  Should point to t_methoddata_kwstr.
120  */
121 static void init_kwstr(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
122 /** Frees the memory allocated for string keyword evaluation. */
123 static void free_data_kwstr(void* data);
124 /** Evaluates integer selection keywords. */
125 static void evaluate_keyword_int(const gmx::SelMethodEvalContext& context,
126                                  gmx_ana_index_t*                 g,
127                                  gmx_ana_selvalue_t*              out,
128                                  void*                            data);
129 /** Evaluates real selection keywords. */
130 static void evaluate_keyword_real(const gmx::SelMethodEvalContext& context,
131                                   gmx_ana_index_t*                 g,
132                                   gmx_ana_selvalue_t*              out,
133                                   void*                            data);
134 /** Evaluates string selection keywords. */
135 static void evaluate_keyword_str(const gmx::SelMethodEvalContext& context,
136                                  gmx_ana_index_t*                 g,
137                                  gmx_ana_selvalue_t*              out,
138                                  void*                            data);
139
140 /*! \internal \brief
141  * Data structure for integer keyword expression evaluation.
142  */
143 typedef struct t_methoddata_kwint
144 {
145     /** Array of values for the keyword. */
146     int* v;
147     /** Number of ranges in the \p r array. */
148     int n;
149     /*! \brief
150      * Array of sorted integer ranges to match against.
151      *
152      * Each range is made of two integers, giving the endpoints (inclusive).
153      * This field stores the pointer to the ranges allocated by the
154      * parameter parser; see \ref SPAR_RANGES for more information.
155      */
156     int* r;
157 } t_methoddata_kwint;
158
159 /*! \internal \brief
160  * Data structure for real keyword expression evaluation.
161  */
162 typedef struct t_methoddata_kwreal
163 {
164     /** Array of values for the keyword. */
165     real* v;
166     /** Number of ranges in the \p r array. */
167     int n;
168     /*! \brief
169      * Array of sorted ranges to match against.
170      *
171      * Each range is made of two values, giving the endpoints (inclusive).
172      * This field stores the pointer to the ranges allocated by the
173      * parameter parser; see \ref SPAR_RANGES for more information.
174      */
175     real* r;
176 } t_methoddata_kwreal;
177
178 namespace
179 {
180
181 /*! \internal \brief
182  * Single item in the list of strings/regular expressions to match.
183  *
184  * \ingroup module_selection
185  */
186 class StringKeywordMatchItem
187 {
188 public:
189     /*! \brief
190      * Constructs a matcher from a string.
191      *
192      * \param[in] matchType String matching type.
193      * \param[in] str       String to use for matching.
194      */
195     StringKeywordMatchItem(gmx::SelectionStringMatchType matchType, const char* str) : str_(str)
196     {
197         useRegExp_ = (matchType == gmx::eStringMatchType_RegularExpression);
198         if (matchType == gmx::eStringMatchType_Auto)
199         {
200             for (size_t j = 0; j < std::strlen(str); ++j)
201             {
202                 if (std::ispunct(str[j]) && str[j] != '?' && str[j] != '*')
203                 {
204                     useRegExp_ = true;
205                     break;
206                 }
207             }
208         }
209         if (useRegExp_)
210         {
211             try
212             {
213                 regex_.assign(str, std::regex::nosubs | std::regex::extended);
214             }
215             catch (const std::regex_error& /*ex*/)
216             {
217                 // TODO: Better error messages.
218                 GMX_THROW(gmx::InvalidInputError(
219                         gmx::formatString("Error in regular expression \"%s\"", str)));
220             }
221         }
222     }
223
224     /*! \brief
225      * Checks whether this item matches a string.
226      *
227      * \param[in] matchType String matching type.
228      * \param[in] value     String to match.
229      * \returns   true if this item matches \p value.
230      */
231     bool match(gmx::SelectionStringMatchType matchType, const char* value) const
232     {
233         if (matchType == gmx::eStringMatchType_Exact)
234         {
235             return str_ == value;
236         }
237         else if (useRegExp_)
238         {
239             return std::regex_match(value, regex_);
240         }
241         else
242         {
243             return gmx_wcmatch(str_.c_str(), value) == 0;
244         }
245     }
246
247 private:
248     //! The raw string passed for the matcher.
249     std::string str_;
250     //! Whether a regular expression match is used.
251     bool useRegExp_;
252     //! Regular expression compiled from \p str_, if applicable.
253     std::regex regex_;
254 };
255
256 /*! \internal \brief
257  * Data structure for string keyword expression evaluation.
258  */
259 struct t_methoddata_kwstr
260 {
261     /** Matching type for the strings. */
262     gmx::SelectionStringMatchType matchType;
263     /** Array of values for the keyword. */
264     char** v;
265     /** Array of strings/regular expressions to match against.*/
266     std::vector<StringKeywordMatchItem> matches;
267 };
268
269 } // namespace
270
271 /** Parameters for integer keyword evaluation. */
272 static gmx_ana_selparam_t smparams_keyword_int[] = {
273     { nullptr, { INT_VALUE, -1, { nullptr } }, nullptr, SPAR_ATOMVAL },
274     { nullptr, { INT_VALUE, -1, { nullptr } }, nullptr, SPAR_RANGES | SPAR_VARNUM },
275 };
276
277 /** Parameters for real keyword evaluation. */
278 static gmx_ana_selparam_t smparams_keyword_real[] = {
279     { nullptr, { REAL_VALUE, -1, { nullptr } }, nullptr, SPAR_ATOMVAL | SPAR_DYNAMIC },
280     { nullptr, { REAL_VALUE, -1, { nullptr } }, nullptr, SPAR_RANGES | SPAR_VARNUM },
281 };
282
283 /** Parameters for string keyword evaluation. */
284 static gmx_ana_selparam_t smparams_keyword_str[] = {
285     { nullptr, { STR_VALUE, -1, { nullptr } }, nullptr, SPAR_ATOMVAL },
286     { nullptr, { STR_VALUE, -1, { nullptr } }, nullptr, SPAR_VARNUM },
287 };
288
289 /** Selection method data for integer keyword evaluation. */
290 gmx_ana_selmethod_t sm_keyword_int = {
291     "kw_int",
292     GROUP_VALUE,
293     SMETH_SINGLEVAL,
294     asize(smparams_keyword_int),
295     smparams_keyword_int,
296     &init_data_kwint,
297     nullptr,
298     &init_kwint,
299     nullptr,
300     nullptr,
301     nullptr,
302     &evaluate_keyword_int,
303     nullptr,
304     { nullptr, nullptr, 0, nullptr },
305 };
306
307 /** Selection method data for real keyword evaluation. */
308 gmx_ana_selmethod_t sm_keyword_real = {
309     "kw_real",
310     GROUP_VALUE,
311     SMETH_SINGLEVAL,
312     asize(smparams_keyword_real),
313     smparams_keyword_real,
314     &init_data_kwreal,
315     nullptr,
316     &init_kwreal,
317     nullptr,
318     nullptr,
319     nullptr,
320     &evaluate_keyword_real,
321     nullptr,
322     { nullptr, nullptr, 0, nullptr },
323 };
324
325 /** Selection method data for string keyword evaluation. */
326 gmx_ana_selmethod_t sm_keyword_str = {
327     "kw_str",
328     GROUP_VALUE,
329     SMETH_SINGLEVAL,
330     asize(smparams_keyword_str),
331     smparams_keyword_str,
332     &init_data_kwstr,
333     nullptr,
334     &init_kwstr,
335     nullptr,
336     &free_data_kwstr,
337     nullptr,
338     &evaluate_keyword_str,
339     nullptr,
340     { nullptr, nullptr, 0, nullptr },
341 };
342
343 /*! \brief
344  * Initializes keyword evaluation for an arbitrary group.
345  *
346  * \param[in] top   Not used.
347  * \param[in] npar  Not used.
348  * \param[in] param Not used.
349  * \param[in] data  Should point to \ref t_methoddata_kweval.
350  * \returns   0 on success, a non-zero error code on return.
351  *
352  * Calls the initialization method of the wrapped keyword.
353  */
354 static void init_kweval(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
355 /*! \brief
356  * Initializes output for keyword evaluation in an arbitrary group.
357  *
358  * \param[in]     top   Not used.
359  * \param[in,out] out   Pointer to output data structure.
360  * \param[in,out] data  Should point to \c t_methoddata_kweval.
361  * \returns       0 for success.
362  */
363 static void init_output_kweval(const gmx_mtop_t* top, gmx_ana_selvalue_t* out, void* data);
364 /** Frees the data allocated for keyword evaluation in an arbitrary group. */
365 static void free_data_kweval(void* data);
366 /** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
367 static void init_frame_kweval(const gmx::SelMethodEvalContext& context, void* data);
368 /*! \brief
369  * Evaluates keywords in an arbitrary group.
370  *
371  * See sel_updatefunc() for description of the parameters.
372  * \p data should point to a \c t_methoddata_kweval.
373  *
374  * Calls the evaluation function of the wrapped keyword with the given
375  * parameters, with the exception of using \c t_methoddata_kweval::g for the
376  * evaluation group.
377  */
378 static void evaluate_kweval(const gmx::SelMethodEvalContext& context,
379                             gmx_ana_index_t*                 g,
380                             gmx_ana_selvalue_t*              out,
381                             void*                            data);
382 /*! \brief
383  * Evaluates keywords in an arbitrary set of positions.
384  *
385  * See sel_updatefunc() for description of the parameters.
386  * \p data should point to a \c t_methoddata_kweval.
387  *
388  * Calls the evaluation function of the wrapped keyword with the given
389  * parameters, with the exception of using \c t_methoddata_kweval::p for the
390  * evaluation positions.
391  */
392 static void evaluate_kweval_pos(const gmx::SelMethodEvalContext& context,
393                                 gmx_ana_index_t*                 g,
394                                 gmx_ana_selvalue_t*              out,
395                                 void*                            data);
396
397 /*! \internal \brief
398  * Data structure for keyword evaluation in arbitrary groups.
399  */
400 struct t_methoddata_kweval
401 {
402     //! Initialize new keyword evaluator for the given keyword.
403     t_methoddata_kweval(gmx_ana_selmethod_t* method, void* data) : kwmethod(method), kwmdata(data)
404     {
405         gmx_ana_index_clear(&g);
406     }
407     ~t_methoddata_kweval() { _gmx_selelem_free_method(kwmethod, kwmdata); }
408
409     //! Wrapped keyword method for evaluating the values.
410     gmx_ana_selmethod_t* kwmethod;
411     //! Method data for \p kwmethod.
412     void* kwmdata;
413     //! Group in which \p kwmethod should be evaluated.
414     gmx_ana_index_t g;
415     //! Positions for which \p kwmethod should be evaluated.
416     gmx_ana_pos_t p;
417 };
418
419 /** Parameters for keyword evaluation in an arbitrary group. */
420 static gmx_ana_selparam_t smparams_kweval_group[] = {
421     { nullptr, { GROUP_VALUE, 1, { nullptr } }, nullptr, SPAR_DYNAMIC },
422 };
423 /** Parameters for keyword evaluation from positions. */
424 static gmx_ana_selparam_t smparams_kweval_pos[] = {
425     { nullptr, { POS_VALUE, 1, { nullptr } }, nullptr, SPAR_DYNAMIC },
426 };
427
428
429 /********************************************************************
430  * INTEGER KEYWORD EVALUATION
431  ********************************************************************/
432
433 static void* init_data_kwint(int /* npar */, gmx_ana_selparam_t* /* param */)
434 {
435     t_methoddata_kwint* data;
436
437     snew(data, 1);
438     return data;
439 }
440
441 static void init_kwint(const gmx_mtop_t* /* top */, int /* npar */, gmx_ana_selparam_t* param, void* data)
442 {
443     t_methoddata_kwint* d = static_cast<t_methoddata_kwint*>(data);
444
445     d->v = param[0].val.u.i;
446     d->n = param[1].val.nr;
447     d->r = param[1].val.u.i;
448 }
449
450 /*!
451  * See sel_updatefunc() for description of the parameters.
452  * \p data should point to a \c t_methoddata_kwint.
453  *
454  * Does a binary search to find which atoms match the ranges in the
455  * \c t_methoddata_kwint structure for this selection.
456  * Matching atoms are stored in \p out->u.g.
457  */
458 static void evaluate_keyword_int(const gmx::SelMethodEvalContext& /*context*/,
459                                  gmx_ana_index_t*    g,
460                                  gmx_ana_selvalue_t* out,
461                                  void*               data)
462 {
463     t_methoddata_kwint* d = static_cast<t_methoddata_kwint*>(data);
464     int                 n, i, j, jmin, jmax;
465     int                 val;
466
467     out->u.g->isize = 0;
468     n               = d->n;
469     for (i = 0; i < g->isize; ++i)
470     {
471         val = d->v[i];
472         if (d->r[0] > val || d->r[2 * n - 1] < val)
473         {
474             continue;
475         }
476         jmin = 0;
477         jmax = n;
478         while (jmax - jmin > 1)
479         {
480             j = jmin + (jmax - jmin) / 2;
481             if (val < d->r[2 * j])
482             {
483                 jmax = j;
484             }
485             else
486             {
487                 jmin = j;
488                 if (val <= d->r[2 * j + 1])
489                 {
490                     break;
491                 }
492                 /* ++jmin;*/
493             }
494         }
495         if (val <= d->r[2 * jmin + 1])
496         {
497             out->u.g->index[out->u.g->isize++] = g->index[i];
498         }
499     }
500 }
501
502
503 /********************************************************************
504  * REAL KEYWORD EVALUATION
505  ********************************************************************/
506
507 static void* init_data_kwreal(int /* npar */, gmx_ana_selparam_t* /* param */)
508 {
509     t_methoddata_kwreal* data;
510
511     snew(data, 1);
512     return data;
513 }
514
515 static void init_kwreal(const gmx_mtop_t* /* top */, int /* npar */, gmx_ana_selparam_t* param, void* data)
516 {
517     t_methoddata_kwreal* d = static_cast<t_methoddata_kwreal*>(data);
518
519     d->v = param[0].val.u.r;
520     d->n = param[1].val.nr;
521     d->r = param[1].val.u.r;
522 }
523
524 /*!
525  * See sel_updatefunc() for description of the parameters.
526  * \p data should point to a \c t_methoddata_kwreal.
527  *
528  * Does a binary search to find which atoms match the ranges in the
529  * \c t_methoddata_kwreal structure for this selection.
530  * Matching atoms are stored in \p out->u.g.
531  */
532 static void evaluate_keyword_real(const gmx::SelMethodEvalContext& /*context*/,
533                                   gmx_ana_index_t*    g,
534                                   gmx_ana_selvalue_t* out,
535                                   void*               data)
536 {
537     t_methoddata_kwreal* d = static_cast<t_methoddata_kwreal*>(data);
538     int                  n, i, j, jmin, jmax;
539     real                 val;
540
541     out->u.g->isize = 0;
542     n               = d->n;
543     for (i = 0; i < g->isize; ++i)
544     {
545         val = d->v[i];
546         if (d->r[0] > val || d->r[2 * n - 1] < val)
547         {
548             continue;
549         }
550         jmin = 0;
551         jmax = n;
552         while (jmax - jmin > 1)
553         {
554             j = jmin + (jmax - jmin) / 2;
555             if (val < d->r[2 * j])
556             {
557                 jmax = j;
558             }
559             else
560             {
561                 jmin = j;
562                 if (val <= d->r[2 * j + 1])
563                 {
564                     break;
565                 }
566                 /* ++jmin;*/
567             }
568         }
569         if (val <= d->r[2 * jmin + 1])
570         {
571             out->u.g->index[out->u.g->isize++] = g->index[i];
572         }
573     }
574 }
575
576
577 /********************************************************************
578  * STRING KEYWORD EVALUATION
579  ********************************************************************/
580
581 static void* init_data_kwstr(int /* npar */, gmx_ana_selparam_t* /* param */)
582 {
583     t_methoddata_kwstr* data = new t_methoddata_kwstr();
584     data->matchType          = gmx::eStringMatchType_Auto;
585     return data;
586 }
587
588 /*!
589  * \param[in,out] sel   Selection element to initialize.
590  * \param[in]     matchType  Method to use to match string values.
591  *
592  * Sets the string matching method for string keyword matching.
593  */
594 void _gmx_selelem_set_kwstr_match_type(const gmx::SelectionTreeElementPointer& sel,
595                                        gmx::SelectionStringMatchType           matchType)
596 {
597     t_methoddata_kwstr* d = static_cast<t_methoddata_kwstr*>(sel->u.expr.mdata);
598
599     if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
600         || sel->u.expr.method->name != sm_keyword_str.name)
601     {
602         return;
603     }
604     d->matchType = matchType;
605 }
606
607 static void init_kwstr(const gmx_mtop_t* /* top */, int /* npar */, gmx_ana_selparam_t* param, void* data)
608 {
609     t_methoddata_kwstr* d = static_cast<t_methoddata_kwstr*>(data);
610
611     d->v = param[0].val.u.s;
612     /* Return if this is not the first time */
613     if (!d->matches.empty())
614     {
615         return;
616     }
617     int n = param[1].val.nr;
618     d->matches.reserve(n);
619     for (int i = 0; i < n; ++i)
620     {
621         const char* s = param[1].val.u.s[i];
622         d->matches.emplace_back(d->matchType, s);
623     }
624 }
625
626 /*!
627  * \param data Data to free (should point to a t_methoddata_kwstr).
628  */
629 static void free_data_kwstr(void* data)
630 {
631     t_methoddata_kwstr* d = static_cast<t_methoddata_kwstr*>(data);
632     delete d;
633 }
634
635 /*!
636  * See sel_updatefunc() for description of the parameters.
637  * \p data should point to a \c t_methoddata_kwstr.
638  *
639  * Does a linear search to find which atoms match the strings in the
640  * \c t_methoddata_kwstr structure for this selection.
641  * Wildcards are allowed in the strings.
642  * Matching atoms are stored in \p out->u.g.
643  */
644 static void evaluate_keyword_str(const gmx::SelMethodEvalContext& /*context*/,
645                                  gmx_ana_index_t*    g,
646                                  gmx_ana_selvalue_t* out,
647                                  void*               data)
648 {
649     t_methoddata_kwstr* d = static_cast<t_methoddata_kwstr*>(data);
650
651     out->u.g->isize = 0;
652     for (int i = 0; i < g->isize; ++i)
653     {
654         for (size_t j = 0; j < d->matches.size(); ++j)
655         {
656             if (d->matches[j].match(d->matchType, d->v[i]))
657             {
658                 out->u.g->index[out->u.g->isize++] = g->index[i];
659                 break;
660             }
661         }
662     }
663 }
664
665
666 /********************************************************************
667  * KEYWORD EVALUATION FOR ARBITRARY GROUPS
668  ********************************************************************/
669
670 static void init_kweval(const gmx_mtop_t* top, int /* npar */, gmx_ana_selparam_t* /* param */, void* data)
671 {
672     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
673
674     d->kwmethod->init(top, 0, nullptr, d->kwmdata);
675 }
676
677 static void init_output_kweval(const gmx_mtop_t* /* top */, gmx_ana_selvalue_t* out, void* data)
678 {
679     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
680
681     out->nr = d->g.isize;
682     _gmx_selvalue_reserve(out, out->nr);
683 }
684
685 /*!
686  * \param data Data to free (should point to a \c t_methoddata_kweval).
687  *
688  * Frees the memory allocated for all the members of \c t_methoddata_kweval.
689  */
690 static void free_data_kweval(void* data)
691 {
692     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
693
694     delete d;
695 }
696
697 /*!
698  * \param[in]  context  Evaluation context.
699  * \param      data Should point to a \ref t_methoddata_kweval.
700  */
701 static void init_frame_kweval(const gmx::SelMethodEvalContext& context, void* data)
702 {
703     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
704
705     d->kwmethod->init_frame(context, d->kwmdata);
706 }
707
708 static void evaluate_kweval(const gmx::SelMethodEvalContext& context,
709                             gmx_ana_index_t* /* g */,
710                             gmx_ana_selvalue_t* out,
711                             void*               data)
712 {
713     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
714
715     d->kwmethod->update(context, &d->g, out, d->kwmdata);
716 }
717
718 static void evaluate_kweval_pos(const gmx::SelMethodEvalContext& context,
719                                 gmx_ana_index_t* /* g */,
720                                 gmx_ana_selvalue_t* out,
721                                 void*               data)
722 {
723     t_methoddata_kweval* d = static_cast<t_methoddata_kweval*>(data);
724
725     d->kwmethod->pupdate(context, &d->p, out, d->kwmdata);
726 }
727
728 /*! \brief
729  * Initializes a selection element for evaluating a keyword in a given group.
730  *
731  * \param[in]   method  Keyword selection method to evaluate.
732  * \param[in]   params  Parameters to pass to initialization (the child group).
733  * \param[in]   scanner Scanner data structure.
734  * \returns     Pointer to the created selection element.
735  *
736  * Implements _gmx_sel_init_keyword_evaluator() for \ref GROUP_VALUE input
737  * selections.
738  */
739 static gmx::SelectionTreeElementPointer init_evaluator_group(gmx_ana_selmethod_t* method,
740                                                              const gmx::SelectionParserParameterList& params,
741                                                              void* scanner)
742 {
743     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL)) || method->outinit || method->pupdate)
744     {
745         std::string message =
746                 gmx::formatString("Keyword '%s' cannot be evaluated in this context", method->name);
747         GMX_THROW(gmx::InvalidInputError(message));
748     }
749
750     // TODO: For same ... as ..., some other location could be more intuitive.
751     gmx::SelectionTreeElementPointer sel(new gmx::SelectionTreeElement(
752             SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
753     _gmx_selelem_set_method(sel, method, scanner);
754
755     t_methoddata_kweval* data = new t_methoddata_kweval(sel->u.expr.method, sel->u.expr.mdata);
756
757     snew(sel->u.expr.method, 1);
758     sel->u.expr.method->name        = data->kwmethod->name;
759     sel->u.expr.method->type        = data->kwmethod->type;
760     sel->u.expr.method->flags       = data->kwmethod->flags | SMETH_VARNUMVAL;
761     sel->u.expr.method->init_data   = nullptr;
762     sel->u.expr.method->set_poscoll = nullptr;
763     sel->u.expr.method->init        = method->init ? &init_kweval : nullptr;
764     sel->u.expr.method->outinit     = &init_output_kweval;
765     sel->u.expr.method->free        = &free_data_kweval;
766     sel->u.expr.method->init_frame  = method->init_frame ? &init_frame_kweval : nullptr;
767     sel->u.expr.method->update      = &evaluate_kweval;
768     sel->u.expr.method->pupdate     = nullptr;
769     sel->u.expr.method->nparams     = asize(smparams_kweval_group);
770     sel->u.expr.method->param       = smparams_kweval_group;
771     _gmx_selelem_init_method_params(sel, scanner);
772     sel->u.expr.mdata = data;
773
774     sel->u.expr.method->param[0].val.u.g = &data->g;
775
776     _gmx_sel_parse_params(params, sel->u.expr.method->nparams, sel->u.expr.method->param, sel, scanner);
777     return sel;
778 }
779
780 /*! \brief
781  * Initializes a selection element for evaluating a keyword in given positions.
782  *
783  * \param[in]   method  Keyword selection method to evaluate.
784  * \param[in]   params  Parameters to pass to initialization (the child positions).
785  * \param[in]   scanner Scanner data structure.
786  * \returns     Pointer to the created selection element.
787  *
788  * Implements _gmx_sel_init_keyword_evaluator() for \ref POS_VALUE input
789  * selections.
790  */
791 static gmx::SelectionTreeElementPointer init_evaluator_pos(gmx_ana_selmethod_t* method,
792                                                            const gmx::SelectionParserParameterList& params,
793                                                            void* scanner)
794 {
795     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL)) || method->outinit || method->pupdate == nullptr)
796     {
797         std::string message =
798                 gmx::formatString("Keyword '%s' cannot be evaluated in this context", method->name);
799         GMX_THROW(gmx::InvalidInputError(message));
800     }
801
802     gmx::SelectionTreeElementPointer sel(new gmx::SelectionTreeElement(
803             SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
804     _gmx_selelem_set_method(sel, method, scanner);
805
806     t_methoddata_kweval* data = new t_methoddata_kweval(sel->u.expr.method, sel->u.expr.mdata);
807
808     snew(sel->u.expr.method, 1);
809     sel->u.expr.method->name        = data->kwmethod->name;
810     sel->u.expr.method->type        = data->kwmethod->type;
811     sel->u.expr.method->flags       = data->kwmethod->flags | SMETH_SINGLEVAL;
812     sel->u.expr.method->init_data   = nullptr;
813     sel->u.expr.method->set_poscoll = nullptr;
814     sel->u.expr.method->init        = method->init ? &init_kweval : nullptr;
815     sel->u.expr.method->outinit     = nullptr;
816     sel->u.expr.method->free        = &free_data_kweval;
817     sel->u.expr.method->init_frame  = method->init_frame ? &init_frame_kweval : nullptr;
818     sel->u.expr.method->update      = &evaluate_kweval_pos;
819     sel->u.expr.method->pupdate     = nullptr;
820     sel->u.expr.method->nparams     = asize(smparams_kweval_pos);
821     sel->u.expr.method->param       = smparams_kweval_pos;
822     _gmx_selelem_init_method_params(sel, scanner);
823     sel->u.expr.mdata = data;
824
825     sel->u.expr.method->param[0].val.u.p = &data->p;
826
827     _gmx_sel_parse_params(params, sel->u.expr.method->nparams, sel->u.expr.method->param, sel, scanner);
828     return sel;
829 }
830
831 gmx::SelectionTreeElementPointer _gmx_sel_init_keyword_evaluator(gmx_ana_selmethod_t* method,
832                                                                  const gmx::SelectionTreeElementPointer& child,
833                                                                  void* scanner)
834 {
835     gmx::SelectionParserParameterList params;
836     params.push_back(gmx::SelectionParserParameter::createFromExpression(nullptr, child));
837     if (child->v.type == GROUP_VALUE)
838     {
839         return init_evaluator_group(method, params, scanner);
840     }
841     else if (child->v.type == POS_VALUE)
842     {
843         return init_evaluator_pos(method, params, scanner);
844     }
845     else
846     {
847         std::string text(_gmx_sel_lexer_get_text(scanner, child->location()));
848         std::string message =
849                 gmx::formatString("Expression '%s' cannot be used to evaluate keywords", text.c_str());
850         GMX_THROW(gmx::InvalidInputError(message));
851     }
852 }