Refactor for testing interactive selection input
[alexxy/gromacs.git] / src / gromacs / selection / parsetree.cpp
index 926aae201d4c1df0c3396181e2a6df3296692c81..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
  * Each element has exactly two children (one for unary negation elements),
  * which are in the order given in the input.
  */
-#include <stdio.h>
+#include "gmxpre.h"
+
+#include "parsetree.h"
+
 #include <stdarg.h>
+#include <stdio.h>
 
 #include <boost/exception_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
-#include "gromacs/selection/poscalc.h"
 #include "gromacs/selection/selection.h"
-#include "gromacs/selection/selmethod.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 "parsetree.h"
+#include "poscalc.h"
+#include "scanner.h"
 #include "selectioncollection-impl.h"
 #include "selelem.h"
+#include "selmethod.h"
 #include "symrec.h"
 
-#include "scanner.h"
-
+using gmx::SelectionLocation;
 using gmx::SelectionParserValue;
 using gmx::SelectionParserValueList;
 using gmx::SelectionParserValueListPointer;
@@ -253,36 +256,77 @@ using gmx::SelectionParserValue;
 using gmx::SelectionTreeElement;
 using gmx::SelectionTreeElementPointer;
 
-void
-_gmx_selparser_error(yyscan_t scanner, const char *fmt, ...)
+namespace
+{
+
+/*! \brief
+ * Formats context string for errors.
+ *
+ * The returned string is used as the context for errors reported during
+ * parsing.
+ */
+std::string
+formatCurrentErrorContext(yyscan_t scanner)
+{
+    return gmx::formatString(
+            "While parsing '%s'",
+            _gmx_sel_lexer_get_current_text(scanner).c_str());
+}
+
+} // namespace
+
+bool
+_gmx_selparser_handle_exception(yyscan_t scanner, std::exception *ex)
 {
-    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);
+    try
+    {
+        bool                   canContinue = false;
+        gmx::GromacsException *gromacsException
+            = dynamic_cast<gmx::GromacsException *>(ex);
+        if (gromacsException != NULL)
+        {
+            gromacsException->prependContext(formatCurrentErrorContext(scanner));
+            canContinue = (dynamic_cast<gmx::UserInputError *>(ex) != NULL);
+        }
+        _gmx_sel_lexer_set_exception(scanner, boost::current_exception());
+        return canContinue;
+    }
+    catch (const std::exception &)
+    {
+        _gmx_sel_lexer_set_exception(scanner, boost::current_exception());
+        return false;
+    }
 }
 
 bool
