Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions parser/equationsParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,11 @@ void CalcArray(vector<string> equations, vector<string> &out) {
}
}

Value PureCalc(string input) {
Value result;
ParserX parser(pckALL_NON_COMPLEX);
parser.SetExpr(input);
return parser.Eval();
}

EQUATIONS_PARSER_END
1 change: 1 addition & 0 deletions parser/equationsParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void ReplaceAll(std::string& source, const std::string& from, const std::string&
std::string Calc(std::string input);
std::string CalcJson(std::string input);
void CalcArray(std::vector<std::string> in, std::vector<std::string> &out);
mup::Value PureCalc(std::string input);

EQUATIONS_PARSER_END

Expand Down
106 changes: 97 additions & 9 deletions parser/mpFuncCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,21 @@

#include "mpValue.h"
#include "mpParserBase.h"
#include "equationsParser.h"

#define ONE_DAY (24 * 60 * 60)

MUP_NAMESPACE_START

void raise_error (EErrorCodes error, int position, const ptr_val_type *arguments) {
ErrorContext err;
err.Errc = error;
err.Arg = position;
err.Type1 = arguments[position - 1]->GetType();
err.Type2 = 's';
throw ParserError(err);
}

//------------------------------------------------------------------------------
//
// FunParserID
Expand Down Expand Up @@ -399,6 +409,93 @@ MUP_NAMESPACE_START
return new FunMask(*this);
}

//----------------------------------------------------------------------------------
// |
// Class FunCase |
// Function calculate custom switch case |
// Usage: case("variable", "exp1;res1", "exp2;res2", "default;defres") |
// Optional: '@' token in exp = evaluate(variable + exp1) |
// Optional: '@' token in res = evaluate(res) |
// |
//----------------------------------------------------------------------------------

FunCase::FunCase()
:ICallback(cmFUNC, _T("case"), -1)
{}

Value get_case_value(string_type expression) {
Value result;
if(expression[0] == '@'){
result = EquationsParser::PureCalc(expression.substr(1));
} else {
result = expression;
}
return result;
}

bool compare_variable_to_operand(string_type variable, string_type operand, int err_pos, const ptr_val_type *a_pArg) {
bool comparison_result;
if(operand[0] == '@'){
Value result = EquationsParser::PureCalc(variable + operand.substr(1));
if(result.GetType() != 'b'){
raise_error(ecNOT_BOOL_CASE, err_pos, a_pArg);
}
comparison_result = result.GetBool();
} else {
comparison_result = (variable.compare(operand) == 0);
}
return comparison_result;
}

void FunCase::Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc)
{
if (a_iArgc < 2) {
throw ParserError(ErrorContext(ecTOO_FEW_PARAMS, GetExprPos(), GetIdent()));
}

string_type variable = a_pArg[0]->GetString();
string_type operand, result, case_arguments;
bool found_match = false;
for(int i=1; i < a_iArgc && !found_match ; i++) {
case_arguments = a_pArg[i]->GetString();
size_t pos = case_arguments.find(';');
if(pos == std::string::npos || pos == 0 || pos == case_arguments.size()){
raise_error(ecMISSING_CASE_SEPARATOR, i, a_pArg);
}
operand = case_arguments.substr(0, pos);
result = case_arguments.substr(pos + 1);
if(compare_variable_to_operand(variable, operand, i, a_pArg)) {
*ret = get_case_value(result);
found_match = true;
}
}

if(!found_match){
case_arguments = a_pArg[a_iArgc - 1]->GetString();
size_t pos = case_arguments.find(';');
if(pos == std::string::npos || pos == 0 || pos == case_arguments.size()){
raise_error(ecMISSING_CASE_SEPARATOR, a_iArgc - 1, a_pArg);
}
operand = case_arguments.substr(0, pos);
result = case_arguments.substr(pos + 1);
if(operand.compare("default") == 0) {
*ret = get_case_value(result);
} else {
raise_error(ecMISSING_CASE_DEFAULT, a_iArgc - 1, a_pArg);
}
}
}

