Add syntax to force selection string matching mode.
[alexxy/gromacs.git] / src / gromacs / selection / sm_keywords.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements internal selection methods for numeric and string keyword
34  * evaluation.
35  *
36  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
37  * \ingroup module_selection
38  */
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <ctype.h>
44 #ifdef HAVE_SYS_TYPES_H
45 #include <sys/types.h>  /*old Mac needs types before regex.h*/
46 #endif
47 #ifdef HAVE_REGEX_H
48 #include <regex.h>
49 #define USE_REGEX
50 #endif
51
52 #include <string>
53
54 #include "gromacs/legacyheaders/macros.h"
55 #include "gromacs/legacyheaders/smalloc.h"
56 #include "gromacs/legacyheaders/string2.h"
57
58 #include "gromacs/selection/selmethod.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/messagestringcollector.h"
61 #include "gromacs/utility/stringutil.h"
62
63 #include "keywords.h"
64 #include "parsetree.h"
65 #include "scanner.h"
66 #include "selelem.h"
67
68 /** Allocates data for integer keyword evaluation. */
69 static void *
70 init_data_kwint(int npar, gmx_ana_selparam_t *param);
71 /** Allocates data for real keyword evaluation. */
72 static void *
73 init_data_kwreal(int npar, gmx_ana_selparam_t *param);
74 /** Allocates data for string keyword evaluation. */
75 static void *
76 init_data_kwstr(int npar, gmx_ana_selparam_t *param);
77 /** Initializes data for integer keyword evaluation. */
78 static void
79 init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
80 /** Initializes data for real keyword evaluation. */
81 static void
82 init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
83 /** Initializes data for string keyword evaluation. */
84 static void
85 init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
86 /** Frees the memory allocated for string keyword evaluation. */
87 static void
88 free_data_kwstr(void *data);
89 /** Evaluates integer selection keywords. */
90 static void
91 evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
92                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
93 /** Evaluates real selection keywords. */
94 static void
95 evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
96                       gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
97 /** Evaluates string selection keywords. */
98 static void
99 evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
100                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
101
102 /*! \internal \brief
103  * Data structure for integer keyword expression evaluation.
104  */
105 typedef struct t_methoddata_kwint
106 {
107     /** Array of values for the keyword. */
108     int               *v;
109     /** Number of ranges in the \p r array. */
110     int                n;
111     /*! \brief
112      * Array of sorted integer ranges to match against.
113      *
114      * Each range is made of two integers, giving the endpoints (inclusive).
115      * This field stores the pointer to the ranges allocated by the
116      * parameter parser; see \ref SPAR_RANGES for more information.
117      */
118     int               *r;
119 } t_methoddata_kwint;
120
121 /*! \internal \brief
122  * Data structure for real keyword expression evaluation.
123  */
124 typedef struct t_methoddata_kwreal
125 {
126     /** Array of values for the keyword. */
127     real              *v;
128     /** Number of ranges in the \p r array. */
129     int                n;
130     /*! \brief
131      * Array of sorted ranges to match against.
132      *
133      * Each range is made of two values, giving the endpoints (inclusive).
134      * This field stores the pointer to the ranges allocated by the
135      * parameter parser; see \ref SPAR_RANGES for more information.
136      */
137     real              *r;
138 } t_methoddata_kwreal;
139
140 /*! \internal \brief
141  * Data structure for string keyword expression evaluation.
142  */
143 typedef struct t_methoddata_kwstr
144 {
145     /** Matching type for the strings. */
146     gmx::SelectionStringMatchType       matchType;
147     /** Array of values for the keyword. */
148     char             **v;
149     /** Number of elements in the \p val array. */
150     int                n;
151     /*! \internal \brief
152      * Array of strings/regular expressions to match against.
153      */
154     struct t_methoddata_kwstr_match {
155         /** true if the expression is a regular expression, false otherwise. */
156         bool           bRegExp;
157         /** The value to match against. */
158         union {
159 #ifdef USE_REGEX
160             /** Compiled regular expression if \p bRegExp is true. */
161             regex_t    r;
162 #endif
163             /** The string if \p bRegExp is false; */
164             const char *s;
165         }              u;
166     }                 *m;
167     /**< Array of strings/regular expressions to match against.*/
168 } t_methoddata_kwstr;
169
170 /** Parameters for integer keyword evaluation. */
171 static gmx_ana_selparam_t smparams_keyword_int[] = {
172     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
173     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
174 };
175
176 /** Parameters for real keyword evaluation. */
177 static gmx_ana_selparam_t smparams_keyword_real[] = {
178     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL | SPAR_DYNAMIC},
179     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
180 };
181
182 /** Parameters for string keyword evaluation. */
183 static gmx_ana_selparam_t smparams_keyword_str[] = {
184     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
185     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
186 };
187
188 /** \internal Selection method data for integer keyword evaluation. */
189 gmx_ana_selmethod_t sm_keyword_int = {
190     "kw_int", GROUP_VALUE, SMETH_SINGLEVAL,
191     asize(smparams_keyword_int), smparams_keyword_int,
192     &init_data_kwint,
193      NULL,
194     &init_kwint,
195      NULL,
196      NULL,
197      NULL,
198     &evaluate_keyword_int,
199      NULL,
200     {NULL, 0, NULL},
201 };
202
203 /** \internal Selection method data for real keyword evaluation. */
204 gmx_ana_selmethod_t sm_keyword_real = {
205     "kw_real", GROUP_VALUE, SMETH_SINGLEVAL,
206     asize(smparams_keyword_real), smparams_keyword_real,
207     &init_data_kwreal,
208      NULL,
209     &init_kwreal,
210      NULL,
211      NULL,
212      NULL,
213     &evaluate_keyword_real,
214      NULL,
215     {NULL, 0, NULL},
216 };
217
218 /** \internal Selection method data for string keyword evaluation. */
219 gmx_ana_selmethod_t sm_keyword_str = {
220     "kw_str", GROUP_VALUE, SMETH_SINGLEVAL,
221     asize(smparams_keyword_str), smparams_keyword_str,
222     &init_data_kwstr,
223      NULL,
224     &init_kwstr,
225      NULL,
226     &free_data_kwstr,
227      NULL,
228     &evaluate_keyword_str,
229      NULL,
230     {NULL, 0, NULL},
231 };
232
233 /** Initializes keyword evaluation for an arbitrary group. */
234 static void
235 init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
236 /** Initializes output for keyword evaluation in an arbitrary group. */
237 static void
238 init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data);
239 /** Frees the data allocated for keyword evaluation in an arbitrary group. */
240 static void
241 free_data_kweval(void *data);
242 /** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
243 static void
244 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
245 /** Evaluates keywords in an arbitrary group. */
246 static void
247 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
248                 gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
249
250 /*! \internal \brief
251  * Data structure for keyword evaluation in arbitrary groups.
252  */
253 typedef struct
254 {
255     /** Wrapped keyword method for evaluating the values. */
256     gmx_ana_selmethod_t  *kwmethod;
257     /** Method data for \p kwmethod. */
258     void                 *kwmdata;
259     /** Group in which \p kwmethod should be evaluated. */
260     gmx_ana_index_t       g;
261 } t_methoddata_kweval;
262
263 /** Parameters for keyword evaluation in an arbitrary group. */
264 static gmx_ana_selparam_t smparams_kweval[] = {
265     {NULL,   {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
266 };
267
268
269 /********************************************************************
270  * INTEGER KEYWORD EVALUATION
271  ********************************************************************/
272
273 /*!
274  * \param[in] npar  Not used.
275  * \param     param Not used.
276  * \returns   Pointer to the allocated data (\ref t_methoddata_kwint).
277  *
278  * Allocates memory for a \ref t_methoddata_kwint structure.
279  */
280 static void *
281 init_data_kwint(int npar, gmx_ana_selparam_t *param)
282 {
283     t_methoddata_kwint *data;
284
285     snew(data, 1);
286     return data;
287 }
288
289 /*!
290  * \param[in] top   Not used.
291  * \param[in] npar  Not used (should be 2).
292  * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
293  * \param[in] data  Should point to \ref t_methoddata_kwint.
294  */
295 static void
296 init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
297 {
298     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
299
300     d->v = param[0].val.u.i;
301     d->n = param[1].val.nr;
302     d->r = param[1].val.u.i;
303 }
304
305 /*!
306  * See sel_updatefunc() for description of the parameters.
307  * \p data should point to a \c t_methoddata_kwint.
308  *
309  * Does a binary search to find which atoms match the ranges in the
310  * \c t_methoddata_kwint structure for this selection.
311  * Matching atoms are stored in \p out->u.g.
312  */
313 static void
314 evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
315                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
316 {
317     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
318     int                 n, i, j, jmin, jmax;
319     int                 val;
320
321     out->u.g->isize = 0;
322     n    = d->n;
323     for (i = 0; i < g->isize; ++i)
324     {
325         val = d->v[i];
326         if (d->r[0] > val || d->r[2*n-1] < val)
327         {
328             continue;
329         }
330         jmin = 0;
331         jmax = n;
332         while (jmax - jmin > 1)
333         {
334             j = jmin + (jmax - jmin) / 2;
335             if (val < d->r[2*j])
336             {
337                 jmax = j;
338             }
339             else
340             {
341                 jmin = j;
342                 if (val <= d->r[2*j+1])
343                 {
344                     break;
345                 }
346                 /* ++jmin;*/
347             }
348         }
349         if (val <= d->r[2*jmin+1])
350         {
351             out->u.g->index[out->u.g->isize++] = g->index[i];
352         }
353     }
354 }
355
356
357 /********************************************************************
358  * REAL KEYWORD EVALUATION
359  ********************************************************************/
360
361 /*!
362  * \param[in] npar  Not used.
363  * \param     param Not used.
364  * \returns   Pointer to the allocated data (\ref t_methoddata_kwreal).
365  *
366  * Allocates memory for a \ref t_methoddata_kwreal structure.
367  */
368 static void *
369 init_data_kwreal(int npar, gmx_ana_selparam_t *param)
370 {
371     t_methoddata_kwreal *data;
372
373     snew(data, 1);
374     return data;
375 }
376
377 /*!
378  * \param[in] top   Not used.
379  * \param[in] npar  Not used (should be 2).
380  * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
381  * \param[in] data  Should point to \ref t_methoddata_kwreal.
382  * \returns   0 (the initialization always succeeds).
383  */
384 static void
385 init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
386 {
387     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
388
389     d->v = param[0].val.u.r;
390     d->n = param[1].val.nr;
391     d->r = param[1].val.u.r;
392 }
393
394 /*!
395  * See sel_updatefunc() for description of the parameters.
396  * \p data should point to a \c t_methoddata_kwreal.
397  *
398  * Does a binary search to find which atoms match the ranges in the
399  * \c t_methoddata_kwreal structure for this selection.
400  * Matching atoms are stored in \p out->u.g.
401  */
402 static void
403 evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
404                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
405 {
406     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
407     int                  n, i, j, jmin, jmax;
408     real                 val;
409
410     out->u.g->isize = 0;
411     n    = d->n;
412     for (i = 0; i < g->isize; ++i)
413     {
414         val = d->v[i];
415         if (d->r[0] > val || d->r[2*n-1] < val)
416         {
417             continue;
418         }
419         jmin = 0;
420         jmax = n;
421         while (jmax - jmin > 1)
422         {
423             j = jmin + (jmax - jmin) / 2;
424             if (val < d->r[2*j])
425             {
426                 jmax = j;
427             }
428             else
429             {
430                 jmin = j;
431                 if (val <= d->r[2*j+1])
432                 {
433                     break;
434                 }
435                 /* ++jmin;*/
436             }
437         }
438         if (val <= d->r[2*jmin+1])
439         {
440             out->u.g->index[out->u.g->isize++] = g->index[i];
441         }
442     }
443 }
444
445
446 /********************************************************************
447  * STRING KEYWORD EVALUATION
448  ********************************************************************/
449
450 /*!
451  * \param[in] npar  Not used.
452  * \param     param Not used.
453  * \returns Pointer to the allocated data (\ref t_methoddata_kwstr).
454  *
455  * Allocates memory for a \ref t_methoddata_kwstr structure.
456  */
457 static void *
458 init_data_kwstr(int npar, gmx_ana_selparam_t *param)
459 {
460     t_methoddata_kwstr *data;
461
462     snew(data, 1);
463     data->matchType = gmx::eStringMatchType_Auto;
464     return data;
465 }
466
467 /*!
468  * \param[in,out] sel   Selection element to initialize.
469  * \param[in]     matchType  Method to use to match string values.
470  *
471  * Sets the string matching method for string keyword matching.
472  */
473 void
474 _gmx_selelem_set_kwstr_match_type(const gmx::SelectionTreeElementPointer &sel,
475                                   gmx::SelectionStringMatchType matchType)
476 {
477     t_methoddata_kwstr *d = (t_methoddata_kwstr *)sel->u.expr.mdata;
478
479     if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
480         || sel->u.expr.method->name != sm_keyword_str.name)
481     {
482         return;
483     }
484     d->matchType = matchType;
485 }
486
487 /*!
488  * \param[in] top   Not used.
489  * \param[in] npar  Not used (should be 2).
490  * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
491  * \param[in] data  Should point to \ref t_methoddata_kwstr.
492  */
493 static void
494 init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
495 {
496     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
497     int                 i;
498
499     d->v   = param[0].val.u.s;
500     d->n   = param[1].val.nr;
501     /* Return if this is not the first time */
502     if (d->m)
503     {
504         return;
505     }
506     snew(d->m, d->n);
507     for (i = 0; i < d->n; ++i)
508     {
509         const char *s = param[1].val.u.s[i];
510         bool bRegExp = (d->matchType == gmx::eStringMatchType_RegularExpression);
511         if (d->matchType == gmx::eStringMatchType_Auto)
512         {
513             for (size_t j = 0; j < strlen(s); ++j)
514             {
515                 if (ispunct(s[j]) && s[j] != '?' && s[j] != '*')
516                 {
517                     bRegExp = true;
518                     break;
519                 }
520             }
521         }
522         if (bRegExp)
523         {
524 #ifdef USE_REGEX
525             std::string buf(gmx::formatString("^%s$", s));
526             if (regcomp(&d->m[i].u.r, buf.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
527             {
528                 GMX_THROW(gmx::InvalidInputError(gmx::formatString(
529                                 "Error in regular expression \"%s\"", s)));
530             }
531 #else
532             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
533                             "No regular expression support, cannot match \"%s\"", s)));
534 #endif
535         }
536         else
537         {
538             d->m[i].u.s = s;
539         }
540         d->m[i].bRegExp = bRegExp;
541     }
542 }
543
544 /*!
545  * \param data Data to free (should point to a \ref t_methoddata_kwstr).
546  *
547  * Frees the memory allocated for t_methoddata_kwstr::val.
548  */
549 static void
550 free_data_kwstr(void *data)
551 {
552     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
553     int                 i;
554
555     for (i = 0; i < d->n; ++i)
556     {
557         if (d->m[i].bRegExp)
558         {
559 #ifdef USE_REGEX
560             /* This branch should only be taken if regular expressions
561              * are available, but the ifdef is still needed. */
562             regfree(&d->m[i].u.r);
563 #endif
564         }
565     }
566     sfree(d->m);
567 }
568
569 /*!
570  * See sel_updatefunc() for description of the parameters.
571  * \p data should point to a \c t_methoddata_kwstr.
572  *
573  * Does a linear search to find which atoms match the strings in the
574  * \c t_methoddata_kwstr structure for this selection.
575  * Wildcards are allowed in the strings.
576  * Matching atoms are stored in \p out->u.g.
577  */
578 static void
579 evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
580                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
581 {
582     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
583     int                 i, j;
584     bool                bFound;
585
586     out->u.g->isize = 0;
587     for (i = 0; i < g->isize; ++i)
588     {
589         bFound = false;
590         for (j = 0; j < d->n && !bFound; ++j)
591         {
592             if (d->m[j].bRegExp)
593             {
594 #ifdef USE_REGEX
595                 /* This branch should only be taken if regular expressions
596                  * are available, but the ifdef is still needed. */
597                 bFound = (regexec(&d->m[j].u.r, d->v[i], 0, NULL, 0) == 0);
598 #endif
599             }
600             else if (d->matchType == gmx::eStringMatchType_Exact)
601             {
602                 bFound = (strcmp(d->m[j].u.s, d->v[i]) == 0);
603             }
604             else
605             {
606                 bFound = (gmx_wcmatch(d->m[j].u.s, d->v[i]) == 0);
607             }
608         }
609         if (bFound)
610         {
611             out->u.g->index[out->u.g->isize++] = g->index[i];
612         }
613     }
614 }
615
616
617 /********************************************************************
618  * KEYWORD EVALUATION FOR ARBITRARY GROUPS
619  ********************************************************************/
620
621 /*!
622  * \param[in] top   Not used.
623  * \param[in] npar  Not used.
624  * \param[in] param Not used.
625  * \param[in] data  Should point to \ref t_methoddata_kweval.
626  * \returns   0 on success, a non-zero error code on return.
627  *
628  * Calls the initialization method of the wrapped keyword.
629  */
630 static void
631 init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
632 {
633     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
634
635     d->kwmethod->init(top, 0, NULL, d->kwmdata);
636 }
637
638 /*!
639  * \param[in]     top   Not used.
640  * \param[in,out] out   Pointer to output data structure.
641  * \param[in,out] data  Should point to \c t_methoddata_kweval.
642  * \returns       0 for success.
643  */
644 static void
645 init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data)
646 {
647     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
648
649     out->nr = d->g.isize;
650 }
651
652 /*!
653  * \param data Data to free (should point to a \c t_methoddata_kweval).
654  *
655  * Frees the memory allocated for all the members of \c t_methoddata_kweval.
656  */
657 static void
658 free_data_kweval(void *data)
659 {
660     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
661
662     _gmx_selelem_free_method(d->kwmethod, d->kwmdata);
663 }
664
665 /*!
666  * \param[in]  top  Topology.
667  * \param[in]  fr   Current frame.
668  * \param[in]  pbc  PBC structure.
669  * \param      data Should point to a \ref t_methoddata_kweval.
670  * \returns    0 on success, a non-zero error code on error.
671  *
672  * Creates a lookup structure that enables fast queries of whether a point
673  * is within the solid angle or not.
674  */
675 static void
676 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
677 {
678     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
679
680     d->kwmethod->init_frame(top, fr, pbc, d->kwmdata);
681 }
682
683 /*!
684  * See sel_updatefunc() for description of the parameters.
685  * \p data should point to a \c t_methoddata_kweval.
686  *
687  * Calls the evaluation function of the wrapped keyword with the given
688  * parameters, with the exception of using \c t_methoddata_kweval::g for the
689  * evaluation group.
690  */
691 static void
692 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
693                 gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
694 {
695     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
696
697     d->kwmethod->update(top, fr, pbc, &d->g, out, d->kwmdata);
698 }
699
700 /*!
701  * \param[in]   method  Keyword selection method to evaluate.
702  * \param[in]   params  Parameter that gives the group to evaluate \p method in.
703  * \param[in]   scanner Scanner data structure.
704  * \returns     Pointer to the created selection element (NULL on error).
705  *
706  * Creates a \ref SEL_EXPRESSION selection element that evaluates the keyword
707  * method given by \p method in the group given by \p param.
708  *
709  * The name of \p param should be empty.
710  */
711 gmx::SelectionTreeElementPointer
712 _gmx_sel_init_keyword_evaluator(gmx_ana_selmethod_t *method,
713                                 const gmx::SelectionParserParameterList &params,
714                                 void *scanner)
715 {
716     gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
717     char  buf[1024];
718     sprintf(buf, "In evaluation of '%s'", method->name);
719     gmx::MessageStringContext   context(errors, buf);
720
721     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
722         || method->outinit || method->pupdate)
723     {
724         GMX_THROW(gmx::InternalError(
725                 "Unsupported keyword method for arbitrary group evaluation"));
726     }
727
728     gmx::SelectionTreeElementPointer sel(
729             new gmx::SelectionTreeElement(SEL_EXPRESSION));
730     _gmx_selelem_set_method(sel, method, scanner);
731
732     t_methoddata_kweval  *data;
733     snew(data, 1);
734     data->kwmethod = sel->u.expr.method;
735     data->kwmdata  = sel->u.expr.mdata;
736     gmx_ana_index_clear(&data->g);
737
738     snew(sel->u.expr.method, 1);
739     memcpy(sel->u.expr.method, data->kwmethod, sizeof(gmx_ana_selmethod_t));
740     sel->u.expr.method->flags       |= SMETH_VARNUMVAL;
741     sel->u.expr.method->init_data    = NULL;
742     sel->u.expr.method->set_poscoll  = NULL;
743     sel->u.expr.method->init         = method->init ? &init_kweval : NULL;
744     sel->u.expr.method->outinit      = &init_output_kweval;
745     sel->u.expr.method->free         = &free_data_kweval;
746     sel->u.expr.method->init_frame   = method->init_frame ? &init_frame_kweval : NULL;
747     sel->u.expr.method->update       = &evaluate_kweval;
748     sel->u.expr.method->pupdate      = NULL;
749     sel->u.expr.method->nparams      = asize(smparams_kweval);
750     sel->u.expr.method->param        = smparams_kweval;
751     _gmx_selelem_init_method_params(sel, scanner);
752     sel->u.expr.mdata = data;
753
754     sel->u.expr.method->param[0].val.u.g = &data->g;
755
756     if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
757                                sel->u.expr.method->param, sel, scanner))
758     {
759         return gmx::SelectionTreeElementPointer();
760     }
761     return sel;
762 }