-_gmx_selparser_handle_exception(yyscan_t scanner, const std::exception &ex)
+_gmx_selparser_handle_error(yyscan_t scanner)
 {
-    if (dynamic_cast<const gmx::UserInputError *>(&ex) != NULL)
+    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
+    {
+        _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
+    }
+    catch (gmx::UserInputError &ex)
     {
-        // TODO: Consider whether also the non-interactive parser should
-        // postpone the exception such that the whole selection can be added as
-        // context.
-        if (_gmx_sel_is_lexer_interactive(scanner))
+        ex.prependContext(context);
+        gmx::TextWriter *statusWriter
+            = _gmx_sel_lexer_get_status_writer(scanner);
+        if (statusWriter != NULL)
         {
-            // TODO: Handle exceptions that printing the message may produce.
-            gmx::formatExceptionMessageToFile(stderr, ex);
+            gmx::formatExceptionMessageToWriter(statusWriter, ex);
             return true;
         }
+        throw;
     }
-    _gmx_sel_lexer_set_exception(scanner, boost::current_exception());
-    return false;
+    catch (gmx::GromacsException &ex)
+    {
+        ex.prependContext(context);
+        throw;
+    }
+    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
@@ -292,15 +336,16 @@ namespace gmx
  * SelectionParserValue
  */
 
-SelectionParserValue::SelectionParserValue(e_selvalue_t type)
-    : type(type)
+SelectionParserValue::SelectionParserValue(
+        e_selvalue_t type, const SelectionLocation &location)
+    : type(type), location_(location)
 {
     memset(&u, 0, sizeof(u));
 }
 
 SelectionParserValue::SelectionParserValue(
         const SelectionTreeElementPointer &expr)
-    : type(expr->v.type), expr(expr)
+    : type(expr->v.type), expr(expr), location_(expr->location())
 {
     memset(&u, 0, sizeof(u));
 }
@@ -310,9 +355,10 @@ SelectionParserValue::SelectionParserValue(
  */
 
 SelectionParserParameter::SelectionParserParameter(
-        const char                     *name,
-        SelectionParserValueListPointer values)
-    : name_(name != NULL ? name : ""),
+        const char                      *name,
+        SelectionParserValueListPointer  values,
+        const SelectionLocation         &location)
+    : name_(name != NULL ? name : ""), location_(location),
       values_(values ? move(values)
               : SelectionParserValueListPointer(new SelectionParserValueList))
 {
@@ -534,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)
     {
@@ -555,18 +599,21 @@ 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));
     }
 }
 
 gmx::SelectionTreeElementPointer
 _gmx_sel_init_arithmetic(const gmx::SelectionTreeElementPointer &left,
                          const gmx::SelectionTreeElementPointer &right,
-                         char op, yyscan_t /* scanner */)
+                         char op, yyscan_t scanner)
 {
-    SelectionTreeElementPointer sel(new SelectionTreeElement(SEL_ARITHMETIC));
+    SelectionTreeElementPointer sel(
+            new SelectionTreeElement(
+                    SEL_ARITHMETIC, _gmx_sel_lexer_get_current_location(scanner)));
     sel->v.type        = REAL_VALUE;
     switch (op)
     {
@@ -601,10 +648,9 @@ _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, "In comparison initialization");
-
-    SelectionTreeElementPointer  sel(new SelectionTreeElement(SEL_EXPRESSION));
+    SelectionTreeElementPointer  sel(
+            new SelectionTreeElement(
+                    SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_method(sel, &sm_compare, scanner);
 
     SelectionParserParameterList params;
@@ -616,14 +662,14 @@ _gmx_sel_init_comparison(const gmx::SelectionTreeElementPointer &left,
     name  = right->v.type == INT_VALUE ? "int2" : "real2";
     params.push_back(SelectionParserParameter::createFromExpression(name, right));
     // Create the parameter for the operator.
+    // TODO: Consider whether a proper location is needed.
+    SelectionLocation location(SelectionLocation::createEmpty());
     params.push_back(
             SelectionParserParameter::create(
-                    "op", SelectionParserValue::createString(cmpop)));
-    if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
-                               sel->u.expr.method->param, sel, scanner))
-    {
-        return SelectionTreeElementPointer();
-    }
+                    "op", SelectionParserValue::createString(cmpop, location),
+                    location));
+    _gmx_sel_parse_params(params, sel->u.expr.method->nparams,
+                          sel->u.expr.method->param, sel, scanner);
 
     return sel;
 }
@@ -650,11 +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);
-    char  buf[128];
-    sprintf(buf, "In keyword '%s'", method->name);
-    gmx::MessageStringContext  context(errors, buf);
-
     if (method->nparams > 0)
     {
         // TODO: Would assert be better?
@@ -662,7 +703,10 @@ init_keyword_internal(gmx_ana_selmethod_t *method,
                           "Keyword initialization called with non-keyword method"));
     }
 
-    SelectionTreeElementPointer root(new SelectionTreeElement(SEL_EXPRESSION));
+    const SelectionLocation    &location = _gmx_sel_lexer_get_current_location(scanner);
+    // TODO: If there are arguments, the location would be better as just the
+    // location of the keyword itself.
+    SelectionTreeElementPointer root(new SelectionTreeElement(SEL_EXPRESSION, location));
     SelectionTreeElementPointer child = root;
     _gmx_selelem_set_method(child, method, scanner);
 
@@ -680,7 +724,7 @@ init_keyword_internal(gmx_ana_selmethod_t *method,
                                   "Unknown type for keyword selection"));
         }
         /* Initialize the selection element */
