Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / selection / symrec.cpp
index 4fba00d570f79b3300263d0eb0c62cde40df6d0c..cd648f60efc0261e932ed93050c6a9b20a44774d 100644 (file)
 /*
+ * This file is part of the GROMACS molecular simulation package.
  *
- *                This source code is part of
+ * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
  *
- *                 G   R   O   M   A   C   S
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
  *
- *          GROningen MAchine for Chemical Simulations
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2009, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
  *
- * If you want to redistribute modifications, please consider that
- * scientific software is very special. Version control is crucial -
- * bugs must be traceable. We will be happy to consider code for
- * inclusion in the official distribution, but derived work must not
- * be called official GROMACS. Details are found in the README & COPYING
- * files - if they are missing, get the official version at www.gromacs.org.
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
  *
  * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- *
- * For more info, check our website at http://www.gromacs.org
+ * the research papers on the package. Check out http://www.gromacs.org.
  */
 /*! \internal \file
  * \brief
- * Implements functions in symrec.h.
+ * Implements classes in symrec.h.
  *
- * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_selection
  */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "gmxpre.h"
+
+#include "symrec.h"
 
-#include <macros.h>
-#include <smalloc.h>
-#include <string2.h>
-#include <typedefs.h>
-#include <gmx_fatal.h>
+#include <map>
+#include <string>
+#include <utility>
 
-#include "gromacs/selection/poscalc.h"
+#include "gromacs/legacyheaders/macros.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/uniqueptr.h"
 
+#include "poscalc.h"
 #include "selelem.h"
-#include "symrec.h"
 
-/*! \internal \brief
- * Symbol table for the selection parser.
- */
-struct gmx_sel_symtab_t
+namespace gmx
 {
-    /** Pointer to the first symbol in the linked list of symbols. */
-    gmx_sel_symrec_t *first;
-};
+
+/********************************************************************
+ * SelectionParserSymbol
+ */
 
 /*! \internal \brief
- * Single symbol for the selection parser.
+ * Private implementation class for SelectionParserSymbol.
+ *
+ * \ingroup module_selection
  */
-struct gmx_sel_symrec_t
+class SelectionParserSymbol::Impl
 {
-    /** Name of the symbol. */
-    char                           *name;
-    /** Type of the symbol. */
-    e_symbol_t                      type;
-    /** Value of the symbol. */
-    union {
-        /** Pointer to the method structure (\ref SYMBOL_METHOD). */
-        struct gmx_ana_selmethod_t *meth;
-        /** Pointer to the variable value (\ref SYMBOL_VARIABLE). */
-        struct t_selelem           *var;
-    }                               u;
-    /** Pointer to the next symbol. */
-    struct gmx_sel_symrec_t        *next;
-};
+    public:
+        /*! \brief
+         * Initializes a symbol.
+         *
+         * \param[in] type  Type for the symbol.
+         * \param[in] name  Name for the symbol.
+         *
+         * The symbol table is responsible for initializing the \a meth_ and
+         * \a var_ members as appropriate.
+         */
+        Impl(SymbolType type, const char *name)
+            : name_(name), type_(type), meth_(NULL)
+        {
+        }
 
-/** List of reserved symbols to register in add_reserved_symbols(). */
-static const char *const sym_reserved[] = {
-    "group",
-    "to",
-    "not",
-    "and",
-    "or",
-    "xor",
-    "yes",
-    "no",
-    "on",
-    "off",
-    "help",
+        //! Name of the symbol.
+        std::string                     name_;
+        //! Type of the symbol.
+        SymbolType                      type_;
+        //! Pointer to the method structure (\ref MethodSymbol).
+        gmx_ana_selmethod_t            *meth_;
+        //! Pointer to the variable value (\ref VariableSymbol).
+        SelectionTreeElementPointer     var_;
 };
 
-/*!
- * \param[in] sym Symbol to query.
- * \returns   The name of \p sym.
- *
- * The returned pointer should not be free'd.
- */
-char *
-_gmx_sel_sym_name(gmx_sel_symrec_t *sym)
+SelectionParserSymbol::SelectionParserSymbol(Impl *impl)
+    : impl_(impl)
 {
-    return sym->name;
 }
 