const char_type* FunCase::GetDesc() const
{
return _T("case(variable, expression_list) - Returns the result of a switch case on variable.");
}

////------------------------------------------------------------------------------
IToken* FunCase::Clone() const
{
return new FunCase(*this);
}
//------------------------------------------------------------------------------
// |
// Below we have the section related to Date functions |
Expand Down Expand Up @@ -449,15 +546,6 @@ MUP_NAMESPACE_START
*date = *localtime(&new_day);
}

void raise_error (EErrorCodes error, int position, const ptr_val_type *arguments) {
ErrorContext err;
err.Errc = error;
err.Arg = position;
err.Type1 = arguments[position - 1]->GetType();
err.Type2 = 's';
throw ParserError(err);
}

string_type localized_weekday(int week_day, const ptr_val_type *a_pArg) {
string_type locale = a_pArg[1]->GetString();
string_type ret = "";
Expand Down
15 changes: 14 additions & 1 deletion parser/mpFuncCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ MUP_NAMESPACE_START
virtual IToken* Clone() const override;
}; // class FunMask

//------------------------------------------------------------------------------
/** \brief Returns the result of a case operation.
\ingroup functions
*/
class FunCase: public ICallback
{
public:
FunCase();
virtual void Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) override;
virtual const char_type* GetDesc() const override;
virtual IToken* Clone() const override;
}; // class FunCase

//------------------------------------------------------------------------------
/** \brief Determine the difference in days between two dates.
\ingroup functions
Expand Down Expand Up @@ -238,7 +251,7 @@ MUP_NAMESPACE_START
}; // class FunWeekYear

//------------------------------------------------------------------------------
/** \brief Return the week of year of a date.
/** \brief Return the weekday of a date.
\ingroup functions
*/
class FunWeekDay: public ICallback
Expand Down
1 change: 1 addition & 0 deletions parser/mpPackageCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ void PackageCommon::AddToParser(ParserXBase *pParser)

// Special functions
pParser->DefineFun(new FunMask());
pParser->DefineFun(new FunCase());

// Date functions
pParser->DefineFun(new FunDaysDiff());
Expand Down
3 changes: 3 additions & 0 deletions parser/mpParserMessageProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ MUP_NAMESPACE_START
m_vErrMsg[ecINVALID_TYPES_MATCH] = _T("Both values of the default(x, y) function should have the same type");
m_vErrMsg[ecUKNOWN_LOCALE] = _T("The chosen locale is not supported");
m_vErrMsg[ecINVALID_TIME_FORMAT] = _T("Invalid time format on parameter(s). Please use the \"HH:MM:SS\" format.");
m_vErrMsg[ecMISSING_CASE_SEPARATOR] = _T("Missing \";\" separator. Please separate the comparison from the result with \";\"");
m_vErrMsg[ecMISSING_CASE_DEFAULT] = _T("Missing default in case operator. Please make sure case has a default or that one or more of the conditions are true");
m_vErrMsg[ecNOT_BOOL_CASE] = _T("Case comparison not resulting in true or false");
}

#if defined(_UNICODE)
Expand Down
4 changes: 4 additions & 0 deletions parser/mpTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ enum EErrorCodes
// time related errors
ecINVALID_TIME_FORMAT = 60, ///< Invalid time format

ecMISSING_CASE_SEPARATOR = 61, ///< Missing ; case separator
ecMISSING_CASE_DEFAULT = 62, ///< Missing default in case operation
ecNOT_BOOL_CASE = 63, ///< Case comparison not resulting in boolean

// The last two are special entries
ecCOUNT, ///< This is no error code, It just stores the total number of error codes
ecUNDEFINED = -1 ///< Undefined message, placeholder to detect unassigned error messages
Expand Down
32 changes: 32 additions & 0 deletions tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,38 @@ test_eval 'weekday("2019-03-21", "invalidlocale")' 'The chosen locale is not sup
test_eval 'weekday("2024-32-21")' 'Invalid format on the parameter(s). Please use two "yyyy-mm-dd" for dates OR two "yyyy-mm-ddTHH:MM" for date_times.'
test_eval 'weekday("2024-09-17", "en", "one too many params")' 'Too many parameters passed to function "weekday".'