-        root.reset(new SelectionTreeElement(SEL_EXPRESSION));
+        root.reset(new SelectionTreeElement(SEL_EXPRESSION, location));
         _gmx_selelem_set_method(root, kwmethod, scanner);
         if (method->type == STR_VALUE)
         {
@@ -689,14 +733,12 @@ init_keyword_internal(gmx_ana_selmethod_t *method,
         SelectionParserParameterList params;
         params.push_back(
                 SelectionParserParameter::createFromExpression(NULL, child));
-        params.push_back(SelectionParserParameter::create(NULL, move(args)));
-        if (!_gmx_sel_parse_params(params, root->u.expr.method->nparams,
-                                   root->u.expr.method->param, root, scanner))
-        {
-            return SelectionTreeElementPointer();
-        }
+        params.push_back(
+                SelectionParserParameter::create(NULL, move(args), location));
+        _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;
 }
@@ -744,6 +786,26 @@ _gmx_sel_init_keyword_strmatch(gmx_ana_selmethod_t *method,
     return init_keyword_internal(method, matchType, move(args), rpost, scanner);
 }
 
+/*!
+ * \param[in]  method Method to use for initialization.
+ * \param[in]  group  Selection in which the keyword should be evaluated.
+ * \param[in]  rpost  Reference position type to use (NULL = default).
+ * \param[in]  scanner Scanner data structure.
+ * \returns    The created selection element.
+ *
+ * This function handles the creation of a gmx::SelectionTreeElement object for
+ * expressions like "z of ...".
+ */
+SelectionTreeElementPointer
+_gmx_sel_init_keyword_of(gmx_ana_selmethod_t                    *method,
+                         const gmx::SelectionTreeElementPointer &group,
+                         const char *rpost, yyscan_t scanner)
+{
+    // TODO Provide an error if rpost is provided.
+    GMX_UNUSED_VALUE(rpost);
+    return _gmx_sel_init_keyword_evaluator(method, group, scanner);
+}
+
 /*!
  * \param[in]  method Method to use for initialization.
  * \param[in]  params Pointer to the first parameter.
@@ -765,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);
-    char  buf[128];
-    sprintf(buf, "In keyword '%s'", method->name);
-    gmx::MessageStringContext  context(errors, buf);
 
     _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();
-    }
-    SelectionTreeElementPointer root(new SelectionTreeElement(SEL_EXPRESSION));
+    _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;
 }
@@ -808,13 +859,10 @@ _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);
-    char  buf[128];
-    sprintf(buf, "In keyword '%s'", method->name);
-    gmx::MessageStringContext  context(errors, buf);
-
     _gmx_sel_finish_method(scanner);
-    SelectionTreeElementPointer modifier(new SelectionTreeElement(SEL_MODIFIER));
+    SelectionTreeElementPointer modifier(
+            new SelectionTreeElement(
+                    SEL_MODIFIER, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_method(modifier, method, scanner);
     SelectionTreeElementPointer root;
     if (method->type == NO_VALUE)
@@ -834,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;
 }
@@ -856,37 +901,34 @@ SelectionTreeElementPointer
 _gmx_sel_init_position(const gmx::SelectionTreeElementPointer &expr,
                        const char *type, yyscan_t scanner)
 {
-    gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
-    char  buf[128];
-    sprintf(buf, "In position evaluation");
-    gmx::MessageStringContext   context(errors, buf);
-
-    SelectionTreeElementPointer root(new SelectionTreeElement(SEL_EXPRESSION));
+    SelectionTreeElementPointer  root(
+            new SelectionTreeElement(
+                    SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_method(root, &sm_keyword_pos, scanner);
     _gmx_selelem_set_kwpos_type(root.get(), type);
     /* Create the parameters for the parameter parser. */
     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;
 }
 
 /*!
- * \param[in] x,y,z  Coordinates for the position.
+ * \param[in] x,y,z   Coordinates for the position.
+ * \param[in] scanner Scanner data structure.
  * \returns   The creates selection element.
  */
 SelectionTreeElementPointer
