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