Commit f99c4758 authored by Roman Tolchenov's avatar Roman Tolchenov
Browse files

Re #17176. Fix Expression to read bracketed tie correctly.

parent 8cdbfe0e
......@@ -50,6 +50,14 @@ Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class MANTID_API_DLL Expression {
public:
/// Specialised exception for parsing errors
class ParsingError: public std::runtime_error {
public:
ParsingError(const std::string& msg, const std::string& expr, size_t i);
ParsingError(const std::string& msg);
};
/// Default contructor
Expression();
/// contructor
......
......@@ -14,6 +14,55 @@ typedef Mantid::Kernel::StringTokenizer tokenizer;
const std::string DEFAULT_OPS_STR[] = {";", ",", "=", "== != > < <= >=",
"&& || ^^", "+ -", "* /", "^"};
namespace {
/// Make the full text of the error message
/// @param msg :: The text of the error message.
/// @param expr :: An expression string that caused the error.
/// @param i :: An index of a symbol in expr that may help identify the location
/// of the error.
std::string makeErrorMessage(const std::string &msg, const std::string &expr,
size_t i) {
const size_t MAX_LEFT_SIZE = 10;
const size_t MAX_RIGHT_SIZE = 10;
std::ostringstream res;
res << msg << " at\n\n";
size_t j = i;
size_t skip = 0;
size_t n = expr.size();
std::string leftEllipsis = "";
if (i > MAX_LEFT_SIZE) {
skip = i - MAX_LEFT_SIZE;
leftEllipsis = "...";
j = MAX_LEFT_SIZE + leftEllipsis.size();
n -= skip;
}
std::string rightEllipsis = "";
if (n - j > MAX_RIGHT_SIZE) {
n = i + MAX_RIGHT_SIZE;
rightEllipsis = "...";
}
// Write a substring of expr around the error indicator at symbol #i.
res << leftEllipsis << expr.substr(skip, n) << rightEllipsis << '\n';
res << std::string(j, ' ') << '^' << '\n';
return res.str();
}
} // namespace
/// Constructor
/// @param msg :: The text of the error message.
/// @param expr :: An expression string that caused the error.
/// @param i :: An index of a symbol in expr that may help identify the location
/// of the error.
Expression::ParsingError::ParsingError(const std::string &msg,
const std::string &expr, size_t i)
: std::runtime_error(makeErrorMessage(msg, expr, i)) {}
/// Constructor
/// @param msg :: The text of the error message.
Expression::ParsingError::ParsingError(const std::string &msg)
: std::runtime_error(msg) {}
Expression::Expression() {
m_operators.reset(new Operators());
......@@ -217,10 +266,7 @@ void Expression::tokenize() {
m_expr.resize(last);
break;
} else {
throw std::runtime_error(
"Syntax error in expression.\n\nA binary "
"operator isn't followed by a value:\n " +
m_expr);
throw ParsingError("A binary operator isn't followed by a value", m_expr, i);
}
}
......@@ -231,7 +277,7 @@ void Expression::tokenize() {
}
if (is1 > last) {
throw std::runtime_error("Expression: syntax error");
throw ParsingError("Syntax error", m_expr, last);
}
std::string op = m_expr.substr(i, is1 - i);
......@@ -254,8 +300,7 @@ void Expression::tokenize() {
if (is_op_symbol(m_expr[is1 + 1])) {
uop += m_expr[is1 + 1];
if (is1 + 2 > last) {
mess << "Expression: syntax error at " << is1 + 1;
throw std::runtime_error(mess.str());
throw ParsingError("Syntax error", m_expr, is1 + 1);
}
}
if (is_unary(uop)) {
......@@ -274,8 +319,7 @@ void Expression::tokenize() {
error = false;
}
if (error) {
mess << "Expression: unrecognized operator " << op;
throw std::runtime_error(mess.str());
throw ParsingError("Unrecognized operator", m_expr, i);
}
}
......@@ -302,7 +346,7 @@ void Expression::tokenize() {
if (lvl)
lvl--;
else {
throw std::runtime_error("Unmatched brackets");
throw ParsingError("Unmatched bracket", m_expr, 0);
}
}
} // !inString || skip
......@@ -390,7 +434,7 @@ void Expression::setFunct(const std::string &name) {
m_funct = name;
trim(m_funct);
if (m_funct.empty()) {
throw std::runtime_error("Expression: Syntax error");
throw ParsingError("Empty function name");
}
// Check if the function has arguments
......@@ -412,7 +456,7 @@ void Expression::setFunct(const std::string &name) {
if (i != std::string::npos) {
std::string::size_type j = name.find_last_of(')');
if (j == std::string::npos || j < i) {
throw std::runtime_error("Unmatched brackets");
throw ParsingError("Unmatched bracket", name, i);
}
if (j > i + 1) // nonzero argument list
......@@ -425,6 +469,9 @@ void Expression::setFunct(const std::string &name) {
if (!tmp.isFunct() || tmp.name() != ",") {
m_terms.push_back(tmp);
} else {
if (f.empty() && tmp.name() == ",") {
f = ",";
}
std::string my_op = m_op;
*this = tmp;
m_op = my_op;
......@@ -446,6 +493,8 @@ std::string Expression::str() const {
} else if (!prec) { // function with a name
res << m_funct;
brackets = true;
} else if (m_op == "-" && m_funct == "+") {
brackets = true;
}
if (!m_terms.empty()) {
......
......@@ -47,6 +47,8 @@ FunctionFactoryImpl::createInitialized(const std::string &input) const {
Expression expr;
try {
expr.parse(input);
} catch (Expression::ParsingError& e) {
inputError(input + "\n " + e.what());
} catch (...) {
inputError(input);
}
......
......@@ -337,6 +337,18 @@ public:
TS_ASSERT_EQUALS(e.str(), "2*(a+b)+(1-sin(x-y))");
}
void testBrackets2() {
Expression e;
e.parse("2*(a+b)-(1-s)");
TS_ASSERT_EQUALS(e.str(), "2*(a+b)-(1-s)");
}
void testBrackets3() {
Expression e;
e.parse("2*(a+b)-(1-sin(x-y))");
TS_ASSERT_EQUALS(e.str(), "2*(a+b)-(1-sin(x-y))");
}
void testGetVariables() {
Expression e;
e.parse("a+b*sin(x)*fun1(fun2(a+c))");
......@@ -381,6 +393,110 @@ public:
TS_ASSERT_EQUALS(e[2].str(), "x");
}
}
void test_tie_expression_with_brackets() {
Expression e;
e.parse("ties=(a0=2,a1=4*(2+2))");
TS_ASSERT_EQUALS(e.str(), "ties=(a0=2,a1=4*(2+2))");
TS_ASSERT_EQUALS(e[1].name(), ",");
}
void test_bad_brackets() {
Expression e;
try {
e.parse("x+(y");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()),
"Unmatched bracket at\n\n(y\n^\n");
}
}
void test_bad_brackets_1() {
Expression e;
try {
e.parse("x+sin(y-3");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()),
"Unmatched bracket at\n\nsin(y-3\n ^\n");
}
}
void test_bad_brackets_2() {
Expression e;
try {
e.parse("x+sinsinsinsinsin(y-3");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(
std::string(e.what()),
"Unmatched bracket at\n\n...nsinsinsin(y-3\n ^\n");
}
}
void test_bad_brackets_3() {
Expression e;
try {
e.parse("x+sinsinsinsinsin(y-3*1+2+3+4+5+6+7+0");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "Unmatched bracket "
"at\n\n...nsinsinsin(y-3*1+2+3+4+"
"5+...\n ^\n");
}
}
void test_bad_brackets_4() {
Expression e;
try {
e.parse("x+y+z)");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "Unmatched bracket at\n\nx+y+z)\n^\n");
}
}
void test_syntax_error() {
Expression e;
try {
e.parse("x+y+");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "A binary operator isn't followed by a value at\n\nx+y+\n ^\n");
}
}
void test_syntax_error_1() {
Expression e;
try {
e.parse("x+y++++z");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "Unrecognized operator at\n\nx+y++++z\n ^\n");
}
}
void test_syntax_error_2() {
Expression e;
try {
e.parse("x+y+ + + +");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "A binary operator isn't followed by a value at\n\nx+y+ + + +\n ^\n");
}
}
void test_syntax_error_3() {
Expression e;
try {
e.parse("x+y+ )");
TS_FAIL("Expected an exception.");
} catch (Expression::ParsingError &e) {
TS_ASSERT_EQUALS(std::string(e.what()), "Unmatched bracket at\n\nx+y+ )\n^\n");
}
}
};
#endif /*EXPRESSIONTEST_H_*/
......@@ -343,6 +343,16 @@ public:
TS_ASSERT_EQUALS(funa->getParameter("a1"), 4);
}
void testCreateWithTies3() {
std::string fnString =
"name=FunctionFactoryTest_FunctA,ties=(a0=2,a1=4*(2+2))";
IFunction_sptr funa =
FunctionFactory::Instance().createInitialized(fnString);
TS_ASSERT(funa);
TS_ASSERT_EQUALS(funa->getParameter("a0"), 2);
TS_ASSERT_EQUALS(funa->getParameter("a1"), 16);
}
void testCreateCompositeWithTies() {
std::string fnString = "name=FunctionFactoryTest_FunctA,ties=(a0=a1=14);"
"name=FunctionFactoryTest_FunctB,b0=0.2,b1=1.2;ties="
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment