Merge release-4-6 into master
[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 classes in symrec.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #include <map>
39 #include <string>
40 #include <utility>
41
42 #include "gromacs/legacyheaders/macros.h"
43
44 #include "gromacs/utility/exceptions.h"
45 #include "gromacs/utility/gmxassert.h"
46 #include "gromacs/utility/stringutil.h"
47 #include "gromacs/utility/uniqueptr.h"
48
49 #include "poscalc.h"
50 #include "selelem.h"
51 #include "symrec.h"
52
53 namespace gmx
54 {
55
56 /********************************************************************
57  * SelectionParserSymbol
58  */
59
60 /*! \internal \brief
61  * Private implementation class for SelectionParserSymbol.
62  *
63  * \ingroup module_selection
64  */
65 class SelectionParserSymbol::Impl
66 {
67     public:
68         /*! \brief
69          * Initializes a symbol.
70          *
71          * \param[in] type  Type for the symbol.
72          * \param[in] name  Name for the symbol.
73          *
74          * The symbol table is responsible for initializing the \a meth_ and
75          * \a var_ members as appropriate.
76          */
77         Impl(SymbolType type, const char *name)
78             : name_(name), type_(type), meth_(NULL)
79         {
80         }
81
82         //! Name of the symbol.
83         std::string                     name_;
84         //! Type of the symbol.
85         SymbolType                      type_;
86         //! Pointer to the method structure (\ref MethodSymbol).
87         gmx_ana_selmethod_t            *meth_;
88         //! Pointer to the variable value (\ref VariableSymbol).
89         SelectionTreeElementPointer     var_;
90 };
91
92 SelectionParserSymbol::SelectionParserSymbol(Impl *impl)
93     : impl_(impl)
94 {
95 }
96
97 SelectionParserSymbol::~SelectionParserSymbol()
98 {
99 }
100
101 const std::string &
102 SelectionParserSymbol::name() const
103 {
104     return impl_->name_;
105 }
106
107 SelectionParserSymbol::SymbolType
108 SelectionParserSymbol::type() const
109 {
110     return impl_->type_;
111 }
112
113 gmx_ana_selmethod_t *
114 SelectionParserSymbol::methodValue() const
115 {
116     GMX_RELEASE_ASSERT(type() == MethodSymbol,
117                        "Attempting to get method handle for a non-method symbol");
118     return impl_->meth_;
119 }
120
121 const gmx::SelectionTreeElementPointer &
122 SelectionParserSymbol::variableValue() const
123 {
124     GMX_RELEASE_ASSERT(type() == VariableSymbol,
125                        "Attempting to get variable value for a non-variable symbol");
126     return impl_->var_;
127 }
128
129 /********************************************************************
130  * SelectionParserSymbolTable::Impl
131  */
132
133 /*! \internal \brief
134  * Private implementation class for SelectionParserSymbolTable.
135  *
136  * All methods in this class may throw std::bad_alloc if out of memory.
137  *
138  * \ingroup module_selection
139  */
140 class SelectionParserSymbolTable::Impl
141 {
142     public:
143         //! Smart pointer type for managing a SelectionParserSymbol.
144         typedef gmx::gmx_unique_ptr<SelectionParserSymbol>::type
145             SymbolPointer;
146         //! Container type for the list of symbols.
147         typedef std::map<std::string, SymbolPointer> SymbolMap;
148
149         /*! \brief
150          * Adds a symbol to the symbol list.
151          *
152          * \param[in] symbol  Symbol to add.
153          */
154         void addSymbol(SymbolPointer symbol);
155         //! Adds the reserved symbols to this symbol table.
156         void addReservedSymbols();
157         //! Adds the position symbols to this symbol table.
158         void addPositionSymbols();
159
160         //! Symbols in this symbol table.
161         SymbolMap               symbols_;
162 };
163
164 void
165 SelectionParserSymbolTable::Impl::addSymbol(SymbolPointer symbol)
166 {
167     symbols_.insert(std::make_pair(symbol->name(), move(symbol)));
168 }
169
170 void
171 SelectionParserSymbolTable::Impl::addReservedSymbols()
172 {
173     const char *const sym_reserved[] = {
174         "group",
175         "to",
176         "not",
177         "and",
178         "or",
179         "xor",
180         "yes",
181         "no",
182         "on",
183         "off",
184         "help",
185     };
186
187     for (size_t i = 0; i < asize(sym_reserved); ++i)
188     {
189         SymbolPointer sym(new SelectionParserSymbol(
190                                   new SelectionParserSymbol::Impl(
191                                           SelectionParserSymbol::ReservedSymbol, sym_reserved[i])));
192         addSymbol(move(sym));
193     }
194 }
195
196 void
197 SelectionParserSymbolTable::Impl::addPositionSymbols()
198 {
199     const char *const *postypes
200         = gmx::PositionCalculationCollection::typeEnumValues;
201     for (int i = 0; postypes[i] != NULL; ++i)
202     {
203         SymbolPointer sym(new SelectionParserSymbol(
204                                   new SelectionParserSymbol::Impl(
205                                           SelectionParserSymbol::PositionSymbol, postypes[i])));
206         addSymbol(move(sym));
207     }
208 }
209
210 /********************************************************************
211  * SelectionParserSymbolIterator
212  */
213
214 /*! \internal \brief
215  * Private implementation class for SelectionParserSymbolIterator.
216  *
217  * \ingroup module_selection
218  */
219 class SelectionParserSymbolIterator::Impl
220 {
221     public:
222         //! Shorthand for the underlying iterator type.
223         typedef SelectionParserSymbolTable::Impl::SymbolMap::const_iterator
224             IteratorType;
225
226         /*! \brief
227          * Constructs an end iterator.
228          *
229          * \param[in] end  Iterator to the end of the iterated container.
230          */
231         explicit Impl(IteratorType end)
232             : iter_(end), end_(end)
233         {
234         }
235         /*! \brief
236          * Constructs an iterator.
237          *
238          * \param[in] iter Iterator to the current symbol.
239          * \param[in] end  Iterator to the end of the iterated container.
240          */
241         Impl(IteratorType iter, IteratorType end)
242             : iter_(iter), end_(end)
243         {
244         }
245
246         //! Underlying iterator to the symbol container.
247         IteratorType            iter_;
248         //! End of the symbol container being iterated.
249         IteratorType            end_;
250 };
251
252 SelectionParserSymbolIterator::SelectionParserSymbolIterator(Impl *impl)
253     : impl_(impl)
254 {
255 }
256
257 SelectionParserSymbolIterator::SelectionParserSymbolIterator(
258         const SelectionParserSymbolIterator &other)
259     : impl_(new Impl(*other.impl_))
260 {
261 }
262
263 SelectionParserSymbolIterator::~SelectionParserSymbolIterator()
264 {
265 }
266
267 SelectionParserSymbolIterator &SelectionParserSymbolIterator::operator=(
268         const SelectionParserSymbolIterator &other)
269 {
270     impl_.reset(new Impl(*other.impl_));
271     return *this;
272 }
273
274 bool SelectionParserSymbolIterator::operator==(
275         const SelectionParserSymbolIterator &other) const
276 {
277     return impl_->iter_ == other.impl_->iter_;
278 }
279
280 const SelectionParserSymbol &SelectionParserSymbolIterator::operator*() const
281 {
282     return *impl_->iter_->second;
283 }
284
285 SelectionParserSymbolIterator &SelectionParserSymbolIterator::operator++()
286 {
287     SelectionParserSymbol::SymbolType type = impl_->iter_->second->type();
288     do
289     {
290         ++impl_->iter_;
291     }
292     while (impl_->iter_ != impl_->end_ && impl_->iter_->second->type() != type);
293     return *this;
294 }
295
296 /********************************************************************
297  * SelectionParserSymbolTable
298  */
299
300 SelectionParserSymbolTable::SelectionParserSymbolTable()
301     : impl_(new Impl)
302 {
303     impl_->addReservedSymbols();
304     impl_->addPositionSymbols();
305 }
306
307 SelectionParserSymbolTable::~SelectionParserSymbolTable()
308 {
309 }
310
311 const SelectionParserSymbol *
312 SelectionParserSymbolTable::findSymbol(const std::string &name,
313                                        bool               bExact) const
314 {
315     Impl::SymbolMap::const_iterator sym = impl_->symbols_.lower_bound(name);
316     if (sym == impl_->symbols_.end())
317     {
318         return NULL;
319     }
320     if (sym->second->name() == name)
321     {
322         return sym->second.get();
323     }
324     if (!bExact && startsWith(sym->second->name(), name))
325     {
326         Impl::SymbolMap::const_iterator next = sym;
327         ++next;
328         if (next != impl_->symbols_.end()
329             && startsWith(next->second->name(), name))
330         {
331             GMX_THROW(InvalidInputError("'" + name + "' is ambiguous"));
332         }
333         if (sym->second->type() == SelectionParserSymbol::MethodSymbol)
334         {
335             return sym->second.get();
336         }
337     }
338     return NULL;
339 }
340
341 SelectionParserSymbolIterator
342 SelectionParserSymbolTable::beginIterator(SelectionParserSymbol::SymbolType type) const
343 {
344     Impl::SymbolMap::const_iterator sym;
345     Impl::SymbolMap::const_iterator end = impl_->symbols_.end();
346     for (sym = impl_->symbols_.begin(); sym != end; ++sym)
347     {
348         if (sym->second->type() == type)
349         {
350             return SelectionParserSymbolIterator(
351                     new SelectionParserSymbolIterator::Impl(sym, end));
352         }
353     }
354     return endIterator();
355 }
356
357 SelectionParserSymbolIterator
358 SelectionParserSymbolTable::endIterator() const
359 {
360     return SelectionParserSymbolIterator(
361             new SelectionParserSymbolIterator::Impl(impl_->symbols_.end()));
362 }
363
364 void
365 SelectionParserSymbolTable::addVariable(const char                             *name,
366                                         const gmx::SelectionTreeElementPointer &sel)
367 {
368     // In the current parser implementation, a syntax error is produced before
369     // this point is reached, but the check is here for robustness.
370     Impl::SymbolMap::const_iterator other = impl_->symbols_.find(name);
371     if (other != impl_->symbols_.end())
372     {
373         if (other->second->type() == SelectionParserSymbol::VariableSymbol)
374         {
375             GMX_THROW(InvalidInputError(
376                               formatString("Reassigning variable '%s' is not supported",
377                                            name)));
378         }
379         else
380         {
381             GMX_THROW(InvalidInputError(
382                               formatString("Variable name '%s' conflicts with a reserved keyword",
383                                            name)));
384         }
385     }
386     Impl::SymbolPointer sym(new SelectionParserSymbol(
387                                     new SelectionParserSymbol::Impl(
388                                             SelectionParserSymbol::VariableSymbol, name)));
389     sym->impl_->var_ = sel;
390     impl_->addSymbol(move(sym));
391 }
392
393 void
394 SelectionParserSymbolTable::addMethod(const char          *name,
395                                       gmx_ana_selmethod_t *method)
396 {
397     if (impl_->symbols_.find(name) != impl_->symbols_.end())
398     {
399         GMX_THROW(APIError(
400                           formatString("Method name '%s' conflicts with another symbol",
401                                        name)));
402     }
403     Impl::SymbolPointer sym(new SelectionParserSymbol(
404                                     new SelectionParserSymbol::Impl(
405                                             SelectionParserSymbol::MethodSymbol, name)));
406     sym->impl_->meth_ = method;
407     impl_->addSymbol(move(sym));
408 }
409
410 } // namespace gmx