-/*!
- * \param[in] sym Symbol to query.
- * \returns   The type of \p sym.
- */
-e_symbol_t
-_gmx_sel_sym_type(gmx_sel_symrec_t *sym)
+SelectionParserSymbol::~SelectionParserSymbol()
 {
-    return sym->type;
 }
 
-/*!
- * \param[in] sym Symbol to query.
- * \returns   The method associated with \p sym, or NULL if \p sym is not a
- *   \ref SYMBOL_METHOD symbol.
- */
-struct gmx_ana_selmethod_t *
-_gmx_sel_sym_value_method(gmx_sel_symrec_t *sym)
+const std::string &
+SelectionParserSymbol::name() const
 {
-    if (sym->type != SYMBOL_METHOD)
-    {
-        gmx_call("symbol is not a method symbol");
-        return NULL;
-    }
-    return sym->u.meth;
+    return impl_->name_;
 }
 
-/*!
- * \param[in] sym Symbol to query.
- * \returns   The variable expression associated with \p sym, or NULL if
- *   \p sym is not a \ref SYMBOL_VARIABLE symbol.
- */
-struct t_selelem *
-_gmx_sel_sym_value_var(gmx_sel_symrec_t *sym)
+SelectionParserSymbol::SymbolType
+SelectionParserSymbol::type() const
 {
-    if (sym->type != SYMBOL_VARIABLE)
-    {
-        gmx_call("symbol is not a variable symbol");
-        return NULL;
-    }
-    return sym->u.var;
+    return impl_->type_;
 }
 
-/*! \brief
- * Adds the reserved symbols to a symbol table.
- * 
- * \param[in,out] tab  Symbol table to which the symbols are added.
+gmx_ana_selmethod_t *
+SelectionParserSymbol::methodValue() const
+{
+    GMX_RELEASE_ASSERT(type() == MethodSymbol,
+                       "Attempting to get method handle for a non-method symbol");
+    return impl_->meth_;
+}
+
+const gmx::SelectionTreeElementPointer &
+SelectionParserSymbol::variableValue() const
+{
+    GMX_RELEASE_ASSERT(type() == VariableSymbol,
+                       "Attempting to get variable value for a non-variable symbol");
+    return impl_->var_;
+}
+
+/********************************************************************
+ * SelectionParserSymbolTable::Impl
+ */
+
+/*! \internal
+ * \brief
+ * Private implementation class for SelectionParserSymbolTable.
+ *
+ * All methods in this class may throw std::bad_alloc if out of memory.
  *
- * Assumes that the symbol table is empty.
+ * \ingroup module_selection
  */
-static void
-add_reserved_symbols(gmx_sel_symtab_t *tab)
+class SelectionParserSymbolTable::Impl
 {
-    gmx_sel_symrec_t *sym;
-    gmx_sel_symrec_t *last;
-    size_t            i;
+    public:
+        //! Smart pointer type for managing a SelectionParserSymbol.
+        typedef gmx::gmx_unique_ptr<SelectionParserSymbol>::type
+            SymbolPointer;
+        //! Container type for the list of symbols.
+        typedef std::map<std::string, SymbolPointer> SymbolMap;
+
+        /*! \brief
+         * Adds a symbol to the symbol list.
+         *
+         * \param[in] symbol  Symbol to add.
+         */
+        void addSymbol(SymbolPointer symbol);
+        //! Adds the reserved symbols to this symbol table.
+        void addReservedSymbols();
+        //! Adds the position symbols to this symbol table.
+        void addPositionSymbols();
+
+        //! Symbols in this symbol table.
+        SymbolMap               symbols_;
+};
 
