/*
* 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.
* 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/fileio/futil.h"
-#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;
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
* 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));
}
*/
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))
{
* \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)
{
}
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)
{
buf[0] = op;
buf[1] = 0;
sel->setName(buf);
- sel->u.arith.opstr = strdup(buf);
+ sel->u.arith.opstr = gmx_strdup(buf);
sel->child = left;
sel->child->next = right;
return sel;
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;
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;
}
{
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?
"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);
"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)
{
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;
}
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.
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;
}
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)
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;
}
_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;
_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 = strdup(name);
+ sel->u.gref.name = gmx_strdup(name);
sel->u.gref.id = -1;
if (_gmx_sel_lexer_has_groups_set(scanner))
{
- gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
- sel->resolveIndexGroupReference(grps);
+ gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ sel->resolveIndexGroupReference(grps, sc->gall.isize);
}
return sel;
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;
if (_gmx_sel_lexer_has_groups_set(scanner))
{
- gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
- sel->resolveIndexGroupReference(grps);
+ gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
+ gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
+ sel->resolveIndexGroupReference(grps, sc->gall.isize);
}
return sel;
}
/*!
- * \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;
}
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;
"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)
{
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;
}
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;
sc->symtab->addVariable(name, root->child);
}
srenew(sc->varstrs, sc->nvars + 1);
- sc->varstrs[sc->nvars] = strdup(pselstr);
+ 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;
}