Refactor for testing interactive selection input
[alexxy/gromacs.git] / src / gromacs / selection / parsetree.cpp
index fc80abcf7f655b469a8964e83d6207bcf1c93c0e..d1eb39fcf3081e1f5e1769c12b36677cea8d2f9e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013,2014,2015, 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.
@@ -66,8 +66,9 @@
  *    methods and initializes the children of the method element.
  *  - selectioncollection.h, selectioncollection.cpp:
  *    These files define the high-level public interface to the parser
- *    through SelectionCollection::parseFromStdin(),
- *    SelectionCollection::parseFromFile() and
+ *    through SelectionCollection::parseInteractive(),
+ *    SelectionCollection::parseFromStdin(),
+ *    SelectionCollection::parseFromFile(), and
  *    SelectionCollection::parseFromString().
  *
  * The basic control flow in the parser is as follows: when a parser function
 #include "gromacs/selection/selection.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
-#include "gromacs/utility/messagestringcollector.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
 
 #include "keywords.h"
 #include "poscalc.h"
@@ -275,19 +275,6 @@ formatCurrentErrorContext(yyscan_t scanner)
 
 } // namespace
 
-void
-_gmx_selparser_error(yyscan_t scanner, const char *fmt, ...)
-{
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    // FIXME: Use an arbitrary length buffer.
-    char    buf[1024];
-    va_list ap;
-    va_start(ap, fmt);
-    vsnprintf(buf, 1024, fmt, ap);
-    va_end(ap);
-    errors->append(buf);
-}
-
 bool
 _gmx_selparser_handle_exception(yyscan_t scanner, std::exception *ex)
 {
@@ -314,7 +301,7 @@ _gmx_selparser_handle_exception(yyscan_t scanner, std::exception *ex)
 bool
 _gmx_selparser_handle_error(yyscan_t scanner)
 {
-    std::string context(gmx::formatString("In selection '%s'",
+    std::string context(gmx::formatString("Invalid selection '%s'",
                                           _gmx_sel_lexer_pselstr(scanner)));
     // The only way to prepend context to the exception is to rethrow it.
     try
@@ -324,9 +311,11 @@ _gmx_selparser_handle_error(yyscan_t scanner)
     catch (gmx::UserInputError &ex)
     {
         ex.prependContext(context);
-        if (_gmx_sel_is_lexer_interactive(scanner))
+        gmx::TextWriter *statusWriter
+            = _gmx_sel_lexer_get_status_writer(scanner);
+        if (statusWriter != NULL)
         {
-            gmx::formatExceptionMessageToFile(stderr, ex);
+            gmx::formatExceptionMessageToWriter(statusWriter, ex);
             return true;
         }
         throw;
@@ -336,11 +325,8 @@ _gmx_selparser_handle_error(yyscan_t scanner)
         ex.prependContext(context);
         throw;
     }
-    // Do legacy processing for non-exception cases.
-    // This is only reached if there was no active exception.
-    _gmx_selparser_error(scanner, "Invalid selection '%s'",
-                         _gmx_sel_lexer_pselstr(scanner));
-    return _gmx_sel_is_lexer_interactive(scanner);
+    GMX_RELEASE_ASSERT(false, "All parsing errors should result in a captured exception");
+    return false; // Some compilers will not believe that the above never returns.
 }
 
 namespace gmx
@@ -594,13 +580,11 @@ _gmx_selelem_set_method(const gmx::SelectionTreeElementPointer &sel,
  * \param[in,out] pcc    Position calculation collection to use.
  * \param[in,out] sel    Selection element to initialize.
  * \param[in]     rpost  Reference position type to use (NULL = default).
- * \param[in]     scanner Scanner data structure.
- * \returns       0 on success, a non-zero error code on error.
  */
 static void
 set_refpos_type(gmx::PositionCalculationCollection *pcc,
-                const SelectionTreeElementPointer &sel,
-                const char *rpost, yyscan_t scanner)
+                const SelectionTreeElementPointer  &sel,
+                const char                         *rpost)
 {
     if (!rpost)
     {
@@ -615,9 +599,10 @@ set_refpos_type(gmx::PositionCalculationCollection *pcc,
     }
     else
     {
-        // TODO: Should this be treated as a real error?
-        _gmx_selparser_error(scanner, "modifier '%s' is not applicable for '%s'",
-                             rpost, sel->u.expr.method->name);
+        std::string message
+            = gmx::formatString("Position modifiers ('%s') is not applicable for '%s'",
+                                rpost, sel->u.expr.method->name);
+        GMX_THROW(gmx::InvalidInputError(message));
     }
 }
 
@@ -663,9 +648,6 @@ _gmx_sel_init_comparison(const gmx::SelectionTreeElementPointer &left,
                          const gmx::SelectionTreeElementPointer &right,
                          const char *cmpop, yyscan_t scanner)
 {
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
-
     SelectionTreeElementPointer  sel(
             new SelectionTreeElement(
                     SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
@@ -686,11 +668,8 @@ _gmx_sel_init_comparison(const gmx::SelectionTreeElementPointer &left,
             SelectionParserParameter::create(
                     "op", SelectionParserValue::createString(cmpop, location),
                     location));
-    if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
-                               sel->u.expr.method->param, sel, scanner))
-    {
-        return SelectionTreeElementPointer();
-    }
+    _gmx_sel_parse_params(params, sel->u.expr.method->nparams,
+                          sel->u.expr.method->param, sel, scanner);
 
     return sel;
 }
@@ -717,9 +696,6 @@ init_keyword_internal(gmx_ana_selmethod_t *method,
 {
     gmx_ana_selcollection_t     *sc = _gmx_sel_lexer_selcollection(scanner);
 
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
-
     if (method->nparams > 0)
     {
         // TODO: Would assert be better?
@@ -759,13 +735,10 @@ init_keyword_internal(gmx_ana_selmethod_t *method,
                 SelectionParserParameter::createFromExpression(NULL, child));
         params.push_back(
                 SelectionParserParameter::create(NULL, move(args), location));
-        if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
-                                   root->u.expr.method->param, root, scanner))
-        {
-            return SelectionTreeElementPointer();
-        }
+        _gmx_sel_parse_params(params, root->u.expr.method->nparams,
+                              root->u.expr.method->param, root, scanner);
     }
-    set_refpos_type(&sc->pcc, child, rpost, scanner);
+    set_refpos_type(&sc->pcc, child, rpost);
 
     return root;
 }
@@ -828,9 +801,7 @@ _gmx_sel_init_keyword_of(gmx_ana_selmethod_t                    *method,
                          const gmx::SelectionTreeElementPointer &group,
                          const char *rpost, yyscan_t scanner)
 {
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
-
+    // TODO Provide an error if rpost is provided.
     GMX_UNUSED_VALUE(rpost);
     return _gmx_sel_init_keyword_evaluator(method, group, scanner);
 }
@@ -856,29 +827,18 @@ _gmx_sel_init_method(gmx_ana_selmethod_t                      *method,
                      const char *rpost, yyscan_t scanner)
 {
     gmx_ana_selcollection_t     *sc = _gmx_sel_lexer_selcollection(scanner);
-    int                          rc;
-
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
 
     _gmx_sel_finish_method(scanner);
     /* The "same" keyword needs some custom massaging of the parameters. */
-    rc = _gmx_selelem_custom_init_same(&method, params, scanner);
-    if (rc != 0)
-    {
-        return SelectionTreeElementPointer();
-    }
+    _gmx_selelem_custom_init_same(&method, params, scanner);
     SelectionTreeElementPointer root(
             new SelectionTreeElement(
                     SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_method(root, method, scanner);
     /* Process the parameters */
-    if (!_gmx_sel_parse_params(*params, root->u.expr.method->nparams,
-                               root->u.expr.method->param, root, scanner))
-    {
-        return SelectionTreeElementPointer();
-    }
-    set_refpos_type(&sc->pcc, root, rpost, scanner);
+    _gmx_sel_parse_params(*params, root->u.expr.method->nparams,
+                          root->u.expr.method->param, root, scanner);
+    set_refpos_type(&sc->pcc, root, rpost);
 
     return root;
 }
@@ -899,9 +859,6 @@ _gmx_sel_init_modifier(gmx_ana_selmethod_t                      *method,
                        const gmx::SelectionTreeElementPointer   &sel,
                        yyscan_t                                  scanner)
 {
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
-
     _gmx_sel_finish_method(scanner);
     SelectionTreeElementPointer modifier(
             new SelectionTreeElement(
@@ -925,11 +882,8 @@ _gmx_sel_init_modifier(gmx_ana_selmethod_t                      *method,
         root = modifier;
     }
     /* Process the parameters */
-    if (!_gmx_sel_parse_params(*params, modifier->u.expr.method->nparams,
-                               modifier->u.expr.method->param, modifier, scanner))
-    {
-        return SelectionTreeElementPointer();
-    }
+    _gmx_sel_parse_params(*params, modifier->u.expr.method->nparams,
+                          modifier->u.expr.method->param, modifier, scanner);
 
     return root;
 }
@@ -947,9 +901,6 @@ SelectionTreeElementPointer
 _gmx_sel_init_position(const gmx::SelectionTreeElementPointer &expr,
                        const char *type, yyscan_t scanner)
 {
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    gmx::MessageStringContext    context(errors, formatCurrentErrorContext(scanner));
-
     SelectionTreeElementPointer  root(
             new SelectionTreeElement(
                     SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
@@ -959,11 +910,8 @@ _gmx_sel_init_position(const gmx::SelectionTreeElementPointer &expr,
     SelectionParserParameterList params;
     params.push_back(SelectionParserParameter::createFromExpression(NULL, expr));
     /* Parse the parameters. */
-    if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
-                               root->u.expr.method->param, root, scanner))
-    {
-        return SelectionTreeElementPointer();
-    }
+    _gmx_sel_parse_params(params, root->u.expr.method->nparams,
+                          root->u.expr.method->param, root, scanner);
 
     return root;
 }
@@ -1117,10 +1065,13 @@ _gmx_sel_init_selection(const char                             *name,
     root->fillNameIfMissing(_gmx_sel_lexer_pselstr(scanner));
 
     /* Print out some information if the parser is interactive */
-    if (_gmx_sel_is_lexer_interactive(scanner))
+    gmx::TextWriter *statusWriter = _gmx_sel_lexer_get_status_writer(scanner);
+    if (statusWriter != NULL)
     {
-        fprintf(stderr, "Selection '%s' parsed\n",
-                _gmx_sel_lexer_pselstr(scanner));
+        const std::string message
+            = gmx::formatString("Selection '%s' parsed",
+                                _gmx_sel_lexer_pselstr(scanner));
+        statusWriter->writeLine(message);
     }
 
     return root;
@@ -1184,9 +1135,12 @@ _gmx_sel_assign_variable(const char                             *name,
     srenew(sc->varstrs, sc->nvars + 1);
     sc->varstrs[sc->nvars] = gmx_strdup(pselstr);
     ++sc->nvars;
-    if (_gmx_sel_is_lexer_interactive(scanner))
+    gmx::TextWriter *statusWriter = _gmx_sel_lexer_get_status_writer(scanner);
+    if (statusWriter != NULL)
     {
-        fprintf(stderr, "Variable '%s' parsed\n", pselstr);
+        const std::string message
+            = gmx::formatString("Variable '%s' parsed", pselstr);
+        statusWriter->writeLine(message);
     }
     return root;
 }