-    last = NULL;
-    for (i = 0; i < asize(sym_reserved); ++i)
+void
+SelectionParserSymbolTable::Impl::addSymbol(SymbolPointer symbol)
+{
+    symbols_.insert(std::make_pair(symbol->name(), move(symbol)));
+}
+
+void
+SelectionParserSymbolTable::Impl::addReservedSymbols()
+{
+    const char *const sym_reserved[] = {
+        "group",
+        "to",
+        "not",
+        "and",
+        "or",
+        "xor",
+        "yes",
+        "no",
+        "on",
+        "off"
+    };
+
+    for (size_t i = 0; i < asize(sym_reserved); ++i)
     {
-        snew(sym, 1);
-        sym->name = strdup(sym_reserved[i]);
-        sym->type = SYMBOL_RESERVED;
-        sym->next = NULL;
-        if (last)
-        {
-            last->next = sym;
-        }
-        else
-        {
-            tab->first = sym;
-        }
-        last = sym;
+        SymbolPointer sym(new SelectionParserSymbol(
+                                  new SelectionParserSymbol::Impl(
+                                          SelectionParserSymbol::ReservedSymbol, sym_reserved[i])));
+        addSymbol(move(sym));
     }
 }
 
-/*! \brief
- * Adds the position symbols to the symbol list.
- * 
- * \param[in,out] tab  Symbol table to which the symbols are added.
- */
-static void
-add_position_symbols(gmx_sel_symtab_t *tab)
+void
+SelectionParserSymbolTable::Impl::addPositionSymbols()
 {
-    gmx_sel_symrec_t  *sym;
-    gmx_sel_symrec_t  *last;
-    int                i;
-
     const char *const *postypes
         = gmx::PositionCalculationCollection::typeEnumValues;
-    last = tab->first;
-    while (last && last->next)
+    for (int i = 0; postypes[i] != NULL; ++i)
     {
-        last = last->next;
+        SymbolPointer sym(new SelectionParserSymbol(
+                                  new SelectionParserSymbol::Impl(
+                                          SelectionParserSymbol::PositionSymbol, postypes[i])));
+        addSymbol(move(sym));
     }
-    for (i = 0; postypes[i] != NULL; ++i)
-    {
-        snew(sym, 1);
-        sym->name = strdup(postypes[i]);
-        sym->type = SYMBOL_POS;
-        sym->next = NULL;
-        if (last)
+}
+
+/********************************************************************
+ * SelectionParserSymbolIterator
+ */
+
+/*! \internal \brief
+ * Private implementation class for SelectionParserSymbolIterator.
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserSymbolIterator::Impl
+{
+    public:
+        //! Shorthand for the underlying iterator type.
+        typedef SelectionParserSymbolTable::Impl::SymbolMap::const_iterator
+            IteratorType;
+
+        /*! \brief
+         * Constructs an end iterator.
+         *
+         * \param[in] end  Iterator to the end of the iterated container.
+         */
+        explicit Impl(IteratorType end)
+            : iter_(end), end_(end)
         {
-            last->next = sym;
         }
-        else
+        /*! \brief
+         * Constructs an iterator.
+         *
+         * \param[in] iter Iterator to the current symbol.
+         * \param[in] end  Iterator to the end of the iterated container.
+         */
+        Impl(IteratorType iter, IteratorType end)
+            : iter_(iter), end_(end)
         {
-            tab->first = sym;
         }
-        last = sym;
-    }
-}
 
-/*!
- * \param[out] tabp Symbol table pointer to initialize.
- *
- * Reserved and position symbols are added to the created table.
- */
-int
-_gmx_sel_symtab_create(gmx_sel_symtab_t **tabp)
+        //! Underlying iterator to the symbol container.
+        IteratorType            iter_;
+        //! End of the symbol container being iterated.
+        IteratorType            end_;
+};
+
+SelectionParserSymbolIterator::SelectionParserSymbolIterator(Impl *impl)
+    : impl_(impl)
 {
-    gmx_sel_symtab_t *tab;
+}
 
-    snew(tab, 1);
-    add_reserved_symbols(tab);
-    add_position_symbols(tab);
-    *tabp = tab;
-    return 0;
+SelectionParserSymbolIterator::SelectionParserSymbolIterator(
+        const SelectionParserSymbolIterator &other)
+    : impl_(new Impl(*other.impl_))
+{
 }
 
-/*!
- * \param[in] tab Symbol table to free.
- *
- * The pointer \p tab is invalid after the call.
- */
-void
-_gmx_sel_symtab_free(gmx_sel_symtab_t *tab)
+SelectionParserSymbolIterator::~SelectionParserSymbolIterator()
 {
-    gmx_sel_symrec_t *sym;
+}
 
