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