[muparser] More functions to return errors

Return OptionalError for more functions, allowing for explicit error
handling.
This commit is contained in:
ridiculousfish 2017-11-24 15:12:04 -08:00
parent d97bb3425f
commit 60d9c9fa00
3 changed files with 80 additions and 63 deletions

View file

@ -220,20 +220,22 @@ class ParserBase {
void ApplyRemainingOprt(ParserStack<token_type> &a_stOpt, void ApplyRemainingOprt(ParserStack<token_type> &a_stOpt,
ParserStack<token_type> &a_stVal) const; ParserStack<token_type> &a_stVal) const;
void ApplyBinOprt(ParserStack<token_type> &a_stOpt, ParserStack<token_type> &a_stVal) const; OptionalError ApplyBinOprt(ParserStack<token_type> &a_stOpt,
ParserStack<token_type> &a_stVal) const;
void ApplyIfElse(ParserStack<token_type> &a_stOpt, ParserStack<token_type> &a_stVal) const; OptionalError ApplyIfElse(ParserStack<token_type> &a_stOpt,
ParserStack<token_type> &a_stVal) const;
void ApplyFunc(ParserStack<token_type> &a_stOpt, ParserStack<token_type> &a_stVal, OptionalError ApplyFunc(ParserStack<token_type> &a_stOpt, ParserStack<token_type> &a_stVal,
int iArgCount) const; int iArgCount) const;
token_type ApplyStrFunc(const token_type &a_FunTok, OptionalError ApplyStrFunc(const token_type &a_FunTok,
const std::vector<token_type> &a_vArg) const; const std::vector<token_type> &a_vArg) const;
int GetOprtPrecedence(const token_type &a_Tok) const; int GetOprtPrecedence(const token_type &a_Tok) const;
EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const; EOprtAssociativity GetOprtAssociativity(const token_type &a_Tok) const;
void CreateRPN() const; OptionalError CreateRPN() const;
ValueOrError ParseString() const; ValueOrError ParseString() const;
ValueOrError ParseCmdCode() const; ValueOrError ParseCmdCode() const;

View file

@ -291,9 +291,8 @@ class ParserToken {
/** \biref Get value of the token. /** \biref Get value of the token.
Only applicable to variable and value tokens. Only applicable to variable and value tokens.
\throw exception_type if token is no value/variable token.
*/ */
TBase GetVal() const { ValueOrError GetVal() const {
switch (m_iCode) { switch (m_iCode) {
case cmVAL: case cmVAL:
return m_fVal; return m_fVal;

View file

@ -538,9 +538,9 @@ EOprtAssociativity ParserBase::GetOprtAssociativity(const token_type &a_Tok) con
const varmap_type &ParserBase::GetUsedVar() const { const varmap_type &ParserBase::GetUsedVar() const {
try { try {
m_pTokenReader->IgnoreUndefVar(true); m_pTokenReader->IgnoreUndefVar(true);
CreateRPN(); // try to create bytecode, but don't use it for any further calculations since // Try to create bytecode, but don't use it for any further calculations since it may
// it // contain references to nonexisting variables.
// may contain references to nonexisting variables. OptionalError err = CreateRPN();
m_pParseFormula = &ParserBase::ParseString; m_pParseFormula = &ParserBase::ParseString;
m_pTokenReader->IgnoreUndefVar(false); m_pTokenReader->IgnoreUndefVar(false);
} catch (exception_type & /*e*/) { } catch (exception_type & /*e*/) {
@ -583,45 +583,42 @@ const string_type &ParserBase::GetExpr() const { return m_pTokenReader->GetExpr(
\param a_FunTok Function token. \param a_FunTok Function token.
\throw exception_type If the function token is not a string function \throw exception_type If the function token is not a string function
*/ */
ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok, OptionalError ParserBase::ApplyStrFunc(const token_type &a_FunTok,
const std::vector<token_type> &a_vArg) const { const std::vector<token_type> &a_vArg) const {
if (a_vArg.back().GetCode() != cmSTRING) if (a_vArg.back().GetCode() != cmSTRING)
Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); Error(ecSTRING_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());
token_type valTok; token_type valTok;
generic_fun_type pFunc = a_FunTok.GetFuncAddr(); generic_fun_type pFunc = a_FunTok.GetFuncAddr();
assert(pFunc); assert(pFunc);
bool errored = false;
try { // Check function arguments; write dummy value into valtok to represent the result
// Check function arguments; write dummy value into valtok to represent the result switch (a_FunTok.GetArgCount()) {
switch (a_FunTok.GetArgCount()) { case 0:
case 0: valTok.SetVal(1);
valTok.SetVal(1); a_vArg[0].GetAsString();
a_vArg[0].GetAsString(); break;
break; case 1:
case 1: valTok.SetVal(1);
valTok.SetVal(1); a_vArg[1].GetAsString();
a_vArg[1].GetAsString(); errored |= a_vArg[0].GetVal().has_error();
a_vArg[0].GetVal(); break;
break; case 2:
case 2: valTok.SetVal(1);
valTok.SetVal(1); a_vArg[2].GetAsString();
a_vArg[2].GetAsString(); errored |= a_vArg[1].GetVal().has_error();
a_vArg[1].GetVal(); errored |= a_vArg[0].GetVal().has_error();
a_vArg[0].GetVal(); break;
break; default:
default: assert(0 && "Unexpected arg count");
assert(0 && "Unexpected arg count"); }
} if (errored) {
} catch (ParserError &) {
Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString()); Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), a_FunTok.GetAsString());
} }
// string functions won't be optimized // string functions won't be optimized
m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx()); m_vRPN.AddStrFun(pFunc, a_FunTok.GetArgCount(), a_vArg.back().GetIdx());
return {};
// Push dummy value representing the function result to the stack
return valTok;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -629,14 +626,14 @@ ParserBase::token_type ParserBase::ApplyStrFunc(const token_type &a_FunTok,
\param iArgCount Number of Arguments actually gathered used only for multiarg functions. \param iArgCount Number of Arguments actually gathered used only for multiarg functions.
\post The result is pushed to the value stack \post The result is pushed to the value stack
\post The function token is removed from the stack \post The function token is removed from the stack
\throw exception_type if Argument count does not match function requirements. \return ParserError if Argument count does not match function requirements.
*/ */
void ParserBase::ApplyFunc(ParserStack<token_type> &a_stOpt, ParserStack<token_type> &a_stVal, OptionalError ParserBase::ApplyFunc(ParserStack<token_type> &a_stOpt,
int a_iArgCount) const { ParserStack<token_type> &a_stVal, int a_iArgCount) const {
assert(m_pTokenReader.get()); assert(m_pTokenReader.get());
// Operator stack empty or does not contain tokens with callback functions // Operator stack empty or does not contain tokens with callback functions
if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr() == 0) return; if (a_stOpt.empty() || a_stOpt.top().GetFuncAddr() == 0) return {};
token_type funTok = a_stOpt.pop(); token_type funTok = a_stOpt.pop();
assert(funTok.GetFuncAddr()); assert(funTok.GetFuncAddr());
@ -675,14 +672,16 @@ void ParserBase::ApplyFunc(ParserStack<token_type> &a_stOpt, ParserStack<token_t
} }
switch (funTok.GetCode()) { switch (funTok.GetCode()) {
case cmFUNC_STR: case cmFUNC_STR: {
stArg.push_back(a_stVal.pop()); stArg.push_back(a_stVal.pop());
if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR) if (stArg.back().GetType() == tpSTR && funTok.GetType() != tpSTR)
Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString()); Error(ecVAL_EXPECTED, m_pTokenReader->GetPos(), funTok.GetAsString());
ApplyStrFunc(funTok, stArg); OptionalError err = ApplyStrFunc(funTok, stArg);
if (err.has_error()) return err;
break; break;
}
case cmOPRT_BIN: case cmOPRT_BIN:
case cmOPRT_POSTFIX: case cmOPRT_POSTFIX:
@ -703,11 +702,12 @@ void ParserBase::ApplyFunc(ParserStack<token_type> &a_stOpt, ParserStack<token_t
token_type token; token_type token;
token.SetVal(1); token.SetVal(1);
a_stVal.push(token); a_stVal.push(token);
return {};
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void ParserBase::ApplyIfElse(ParserStack<token_type> &a_stOpt, OptionalError ParserBase::ApplyIfElse(ParserStack<token_type> &a_stOpt,
ParserStack<token_type> &a_stVal) const { ParserStack<token_type> &a_stVal) const {
// Check if there is an if Else clause to be calculated // Check if there is an if Else clause to be calculated
while (a_stOpt.size() && a_stOpt.top().GetCode() == cmELSE) { while (a_stOpt.size() && a_stOpt.top().GetCode() == cmELSE) {
token_type opElse = a_stOpt.pop(); token_type opElse = a_stOpt.pop();
@ -724,7 +724,9 @@ void ParserBase::ApplyIfElse(ParserStack<token_type> &a_stOpt,
token_type vVal1 = a_stVal.pop(); token_type vVal1 = a_stVal.pop();
token_type vExpr = a_stVal.pop(); token_type vExpr = a_stVal.pop();
a_stVal.push((vExpr.GetVal() != 0) ? vVal1 : vVal2); ValueOrError vExprValue = vExpr.GetVal();
if (vExprValue.has_error()) return vExprValue.error();
a_stVal.push((vExprValue.value() != 0) ? vVal1 : vVal2);
token_type opIf = a_stOpt.pop(); token_type opIf = a_stOpt.pop();
assert(opElse.GetCode() == cmELSE && "Invalid if/else clause"); assert(opElse.GetCode() == cmELSE && "Invalid if/else clause");
@ -732,17 +734,18 @@ void ParserBase::ApplyIfElse(ParserStack<token_type> &a_stOpt,
m_vRPN.AddIfElse(cmENDIF); m_vRPN.AddIfElse(cmENDIF);
} // while pending if-else-clause found } // while pending if-else-clause found
return {};
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
/** \brief Performs the necessary steps to write code for /** \brief Performs the necessary steps to write code for
the execution of binary operators into the bytecode. the execution of binary operators into the bytecode.
*/ */
void ParserBase::ApplyBinOprt(ParserStack<token_type> &a_stOpt, OptionalError ParserBase::ApplyBinOprt(ParserStack<token_type> &a_stOpt,
ParserStack<token_type> &a_stVal) const { ParserStack<token_type> &a_stVal) const {
// is it a user defined binary operator? // is it a user defined binary operator?
if (a_stOpt.top().GetCode() == cmOPRT_BIN) { if (a_stOpt.top().GetCode() == cmOPRT_BIN) {
ApplyFunc(a_stOpt, a_stVal, 2); return ApplyFunc(a_stOpt, a_stVal, 2);
} else { } else {
assert(a_stVal.size() >= 2 && "Too few arguments for binary operator"); assert(a_stVal.size() >= 2 && "Too few arguments for binary operator");
token_type valTok1 = a_stVal.pop(), valTok2 = a_stVal.pop(), optTok = a_stOpt.pop(), resTok; token_type valTok1 = a_stVal.pop(), valTok2 = a_stVal.pop(), optTok = a_stOpt.pop(), resTok;
@ -761,6 +764,7 @@ void ParserBase::ApplyBinOprt(ParserStack<token_type> &a_stOpt,
resTok.SetVal(1); resTok.SetVal(1);
a_stVal.push(resTok); a_stVal.push(resTok);
} }
return {};
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1029,8 +1033,8 @@ ValueOrError ParserBase::ParseCmdCode() const {
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void ParserBase::CreateRPN() const { OptionalError ParserBase::CreateRPN() const {
if (!m_pTokenReader->GetExpr().length()) Error(ecUNEXPECTED_EOF, 0); if (!m_pTokenReader->GetExpr().length()) return ParserError(ecUNEXPECTED_EOF, 0);
ParserStack<token_type> stOpt, stVal; ParserStack<token_type> stOpt, stVal;
ParserStack<int> stArgCount; ParserStack<int> stArgCount;
@ -1061,10 +1065,13 @@ void ParserBase::CreateRPN() const {
m_vRPN.AddVar(static_cast<value_type *>(opt.GetVar())); m_vRPN.AddVar(static_cast<value_type *>(opt.GetVar()));
break; break;
case cmVAL: case cmVAL: {
stVal.push(opt); stVal.push(opt);
m_vRPN.AddVal(opt.GetVal()); ValueOrError optVal = opt.GetVal();
if (optVal.has_error()) throw optVal.error();
m_vRPN.AddVal(optVal.value());
break; break;
}
case cmELSE: case cmELSE:
m_nIfElseCounter--; m_nIfElseCounter--;
@ -1117,7 +1124,8 @@ void ParserBase::CreateRPN() const {
// was a function before this bracket // was a function before this bracket
if (stOpt.size() && stOpt.top().GetCode() != cmOPRT_INFIX && if (stOpt.size() && stOpt.top().GetCode() != cmOPRT_INFIX &&
stOpt.top().GetCode() != cmOPRT_BIN && stOpt.top().GetFuncAddr() != 0) { stOpt.top().GetCode() != cmOPRT_BIN && stOpt.top().GetFuncAddr() != 0) {
ApplyFunc(stOpt, stVal, iArgCount); OptionalError err = ApplyFunc(stOpt, stVal, iArgCount);
if (err.has_error()) return err.error();
} }
} }
} // if bracket content is evaluated } // if bracket content is evaluated
@ -1166,10 +1174,14 @@ void ParserBase::CreateRPN() const {
break; break;
} }
OptionalError oerr;
if (stOpt.top().GetCode() == cmOPRT_INFIX) if (stOpt.top().GetCode() == cmOPRT_INFIX)
ApplyFunc(stOpt, stVal, 1); oerr = ApplyFunc(stOpt, stVal, 1);
else else
ApplyBinOprt(stOpt, stVal); oerr = ApplyBinOprt(stOpt, stVal);
if (oerr.has_error()) {
return oerr.error();
}
} // while ( ... ) } // while ( ... )
if (opt.GetCode() == cmIF) m_vRPN.AddIfElse(opt.GetCode()); if (opt.GetCode() == cmIF) m_vRPN.AddIfElse(opt.GetCode());
@ -1192,10 +1204,12 @@ void ParserBase::CreateRPN() const {
stOpt.push(opt); stOpt.push(opt);
break; break;
case cmOPRT_POSTFIX: case cmOPRT_POSTFIX: {
stOpt.push(opt); stOpt.push(opt);
ApplyFunc(stOpt, stVal, 1); // this is the postfix operator OptionalError oerr = ApplyFunc(stOpt, stVal, 1); // this is the postfix operator
if (oerr.has_error()) return oerr.error();
break; break;
}
default: default:
assert(0 && "muParser internal error"); assert(0 && "muParser internal error");
@ -1228,6 +1242,7 @@ void ParserBase::CreateRPN() const {
if (stVal.top().GetType() != tpDBL) Error(ecSTR_RESULT); if (stVal.top().GetType() != tpDBL) Error(ecSTR_RESULT);
m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads); m_vStackBuffer.resize(m_vRPN.GetMaxStackSize() * s_MaxNumOpenMPThreads);
return {};
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1241,7 +1256,8 @@ void ParserBase::CreateRPN() const {
*/ */
ValueOrError ParserBase::ParseString() const { ValueOrError ParserBase::ParseString() const {
try { try {
CreateRPN(); OptionalError oerr = CreateRPN();
if (oerr.has_error()) return oerr.error();
m_pParseFormula = &ParserBase::ParseCmdCode; m_pParseFormula = &ParserBase::ParseCmdCode;
return (this->*m_pParseFormula)(); return (this->*m_pParseFormula)();
} catch (ParserError &exc) { } catch (ParserError &exc) {
@ -1394,7 +1410,7 @@ void ParserBase::StackDump(const ParserStack<token_type> &a_stVal,
if (val.GetType() == tpSTR) if (val.GetType() == tpSTR)
mu::console() << _T(" \"") << val.GetAsString() << _T("\" "); mu::console() << _T(" \"") << val.GetAsString() << _T("\" ");
else else
mu::console() << _T(" ") << val.GetVal() << _T(" "); mu::console() << _T(" ") << val.GetVal().value() << _T(" ");
} }
mu::console() << "\nOperator stack:\n"; mu::console() << "\nOperator stack:\n";