-    while (tab->first)
-    {
-        sym = tab->first;
-        tab->first = sym->next;
-        if (sym->type == SYMBOL_VARIABLE)
-        {
-            _gmx_selelem_free(sym->u.var);
-        }
-        sfree(sym->name);
-        sfree(sym);
-    }
-    sfree(tab);
+SelectionParserSymbolIterator &SelectionParserSymbolIterator::operator=(
+        const SelectionParserSymbolIterator &other)
+{
+    impl_.reset(new Impl(*other.impl_));
+    return *this;
 }
 
-/*!
- * \param[in] tab    Symbol table to search.
- * \param[in] name   Symbol name to find.
- * \param[in] bExact If false, symbols that begin with \p name are also
- *   considered.
- * \returns   Pointer to the symbol with name \p name, or NULL if not found.
- *
- * If no exact match is found and \p bExact is false, returns a symbol that
- * begins with \p name if a unique matching symbol is found.
- */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol(gmx_sel_symtab_t *tab, const char *name, bool bExact)
+bool SelectionParserSymbolIterator::operator==(
+        const SelectionParserSymbolIterator &other) const
 {
-    return _gmx_sel_find_symbol_len(tab, name, strlen(name), bExact);
+    return impl_->iter_ == other.impl_->iter_;
 }
 
-/*!
- * \param[in] tab    Symbol table to search.
- * \param[in] name   Symbol name to find.
- * \param[in] len    Only consider the first \p len characters of \p name.
- * \param[in] bExact If false, symbols that begin with \p name are also
- *   considered.
- * \returns   Pointer to the symbol with name \p name, or NULL if not found.
- *
- * If no exact match is found and \p bExact is false, returns a symbol that
- * begins with \p name if a unique matching symbol is found.
- *
- * The parameter \p len is there to allow using this function from scanner.l
- * without modifying the text to be scanned or copying it.
- */
-gmx_sel_symrec_t *
-_gmx_sel_find_symbol_len(gmx_sel_symtab_t *tab, const char *name, size_t len,
-                         bool bExact)
+const SelectionParserSymbol &SelectionParserSymbolIterator::operator*() const
 {
-    gmx_sel_symrec_t *sym;
-    gmx_sel_symrec_t *match;
-    bool              bUnique;
-    bool              bMatch;
-
-    match = NULL;
-    bUnique = true;
-    bMatch  = false;
-    sym = tab->first;
-    while (sym)
-    {
-        if (!strncmp(sym->name, name, len))
-        {
-            if (strlen(sym->name) == len)
-            {
-                return sym;
-            }
-            if (bMatch)
-            {
-                bUnique = false;
-            }
-            bMatch = true;
-            if (sym->type == SYMBOL_METHOD)
-            {
-                match = sym;
-            }
-        }
-        sym = sym->next;
-    }
-    if (bExact)
-    {
-        return NULL;
-    }
+    return *impl_->iter_->second;
+}
 
-    if (!bUnique)
+SelectionParserSymbolIterator &SelectionParserSymbolIterator::operator++()
+{
+    SelectionParserSymbol::SymbolType type = impl_->iter_->second->type();
+    do
     {
-        fprintf(stderr, "parse error: ambiguous symbol\n");
-        return NULL;
+        ++impl_->iter_;
     }
-    return match;
+    while (impl_->iter_ != impl_->end_ && impl_->iter_->second->type() != type);
+    return *this;
 }
 
-/*!
- * \param[in] tab   Symbol table to search.
- * \param[in] type  Type of symbol to find.
- * \returns   The first symbol in \p tab with type \p type,
- *   or NULL if there are no such symbols.
+/********************************************************************
+ * SelectionParserSymbolTable
  */
