Use smart pointers to manage the selection tree.
[alexxy/gromacs.git] / src / gromacs / selection / symrec.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 functions in symrec.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include "gromacs/legacyheaders/macros.h"
43 #include "gromacs/legacyheaders/smalloc.h"
44 #include "gromacs/legacyheaders/string2.h"
45
46 #include "gromacs/selection/poscalc.h"
47 #include "gromacs/utility/gmxassert.h"
48
49 #include "selelem.h"
50 #include "symrec.h"
51
52 /*! \internal \brief
53  * Symbol table for the selection parser.
54  */
55 struct gmx_sel_symtab_t
56 {
57     /** Pointer to the first symbol in the linked list of symbols. */
58     gmx_sel_symrec_t *first;
59 };
60
61 /*! \internal \brief
62  * Single symbol for the selection parser.
63  *
64  * \todo
65  * Make this a proper class.
66  */
67 struct gmx_sel_symrec_t
68 {
69     /** Name of the symbol. */
70     char                           *name;
71     /** Type of the symbol. */
72     e_symbol_t                      type;
73     /** Pointer to the method structure (\ref SYMBOL_METHOD). */
74     struct gmx_ana_selmethod_t         *meth;
75     /** Pointer to the variable value (\ref SYMBOL_VARIABLE). */
76     gmx::SelectionTreeElementPointer    var;
77     /** Pointer to the next symbol. */
78     struct gmx_sel_symrec_t        *next;
79 };
80
81 /** List of reserved symbols to register in add_reserved_symbols(). */
82 static const char *const sym_reserved[] = {
83     "group",
84     "to",
85     "not",
86     "and",
87     "or",
88     "xor",
89     "yes",
90     "no",
91     "on",
92     "off",
93     "help",
94 };
95
96 /*!
97  * \param[in] sym Symbol to query.
98  * \returns   The name of \p sym.
99  *
100  * The returned pointer should not be free'd.
101  */
102 char *
103 _gmx_sel_sym_name(gmx_sel_symrec_t *sym)
104 {
105     return sym->name;
106 }
107
108 /*!
109  * \param[in] sym Symbol to query.
110  * \returns   The type of \p sym.
111  */
112 e_symbol_t
113 _gmx_sel_sym_type(gmx_sel_symrec_t *sym)
114 {
115     return sym->type;
116 }
117
118 /*!
119  * \param[in] sym Symbol to query.
120  * \returns   The method associated with \p sym, or NULL if \p sym is not a
121  *   \ref SYMBOL_METHOD symbol.
122  */
123 struct gmx_ana_selmethod_t *
124 _gmx_sel_sym_value_method(gmx_sel_symrec_t *sym)
125 {
126     GMX_RELEASE_ASSERT(sym->type == SYMBOL_METHOD,
127             "Attempting to get method handle for a non-method symbol");
128     return sym->meth;
129 }
130
131 /*!
132  * \param[in] sym Symbol to query.
133  * \returns   The variable expression associated with \p sym, or NULL if
134  *   \p sym is not a \ref SYMBOL_VARIABLE symbol.
135  */
136 const gmx::SelectionTreeElementPointer &
137 _gmx_sel_sym_value_var(gmx_sel_symrec_t *sym)
138 {
139     GMX_RELEASE_ASSERT(sym->type == SYMBOL_VARIABLE,
140             "Attempting to get variable value for a non-variable symbol");
141     return sym->var;
142 }
143
144 /*! \brief
145  * Adds the reserved symbols to a symbol table.
146  * 
147  * \param[in,out] tab  Symbol table to which the symbols are added.
148  *
149  * Assumes that the symbol table is empty.
150  */
151 static void
152 add_reserved_symbols(gmx_sel_symtab_t *tab)
153 {
154     gmx_sel_symrec_t *last;
155     size_t            i;
156
157     last = NULL;
158     for (i = 0; i < asize(sym_reserved); ++i)
159     {
160         gmx_sel_symrec_t *sym = new gmx_sel_symrec_t();
161         sym->name = strdup(sym_reserved[i]);
162         sym->type = SYMBOL_RESERVED;
163         sym->meth = NULL;
164         sym->next = NULL;
165         if (last)
166         {
167             last->next = sym;
168         }
169         else
170         {
171             tab->first = sym;
172         }
173         last = sym;
174     }
175 }
176
177 /*! \brief
178  * Adds the position symbols to the symbol list.
179  * 
180  * \param[in,out] tab  Symbol table to which the symbols are added.
181  */
182 static void
183 add_position_symbols(gmx_sel_symtab_t *tab)
184 {
185     gmx_sel_symrec_t  *last;
186     int                i;
187
188     const char *const *postypes
189         = gmx::PositionCalculationCollection::typeEnumValues;
190     last = tab->first;
191     while (last && last->next)
192     {
193         last = last->next;
194     }
195     for (i = 0; postypes[i] != NULL; ++i)
196     {
197         gmx_sel_symrec_t  *sym = new gmx_sel_symrec_t();
198         sym->name = strdup(postypes[i]);
199         sym->type = SYMBOL_POS;
200         sym->meth = NULL;
201         sym->next = NULL;
202         if (last)
203         {
204             last->next = sym;
205         }
206         else
207         {
208             tab->first = sym;
209         }
210         last = sym;
211     }
212 }
213
214 /*!
215  * \param[out] tabp Symbol table pointer to initialize.
216  *
217  * Reserved and position symbols are added to the created table.
218  */
219 int
220 _gmx_sel_symtab_create(gmx_sel_symtab_t **tabp)
221 {
222     gmx_sel_symtab_t *tab;
223
224     snew(tab, 1);
225     add_reserved_symbols(tab);
226     add_position_symbols(tab);
227     *tabp = tab;
228     return 0;
229 }
230
231 /*!
232  * \param[in] tab Symbol table to free.
233  *
234  * The pointer \p tab is invalid after the call.
235  */
236 void
237 _gmx_sel_symtab_free(gmx_sel_symtab_t *tab)
238 {
239     gmx_sel_symrec_t *sym;
240
241     while (tab->first)
242     {
243         sym = tab->first;
244         tab->first = sym->next;
245         sfree(sym->name);
246         delete sym;
247     }
248     sfree(tab);
249 }
250
251 /*!
252  * \param[in] tab    Symbol table to search.
253  * \param[in] name   Symbol name to find.
254  * \param[in] bExact If false, symbols that begin with \p name are also
255  *   considered.
256  * \returns   Pointer to the symbol with name \p name, or NULL if not found.
257  *
258  * If no exact match is found and \p bExact is false, returns a symbol that
259  * begins with \p name if a unique matching symbol is found.
260  */
261 gmx_sel_symrec_t *
262 _gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, bool bExact)
263 {
264     return _gmx_sel_find_symbol_len(tab, name, strlen(name), bExact);
265 }
266
267 /*!
268  * \param[in] tab    Symbol table to search.
269  * \param[in] name   Symbol name to find.
270  * \param[in] len    Only consider the first \p len characters of \p name.
271  * \param[in] bExact If false, symbols that begin with \p name are also
272  *   considered.
273  * \returns   Pointer to the symbol with name \p name, or NULL if not found.
274  *
275  * If no exact match is found and \p bExact is false, returns a symbol that
276  * begins with \p name if a unique matching symbol is found.
277  *
278  * The parameter \p len is there to allow using this function from scanner.l
279  * without modifying the text to be scanned or copying it.
280  */
281 gmx_sel_symrec_t *
282 _gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
283                          bool bExact)
284 {
285     gmx_sel_symrec_t *sym;
286     gmx_sel_symrec_t *match;
287     bool              bUnique;
288     bool              bMatch;
289
290     match = NULL;
291     bUnique = true;
292     bMatch  = false;
293     sym = tab->first;
294     while (sym)
295     {
296         if (!strncmp(sym->name, name, len))
297         {
298             if (strlen(sym->name) == len)
299             {
300                 return sym;
301             }
302             if (bMatch)
303             {
304                 bUnique = false;
305             }
306             bMatch = true;
307             if (sym->type == SYMBOL_METHOD)
308             {
309                 match = sym;
310             }
311         }
312         sym = sym->next;
313     }
314     if (bExact)
315     {
316         return NULL;
317     }
318
319     if (!bUnique)
320     {
321         fprintf(stderr, "parse error: ambiguous symbol\n");
322         return NULL;
323     }
324     return match;
325 }
326
327 /*!
328  * \param[in] tab   Symbol table to search.
329  * \param[in] type  Type of symbol to find.
330  * \returns   The first symbol in \p tab with type \p type,
331  *   or NULL if there are no such symbols.
332  */
333 gmx_sel_symrec_t *
334 _gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type)
335 {
336     gmx_sel_symrec_t *sym;
337
338     sym = tab->first;
339     while (sym)
340     {
341         if (sym->type == type)
342         {
343             return sym;
344         }
345         sym = sym->next;
346     }
347     return NULL;
348 }
349
350 /*!
351  * \param[in] after Start the search after this symbol.
352  * \param[in] type  Type of symbol to find.
353  * \returns   The next symbol after \p after with type \p type,
354  *   or NULL if there are no more symbols.
355  */
356 gmx_sel_symrec_t *
357 _gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type)
358 {
359     gmx_sel_symrec_t *sym;
360
361     sym = after->next;
362     while (sym)
363     {
364         if (sym->type == type)
365         {
366             return sym;
367         }
368         sym = sym->next;
369     }
370     return NULL;
371 }
372
373 /*! \brief
374  * Internal utility function used in adding symbols to a symbol table.
375  *
376  * \param[in,out] tab   Symbol table to add the symbol to.
377  * \param[in]     name  Name of the symbol to add.
378  * \param[out]    ctype On error, the type of the conflicting symbol is
379  *   written to \p *ctype.
380  * \returns       Pointer to the new symbol record, or NULL if \p name
381  *   conflicts with an existing symbol.
382  */
383 static gmx_sel_symrec_t *
384 add_symbol(gmx_sel_symtab_t *tab, const char *name, e_symbol_t *ctype)
385 {
386     gmx_sel_symrec_t *sym, *psym;
387
388     /* Check if there is a conflicting symbol */
389     psym = NULL;
390     sym  = tab->first;
391     while (sym)
392     {
393         if (!gmx_strcasecmp(sym->name, name))
394         {
395             *ctype = sym->type;
396             return NULL;
397         }
398         psym = sym;
399         sym  = sym->next;
400     }
401
402     /* Create a new symbol record */
403     if (psym == NULL)
404     {
405         tab->first = new gmx_sel_symrec_t();
406         sym = tab->first;
407     }
408     else
409     {
410         psym->next = new gmx_sel_symrec_t();
411         sym = psym->next;
412     }
413     sym->name = strdup(name);
414     sym->meth = NULL;
415     sym->next = NULL;
416     return sym;
417 }
418
419 /*!
420  * \param[in,out] tab    Symbol table to add the symbol to.
421  * \param[in]     name   Name of the new symbol.
422  * \param[in]     sel    Value of the variable.
423  * \returns       Pointer to the created symbol record, or NULL if there was a
424  *   symbol with the same name.
425  */
426 gmx_sel_symrec_t *
427 _gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
428                         const gmx::SelectionTreeElementPointer &sel)
429 {
430     gmx_sel_symrec_t *sym;
431     e_symbol_t        ctype;
432
433     sym = add_symbol(tab, name, &ctype);
434     if (!sym)
435     {
436         fprintf(stderr, "parse error: ");
437         switch (ctype)
438         {
439             case SYMBOL_RESERVED:
440             case SYMBOL_POS:
441                 fprintf(stderr, "variable name (%s) conflicts with a reserved keyword\n",
442                         name);
443                 break;
444             case SYMBOL_VARIABLE:
445                 fprintf(stderr, "duplicate variable name (%s)\n", name);
446                 break;
447             case SYMBOL_METHOD:
448                 fprintf(stderr, "variable name (%s) conflicts with a selection keyword\n",
449                         name);
450                 break;
451         }
452         return NULL;
453     }
454
455     sym->type  = SYMBOL_VARIABLE;
456     sym->var = sel;
457     return sym;
458 }
459
460 /*!
461  * \param[in,out] tab    Symbol table to add the symbol to.
462  * \param[in]     name   Name of the new symbol.
463  * \param[in]     method Method that this symbol represents.
464  * \returns       Pointer to the created symbol record, or NULL if there was a
465  *   symbol with the same name.
466  */
467 gmx_sel_symrec_t *
468 _gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
469                            struct gmx_ana_selmethod_t *method)
470 {
471     gmx_sel_symrec_t *sym;
472     e_symbol_t        ctype;
473
474     sym = add_symbol(tab, name, &ctype);
475     if (!sym)
476     {
477         fprintf(stderr, "parse error: ");
478         switch (ctype)
479         {
480             case SYMBOL_RESERVED:
481             case SYMBOL_POS:
482                 fprintf(stderr, "method name (%s) conflicts with a reserved keyword\n",
483                         name);
484                 break;
485             case SYMBOL_VARIABLE:
486                 fprintf(stderr, "method name (%s) conflicts with a variable name\n",
487                         name);
488                 break;
489             case SYMBOL_METHOD:
490                 fprintf(stderr, "duplicate method name (%s)\n", name);
491                 break;
492         }
493         return NULL;
494     }
495
496     sym->type   = SYMBOL_METHOD;
497     sym->meth = method;
498     return sym;
499 }