# case operation tests

# case textmatch
test_eval 'case("textmatch", "textmatch;matched")' '"matched"'
test_eval 'case("textmatch", "textmatch;matched", "default;not matched")' '"matched"'
test_eval 'case("textmatch", "textmatc;matched", "default;not matched")' '"not matched"'
test_eval 'case("textmatch", "textmatch;@5*5")' '25'
test_eval 'case("textmatch", "textmatc;@5*5", "default;@-1")' '-1'

# case comparison with variable

test_eval 'case("5", "@ > 4;yes it is")' '"yes it is"'
test_eval 'case("5", "@ < 4;yes it is", "default;no it is not")' '"no it is not"'
test_eval 'case("5", "@ > 4;@5*6")' '30'
test_eval 'case("5", "@ < 4;@5*6", "default;@-1")' '-1'

# mix and match
test_eval 'case("5", "@ < 4;yes it is", "5;we can still text match")' '"we can still text match"'
test_eval 'case("5", "@ < 4;yes it is", "default;and use normal default")' '"and use normal default"'
test_eval 'case("5", "@ < 4;yes it is", "5;@5*5")' '25'

# function inside function
test_eval 'case("textmatch", "textmatch;@sum(1,2,3)")' '6'
test_eval 'case("textmatch", "textmatch;@case(\"7\", \"@>sum(1,2,3);caseception\")")' '"caseception"'
test_eval 'case("textmatch", "textmatch;@case(\"5\", \"@>sum(1,2,3);caseception\", \"default;caseceptionpt2\")")' '"caseceptionpt2"'

# case errors
test_eval 'case("5", "@ < 4;yes it is")' 'Missing default in case operator. Please make sure case has a default or that one or more of the conditions are true'
test_eval 'case("5", "@ *5;not a boolean")' 'Case comparison not resulting in true or false'
test_eval 'case("5", "@ *5;not a boolean", "default;should fail even with a default")' 'Case comparison not resulting in true or false'
test_eval 'case("textmatch", "textmatch")' 'Missing ";" separator. Please separate the comparison from the result with ";"'
test_eval 'case("textmatch", "textmatch", "default;should fail even with a default")' 'Missing ";" separator. Please separate the comparison from the result with ";"'
# Regex match test
match_regex 'current_time(5)' '\b([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\b'
match_regex 'current_time()' '\b([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\b'
Expand Down
4 changes: 4 additions & 0 deletions value_test/mpError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ MUP_NAMESPACE_START
m_vErrMsg[ecINVALID_TYPES_MATCH] = _T("Both values of the default(x, y) function should have the same type");
m_vErrMsg[ecUKNOWN_LOCALE] = _T("The chosen locale is not supported");
m_vErrMsg[ecINVALID_TIME_FORMAT] = _T("Invalid time format on parameter(s). Please use the \"HH:MM:SS\" format.");
m_vErrMsg[ecMISSING_CASE_SEPARATOR] = _T("Missing \";\" separator. Please separate the comparison from the result with \";\"");
m_vErrMsg[ecMISSING_CASE_DEFAULT] = _T("Missing default in case operator. Please make sure case has a default or that one or more of the conditions are true");
m_vErrMsg[ecNOT_BOOL_CASE] = _T("Case comparison not resulting in true or false");


#if defined(_DEBUG)
for (int i=0; i<ecCOUNT; ++i)
Expand Down
4 changes: 4 additions & 0 deletions value_test/mpError.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ MUP_NAMESPACE_START
// time related errors
ecINVALID_TIME_FORMAT = 60, ///< Invalid time format

ecMISSING_CASE_SEPARATOR = 61, ///< Missing ; case separator
ecMISSING_CASE_DEFAULT = 62, ///< Missing default in case operation
ecNOT_BOOL_CASE = 63, ///< Case comparison not resulting in boolean

// The last two are special entries
ecCOUNT, ///< This is no error code, It just stores just the total number of error codes
ecUNDEFINED = -1, ///< Undefined message, placeholder to detect unassigned error messages
Expand Down