-gmx_sel_symrec_t *
-_gmx_sel_first_symbol(gmx_sel_symtab_t *tab, e_symbol_t type)
-{
-    gmx_sel_symrec_t *sym;
 
-    sym = tab->first;
-    while (sym)
-    {
-        if (sym->type == type)
-        {
-            return sym;
-        }
-        sym = sym->next;
-    }
-    return NULL;
+SelectionParserSymbolTable::SelectionParserSymbolTable()
+    : impl_(new Impl)
+{
+    impl_->addReservedSymbols();
+    impl_->addPositionSymbols();
 }
 
-/*!
- * \param[in] after Start the search after this symbol.
- * \param[in] type  Type of symbol to find.
- * \returns   The next symbol after \p after with type \p type,
- *   or NULL if there are no more symbols.
- */
-gmx_sel_symrec_t *
-_gmx_sel_next_symbol(gmx_sel_symrec_t *after, e_symbol_t type)
+SelectionParserSymbolTable::~SelectionParserSymbolTable()
 {
-    gmx_sel_symrec_t *sym;
+}
 
-    sym = after->next;
-    while (sym)
+const SelectionParserSymbol *
+SelectionParserSymbolTable::findSymbol(const std::string &name,
+                                       bool               bExact) const
+{
+    Impl::SymbolMap::const_iterator sym = impl_->symbols_.lower_bound(name);
+    if (sym == impl_->symbols_.end())
+    {
+        return NULL;
+    }
+    if (sym->second->name() == name)
     {
-        if (sym->type == type)
+        return sym->second.get();
+    }
+    if (!bExact && startsWith(sym->second->name(), name))
+    {
+        Impl::SymbolMap::const_iterator next = sym;
+        ++next;
+        if (next != impl_->symbols_.end()
+            && startsWith(next->second->name(), name))
+        {
+            GMX_THROW(InvalidInputError("'" + name + "' is ambiguous"));
+        }
+        if (sym->second->type() == SelectionParserSymbol::MethodSymbol)
         {
-            return sym;
+            return sym->second.get();
         }
-        sym = sym->next;
     }
     return NULL;
 }
 
-/*! \brief
- * Internal utility function used in adding symbols to a symbol table.
- *
- * \param[in,out] tab   Symbol table to add the symbol to.
- * \param[in]     name  Name of the symbol to add.
- * \param[out]    ctype On error, the type of the conflicting symbol is
- *   written to \p *ctype.
- * \returns       Pointer to the new symbol record, or NULL if \p name
- *   conflicts with an existing symbol.
- */
-static gmx_sel_symrec_t *
-add_symbol(gmx_sel_symtab_t *tab, const char *name, e_symbol_t *ctype)
+SelectionParserSymbolIterator
+SelectionParserSymbolTable::beginIterator(SelectionParserSymbol::SymbolType type) const
 {
-    gmx_sel_symrec_t *sym, *psym;
-
-    /* Check if there is a conflicting symbol */
-    psym = NULL;
-    sym  = tab->first;
-    while (sym)
+    Impl::SymbolMap::const_iterator sym;
+    Impl::SymbolMap::const_iterator end = impl_->symbols_.end();
+    for (sym = impl_->symbols_.begin(); sym != end; ++sym)
     {
-        if (!gmx_strcasecmp(sym->name, name))
+        if (sym->second->type() == type)
         {
-            *ctype = sym->type;
-            return NULL;
+            return SelectionParserSymbolIterator(
+                    new SelectionParserSymbolIterator::Impl(sym, end));
         }
-        psym = sym;
-        sym  = sym->next;
-    }
-
-    /* Create a new symbol record */
-    if (psym == NULL)
-    {
-        snew(tab->first, 1);
-        sym = tab->first;
-    }
-    else
-    {
-        snew(psym->next, 1);
-        sym = psym->next;
     }
-    sym->name = strdup(name);
-    return sym;
+    return endIterator();
 }
 
-/*!
- * \param[in,out] tab    Symbol table to add the symbol to.
- * \param[in]     name   Name of the new symbol.
- * \param[in]     sel    Value of the variable.
- * \returns       Pointer to the created symbol record, or NULL if there was a
- *   symbol with the same name.
- */
-gmx_sel_symrec_t *
-_gmx_sel_add_var_symbol(gmx_sel_symtab_t *tab, const char *name,
-                        struct t_selelem *sel)
+SelectionParserSymbolIterator
+SelectionParserSymbolTable::endIterator() const
 {
-    gmx_sel_symrec_t *sym;
-    e_symbol_t        ctype;
+    return SelectionParserSymbolIterator(
+            new SelectionParserSymbolIterator::Impl(impl_->symbols_.end()));
+}
 
-    sym = add_symbol(tab, name, &ctype);
-    if (!sym)
+void
+SelectionParserSymbolTable::addVariable(const char                             *name,
+                                        const gmx::SelectionTreeElementPointer &sel)
+{
+    // In the current parser implementation, a syntax error is produced before
+    // this point is reached, but the check is here for robustness.
+    Impl::SymbolMap::const_iterator other = impl_->symbols_.find(name);
+    if (other != impl_->symbols_.end())
     {
-        fprintf(stderr, "parse error: ");
-        switch (ctype)
+        if (other->second->type() == SelectionParserSymbol::VariableSymbol)
         {
-            case SYMBOL_RESERVED:
-            case SYMBOL_POS:
-                fprintf(stderr, "variable name (%s) conflicts with a reserved keyword\n",
-                        name);
-                break;
-            case SYMBOL_VARIABLE:
-                fprintf(stderr, "duplicate variable name (%s)\n", name);
-                break;
-            case SYMBOL_METHOD:
-                fprintf(stderr, "variable name (%s) conflicts with a selection keyword\n",
-                        name);
-                break;
+            GMX_THROW(InvalidInputError(
+                              formatString("Reassigning variable '%s' is not supported",
+                                           name)));
+        }
+        else
+        {
+            GMX_THROW(InvalidInputError(
+                              formatString("Variable name '%s' conflicts with a reserved keyword",
+                                           name)));
         }
-        return NULL;
     }
-
-    sym->type  = SYMBOL_VARIABLE;
-    sym->u.var = sel;
-    sel->refcount++;
-    return sym;
+    Impl::SymbolPointer sym(new SelectionParserSymbol(
+                                    new SelectionParserSymbol::Impl(
+                                            SelectionParserSymbol::VariableSymbol, name)));
+    sym->impl_->var_ = sel;
+    impl_->addSymbol(move(sym));
 }
 
-/*!
- * \param[in,out] tab    Symbol table to add the symbol to.
- * \param[in]     name   Name of the new symbol.
- * \param[in]     method Method that this symbol represents.
- * \returns       Pointer to the created symbol record, or NULL if there was a
- *   symbol with the same name.
- */
-gmx_sel_symrec_t *
-_gmx_sel_add_method_symbol(gmx_sel_symtab_t *tab, const char *name,
-                           struct gmx_ana_selmethod_t *method)
+void
+SelectionParserSymbolTable::addMethod(const char          *name,
+                                      gmx_ana_selmethod_t *method)
 {
-    gmx_sel_symrec_t *sym;
-    e_symbol_t        ctype;
-
-    sym = add_symbol(tab, name, &ctype);
-    if (!sym)
+    if (impl_->symbols_.find(name) != impl_->symbols_.end())
     {
-        fprintf(stderr, "parse error: ");
-        switch (ctype)
-        {
-            case SYMBOL_RESERVED:
-            case SYMBOL_POS:
-                fprintf(stderr, "method name (%s) conflicts with a reserved keyword\n",
-                        name);
-                break;
-            case SYMBOL_VARIABLE:
-                fprintf(stderr, "method name (%s) conflicts with a variable name\n",
-                        name);
-                break;
-            case SYMBOL_METHOD:
-                fprintf(stderr, "duplicate method name (%s)\n", name);
-                break;
-        }
-        return NULL;
+        GMX_THROW(APIError(
+                          formatString("Method name '%s' conflicts with another symbol",
+                                       name)));
     }
-
-    sym->type   = SYMBOL_METHOD;
-    sym->u.meth = method;
-    return sym;
+    Impl::SymbolPointer sym(new SelectionParserSymbol(
+                                    new SelectionParserSymbol::Impl(
+                                            SelectionParserSymbol::MethodSymbol, name)));
+    sym->impl_->meth_ = method;
+    impl_->addSymbol(move(sym));
 }
+
+} // namespace gmx