[muparser] Make some error handling in ParserBase explicit

Return ParserError instead of throwing exceptions
This commit is contained in:
ridiculousfish 2017-11-25 02:30:18 -08:00
parent 62bedde23d
commit f3a9773849
4 changed files with 58 additions and 41 deletions

View File

@ -1079,7 +1079,6 @@ OptionalError ParserBase::CreateRPN() const {
case cmARG_SEP:
if (stArgCount.empty())
return Error(ecUNEXPECTED_ARG_SEP, m_pTokenReader->GetPos());
++stArgCount.top();
// fallthrough intentional (no break!)

View File

@ -42,6 +42,10 @@
#include "muParserDLL.h"
#include "muParserInt.h"
static void throwIfError(mu::OptionalError oerr) {
if (oerr.has_error()) throw oerr.error();
}
#define MU_TRY try {
#define MU_CATCH \
} \
@ -205,7 +209,7 @@ API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser) {
API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->SetExpr(a_szExpr);
throwIfError(p->SetExpr(a_szExpr));
MU_CATCH
}
@ -411,7 +415,8 @@ mupDefineOprt(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_p
muInt_t a_nPrec, muInt_t a_nOprtAsct, muBool_t a_bAllowOpt) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->DefineOprt(a_szName, a_pFun, a_nPrec, (mu::EOprtAssociativity)a_nOprtAsct, a_bAllowOpt != 0);
throwIfError(p->DefineOprt(a_szName, a_pFun, a_nPrec, (mu::EOprtAssociativity)a_nOprtAsct,
a_bAllowOpt != 0));
MU_CATCH
}
@ -420,7 +425,7 @@ API_EXPORT(void)
mupDefineVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_pVar) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->DefineVar(a_szName, a_pVar);
throwIfError(p->DefineVar(a_szName, a_pVar));
MU_CATCH
}
@ -429,7 +434,7 @@ API_EXPORT(void)
mupDefineBulkVar(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t* a_pVar) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->DefineVar(a_szName, a_pVar);
throwIfError(p->DefineVar(a_szName, a_pVar));
MU_CATCH
}
@ -438,7 +443,7 @@ API_EXPORT(void)
mupDefineConst(muParserHandle_t a_hParser, const muChar_t* a_szName, muFloat_t a_fVal) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->DefineConst(a_szName, a_fVal);
throwIfError(p->DefineConst(a_szName, a_fVal));
MU_CATCH
}
@ -447,7 +452,7 @@ API_EXPORT(void)
mupDefineStrConst(muParserHandle_t a_hParser, const muChar_t* a_szName, const muChar_t* a_szVal) {
MU_TRY
muParser_t* const p(AsParser(a_hParser));
p->DefineStrConst(a_szName, a_szVal);
throwIfError(p->DefineStrConst(a_szName, a_szVal));
MU_CATCH
}

View File