-_gmx_sel_init_const_position(real x, real y, real z)
+_gmx_sel_init_const_position(real x, real y, real z, yyscan_t scanner)
 {
     rvec                        pos;
 
-    SelectionTreeElementPointer sel(new SelectionTreeElement(SEL_CONST));
+    SelectionTreeElementPointer sel(
+            new SelectionTreeElement(
+                    SEL_CONST, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_vtype(sel, POS_VALUE);
     _gmx_selvalue_reserve(&sel->v, 1);
     pos[XX] = x;
@@ -908,7 +950,9 @@ SelectionTreeElementPointer
 _gmx_sel_init_group_by_name(const char *name, yyscan_t scanner)
 {
 
-    SelectionTreeElementPointer sel(new SelectionTreeElement(SEL_GROUPREF));
+    SelectionTreeElementPointer sel(
+            new SelectionTreeElement(
+                    SEL_GROUPREF, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_vtype(sel, GROUP_VALUE);
     sel->setName(gmx::formatString("group \"%s\"", name));
     sel->u.gref.name = gmx_strdup(name);
@@ -932,7 +976,9 @@ _gmx_sel_init_group_by_name(const char *name, yyscan_t scanner)
 SelectionTreeElementPointer
 _gmx_sel_init_group_by_id(int id, yyscan_t scanner)
 {
-    SelectionTreeElementPointer sel(new SelectionTreeElement(SEL_GROUPREF));
+    SelectionTreeElementPointer sel(
+            new SelectionTreeElement(
+                    SEL_GROUPREF, _gmx_sel_lexer_get_current_location(scanner)));
     _gmx_selelem_set_vtype(sel, GROUP_VALUE);
     sel->setName(gmx::formatString("group %d", id));
     sel->u.gref.name = NULL;
@@ -949,14 +995,16 @@ _gmx_sel_init_group_by_id(int id, yyscan_t scanner)
 }
 
 /*!
- * \param[in,out] sel  Value of the variable.
+ * \param[in,out] sel      Value of the variable.
+ * \param         scanner  Scanner data structure.
  * \returns       The created selection element that references \p sel.
  *
  * The reference count of \p sel is updated, but no other modifications are
  * made.
  */
 SelectionTreeElementPointer
-_gmx_sel_init_variable_ref(const gmx::SelectionTreeElementPointer &sel)
+_gmx_sel_init_variable_ref(const gmx::SelectionTreeElementPointer &sel,
+                           yyscan_t                                scanner)
 {
     SelectionTreeElementPointer ref;
 
@@ -966,7 +1014,8 @@ _gmx_sel_init_variable_ref(const gmx::SelectionTreeElementPointer &sel)
     }
     else
     {
-        ref.reset(new SelectionTreeElement(SEL_SUBEXPRREF));
+        ref.reset(new SelectionTreeElement(
+                          SEL_SUBEXPRREF, _gmx_sel_lexer_get_current_location(scanner)));
         _gmx_selelem_set_vtype(ref, sel->v.type);
         ref->setName(sel->name());
         ref->child = sel;
@@ -996,7 +1045,9 @@ _gmx_sel_init_selection(const char                             *name,
                           "Each selection must evaluate to a position"));
     }
 
-    SelectionTreeElementPointer root(new SelectionTreeElement(SEL_ROOT));
+    SelectionTreeElementPointer root(
+            new SelectionTreeElement(
+                    SEL_ROOT, _gmx_sel_lexer_get_current_location(scanner)));
     root->child = sel;
     if (name)
     {
@@ -1014,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;
@@ -1058,11 +1112,12 @@ _gmx_sel_assign_variable(const char                             *name,
     }
     else
     {
+        SelectionLocation location(_gmx_sel_lexer_get_current_location(scanner));
         /* Create the root element */
-        root.reset(new SelectionTreeElement(SEL_ROOT));
+        root.reset(new SelectionTreeElement(SEL_ROOT, location));
         root->setName(name);
         /* Create the subexpression element */
-        root->child.reset(new SelectionTreeElement(SEL_SUBEXPR));
+        root->child.reset(new SelectionTreeElement(SEL_SUBEXPR, location));
         root->child->setName(name);
         _gmx_selelem_set_vtype(root->child, expr->v.type);
         root->child->child  = expr;
@@ -1080,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;
 }