@ -41,6 +41,12 @@ using namespace std;
namespace mu {
namespace Test {
static value_type getOrThrow(mu::ValueOrError voerr) {
if (voerr.has_error()) throw voerr.error();
return *voerr;
}
int ParserTester::c_iCount = 0;
//---------------------------------------------------------------------------------------------
@ -93,14 +99,14 @@ int ParserTester::TestInterface() {
p.DefineVar(_T("b"), &afVal[1]);
p.DefineVar(_T("c"), &afVal[2]);
p.SetExpr(_T("a+b+c"));
(void)p.Eval();
getOrThrow(p.Eval());
} catch (...) {
iStat += 1; // this is not supposed to happen
}
try {
p.RemoveVar(_T("c"));
(void)p.Eval();
getOrThrow(p.Eval());
iStat += 1; // not supposed to reach this, nonexisting variable "c" deleted...
} catch (...) {
// failure is expected...
@ -323,12 +329,10 @@ int ParserTester::TestNames() {
PARSER_THROWCHECK(Var, true, _T("a_min"), &a)
PARSER_THROWCHECK(Var, true, _T("a_min0"), &a)
PARSER_THROWCHECK(Var, true, _T("a_min9"), &a)
PARSER_THROWCHECK(Var, false, _T("a_min9"), 0)
// Postfix operators
// fail
PARSER_THROWCHECK(PostfixOprt, false, _T("(k"), f1of1)
PARSER_THROWCHECK(PostfixOprt, false, _T("9+"), f1of1)
PARSER_THROWCHECK(PostfixOprt, false, _T("+"), 0)
// pass
PARSER_THROWCHECK(PostfixOprt, true, _T("-a"), f1of1)
PARSER_THROWCHECK(PostfixOprt, true, _T("?a"), f1of1)
@ -977,7 +981,7 @@ int ParserTester::ThrowTest(const string_type &a_str, int a_iErrc, bool a_bFail)
p.DefineFun(_T("strfun2"), StrFun2);
p.DefineFun(_T("strfun3"), StrFun3);
p.SetExpr(a_str);
(void)p.Eval();
getOrThrow(p.Eval());
} catch (ParserError &e) {
// output the formula in case of an failed test
if (a_bFail == false || (a_bFail == true && a_iErrc != e.GetCode())) {
@ -1021,10 +1025,10 @@ int ParserTester::EqnTestWithVarChange(const string_type &a_str, double a_fVar1,
p.SetExpr(a_str);
var = a_fVar1;
fVal[0] = *p.Eval();
fVal[0] = getOrThrow(p.Eval());
var = a_fVar2;
fVal[1] = *p.Eval();
fVal[1] = getOrThrow(p.Eval());
if (fabs(a_fRes1 - fVal[0]) > 0.0000000001)
throw std::runtime_error("incorrect result (first pass)");
@ -1136,8 +1140,8 @@ int ParserTester::EqnTest(const string_type &a_str, double a_fRes, bool a_fPass)
// Test bytecode integrity
// String parsing and bytecode parsing must yield the same result
fVal[0] = *p1->Eval(); // result from stringparsing
fVal[1] = *p1->Eval(); // result from bytecode
fVal[0] = getOrThrow(p1->Eval()); // result from stringparsing
fVal[1] = getOrThrow(p1->Eval()); // result from bytecode
if (fVal[0] != fVal[1])
throw Parser::exception_type(_T("Bytecode / string parsing mismatch."));
@ -1204,8 +1208,8 @@ int ParserTester::EqnTestInt(const string_type &a_str, double a_fRes, bool a_fPa
p.DefineVar(_T("c"), &vVarVal[2]);
p.SetExpr(a_str);
fVal[0] = *p.Eval(); // result from stringparsing
fVal[1] = *p.Eval(); // result from bytecode
fVal[0] = getOrThrow(p.Eval()); // result from stringparsing
fVal[1] = getOrThrow(p.Eval()); // result from bytecode
if (fVal[0] != fVal[1]) throw Parser::exception_type(_T("Bytecode corrupt."));

View File

@ -166,10 +166,13 @@ ParserTokenReader::token_type ParserTokenReader::ReadNextToken() {
//
string_type strTok;
int iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, m_iPos);
if (iEnd != m_iPos) Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);
if (iEnd != m_iPos) {
Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);
return {};
}
Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos));
return token_type();
return {};
}
//---------------------------------------------------------------------------
@ -267,7 +270,7 @@ bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) {
// The assignment operator need special treatment
if (i == cmASSIGN && m_iSynFlags & noASSIGN)
Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
return Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
if (!m_pParser->HasBuiltInOprt()) continue;
if (m_iSynFlags & noOPT) {
@ -276,7 +279,7 @@ bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) {
// their identifiers
if (IsInfixOpTok(a_Tok)) return true;
Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
return Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
}
m_iSynFlags =
@ -284,7 +287,7 @@ bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) {
break;
case cmBO:
if (m_iSynFlags & noBO) Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
if (m_iSynFlags & noBO) return Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
if (m_lastTok.GetCode() == cmFUNC)
m_iSynFlags =
@ -297,21 +300,23 @@ bool ParserTokenReader::IsBuiltIn(token_type &a_Tok) {
break;
case cmBC:
if (m_iSynFlags & noBC) Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
if (m_iSynFlags & noBC) return Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN;
if (--m_iBrackets < 0) Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
if (--m_iBrackets < 0) return Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
break;
case cmELSE:
if (m_iSynFlags & noELSE) Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
if (m_iSynFlags & noELSE)
return Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;
break;
case cmIF:
if (m_iSynFlags & noIF) Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
if (m_iSynFlags & noIF)
return Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE;
break;
@ -340,7 +345,7 @@ bool ParserTokenReader::IsArgSep(token_type &a_Tok) {
szSep[0] = m_cArgSep;
szSep[1] = 0;
if (m_iSynFlags & noARG_SEP) Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);
if (m_iSynFlags & noARG_SEP) return Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);
m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN;
m_iPos++;
@ -363,9 +368,9 @@ bool ParserTokenReader::IsEOF(token_type &a_Tok) {
// check for EOF
if (!szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/) {
if (m_iSynFlags & noEND) Error(ecUNEXPECTED_EOF, m_iPos);
if (m_iSynFlags & noEND) return Error(ecUNEXPECTED_EOF, m_iPos);
if (m_iBrackets > 0) Error(ecMISSING_PARENS, m_iPos, _T(")"));
if (m_iBrackets > 0) return Error(ecMISSING_PARENS, m_iPos, _T(")"));
m_iSynFlags = 0;
a_Tok.Set(cmEND);
@ -392,7 +397,8 @@ bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok) {
a_Tok.Set(it->second, it->first);
m_iPos += (int)it->first.length();
if (m_iSynFlags & noINFIXOP) Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
if (m_iSynFlags & noINFIXOP)
return Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;
return true;
@ -405,7 +411,7 @@ bool ParserTokenReader::IsInfixOpTok(token_type &a_Tok) {
m_iPos = (int)iEnd;
if (m_iSynFlags & noINFIXOP)
Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
return Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
m_iSynFlags = noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN;
return true;
@ -435,7 +441,8 @@ bool ParserTokenReader::IsFunTok(token_type &a_Tok) {
m_iPos = (int)iEnd;
if (m_iSynFlags & noFUN)
Error(ecUNEXPECTED_FUN, m_iPos - (int)a_Tok.GetAsString().length(), a_Tok.GetAsString());
return Error(ecUNEXPECTED_FUN, m_iPos - (int)a_Tok.GetAsString().length(),
a_Tok.GetAsString());
m_iSynFlags = noANY ^ noBO;
return true;
@ -483,7 +490,7 @@ bool ParserTokenReader::IsOprt(token_type &a_Tok) {
else {
// nope, no infix operator
return false;
// Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
// return Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
}
}
@ -562,7 +569,8 @@ bool ParserTokenReader::IsValTok(token_type &a_Tok) {
m_iPos = iEnd;
a_Tok.SetVal(item->second, strTok);
if (m_iSynFlags & noVAL) Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
if (m_iSynFlags & noVAL)
return Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
return true;
@ -578,7 +586,8 @@ bool ParserTokenReader::IsValTok(token_type &a_Tok) {
// 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2
strTok.assign(m_strFormula.c_str(), iStart, m_iPos - iStart);
if (m_iSynFlags & noVAL) Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
if (m_iSynFlags & noVAL)
return Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
a_Tok.SetVal(fVal, strTok);
m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
@ -604,7 +613,7 @@ bool ParserTokenReader::IsVarTok(token_type &a_Tok) {
varmap_type::const_iterator item = m_pVarDef->find(strTok);
if (item == m_pVarDef->end()) return false;
if (m_iSynFlags & noVAR) Error(ecUNEXPECTED_VAR, m_iPos, strTok);
if (m_iSynFlags & noVAR) return Error(ecUNEXPECTED_VAR, m_iPos, strTok);
m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd);
@ -630,7 +639,7 @@ bool ParserTokenReader::IsStrVarTok(token_type &a_Tok) {
strmap_type::const_iterator item = m_pStrVarDef->find(strTok);
if (item == m_pStrVarDef->end()) return false;
if (m_iSynFlags & noSTR) Error(ecUNEXPECTED_VAR, m_iPos, strTok);
if (m_iSynFlags & noSTR) return Error(ecUNEXPECTED_VAR, m_iPos, strTok);
m_iPos = iEnd;
if (!m_pParser->m_vStringVarBuf.size()) assert(0 && "muParser internal error");
@ -658,7 +667,7 @@ bool ParserTokenReader::IsUndefVarTok(token_type &a_Tok) {
// token identifier.
// related bug report:
// http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979
Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);
return Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);
}
// If a factory is available implicitely create new variables
@ -706,11 +715,11 @@ bool ParserTokenReader::IsString(token_type &a_Tok) {
iSkip++;
}
if (iEnd == string_type::npos) Error(ecUNTERMINATED_STRING, m_iPos, _T("\""));
if (iEnd == string_type::npos) return Error(ecUNTERMINATED_STRING, m_iPos, _T("\""));
string_type strTok(strBuf.begin(), strBuf.begin() + iEnd);
if (m_iSynFlags & noSTR) Error(ecUNEXPECTED_STR, m_iPos, strTok);
if (m_iSynFlags & noSTR) return Error(ecUNEXPECTED_STR, m_iPos, strTok);
m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer
a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size());