From 3e0d769cbcc56ef6c3882a1e65f06b7add93be07 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:01:04 -0300 Subject: [PATCH 01/11] Fix bad description of weekday --- parser/mpFuncCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/mpFuncCommon.h b/parser/mpFuncCommon.h index 7f8406a..d80ec49 100644 --- a/parser/mpFuncCommon.h +++ b/parser/mpFuncCommon.h @@ -225,7 +225,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 From e0167b30f1ccaf502aa8bc70ea2b9775ce9974cd Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:07:40 -0300 Subject: [PATCH 02/11] Add helper function PureCalc to equations parser --- parser/equationsParser.cpp | 7 +++++++ parser/equationsParser.h | 1 + 2 files changed, 8 insertions(+) diff --git a/parser/equationsParser.cpp b/parser/equationsParser.cpp index a053cc2..707594d 100644 --- a/parser/equationsParser.cpp +++ b/parser/equationsParser.cpp @@ -137,4 +137,11 @@ void CalcArray(vector equations, vector &out) { } } +Value PureCalc(string input) { + Value result; + ParserX parser(pckALL_NON_COMPLEX); + parser.SetExpr(input); + return parser.Eval(); +} + EQUATIONS_PARSER_END diff --git a/parser/equationsParser.h b/parser/equationsParser.h index 7ea6317..fe163e6 100644 --- a/parser/equationsParser.h +++ b/parser/equationsParser.h @@ -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 in, std::vector &out); +mup::Value PureCalc(std::string input); EQUATIONS_PARSER_END From 086a184609f3b0e96264094388c4067ee6ff4a51 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:08:16 -0300 Subject: [PATCH 03/11] Add case function --- parser/mpFuncCommon.cpp | 68 ++++++++++++++++++++++++++++++++++++++ parser/mpFuncCommon.h | 13 ++++++++ parser/mpPackageCommon.cpp | 1 + 3 files changed, 82 insertions(+) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index e342f12..7e05733 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -43,6 +43,7 @@ #include "mpValue.h" #include "mpParserBase.h" +#include "equationsParser.h" #define ONE_DAY (24 * 60 * 60) @@ -399,6 +400,73 @@ MUP_NAMESPACE_START return new FunMask(*this); } + //------------------------------------------------------------------------------ + // + // Case + // + //------------------------------------------------------------------------------ + + 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) { + bool comparison_result; + if(operand[0] == '@'){ + Value result = EquationsParser::PureCalc(variable + operand.substr(1)); + if(result.GetType() != 'b'){ + //add error here + } + 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; + for(int i=1; i < a_iArgc ; i++) { + case_arguments = a_pArg[i]->GetString(); + size_t pos = case_arguments.find(';'); + if(pos == std::string::npos || pos == 0 || pos == case_arguments.size()){ + //add error here + } + operand = case_arguments.substr(0, pos); + result = case_arguments.substr(pos + 1); + if(compare_variable_to_operand(variable, operand) || operand.compare("default") == 0){ + *ret = get_case_value(result); + break; + } + } + } + + const char_type* FunCase::GetDesc() const + { + return _T("case(date) - Returns the week day of the date."); + } + + ////------------------------------------------------------------------------------ + IToken* FunCase::Clone() const + { + return new FunCase(*this); + } //------------------------------------------------------------------------------ // | // Below we have the section related to Date functions | diff --git a/parser/mpFuncCommon.h b/parser/mpFuncCommon.h index d80ec49..86e23ee 100644 --- a/parser/mpFuncCommon.h +++ b/parser/mpFuncCommon.h @@ -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 diff --git a/parser/mpPackageCommon.cpp b/parser/mpPackageCommon.cpp index d3cb474..4248b89 100644 --- a/parser/mpPackageCommon.cpp +++ b/parser/mpPackageCommon.cpp @@ -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()); From 067eb74f5cf3b00dbc30b03bc59e032e263f33c8 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:15:29 -0300 Subject: [PATCH 04/11] Add description of usage --- parser/mpFuncCommon.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index 7e05733..6c5597a 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -400,11 +400,14 @@ MUP_NAMESPACE_START return new FunMask(*this); } - //------------------------------------------------------------------------------ - // - // Case - // - //------------------------------------------------------------------------------ + //---------------------------------------------------------------------------------- + // | + // 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) From d87fcb7443ded8b8a05b2acf7239d7a6ae98b87d Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:18:13 -0300 Subject: [PATCH 05/11] add missing class name in description --- parser/mpFuncCommon.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index 6c5597a..1c7d260 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -402,6 +402,7 @@ MUP_NAMESPACE_START //---------------------------------------------------------------------------------- // | + // Class FunCase | // Function calculate custom switch case | // Usage: case("variable", "exp1;res1", "exp2;res2", "default;defres") | // Optional: '@' token in exp = evaluate(variable + exp1) | From e3a7e3d6653fcdf8f5fb4020861e148f03480a47 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Wed, 25 Sep 2024 01:19:09 -0300 Subject: [PATCH 06/11] Fix copied description --- parser/mpFuncCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index 1c7d260..ec68a88 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -463,7 +463,7 @@ MUP_NAMESPACE_START const char_type* FunCase::GetDesc() const { - return _T("case(date) - Returns the week day of the date."); + return _T("case(variable, expression_list) - Returns the result of a switch case on variable."); } ////------------------------------------------------------------------------------ From 7bea921f399e003a534962a84339f99549bed407 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Sun, 29 Sep 2024 16:21:46 -0300 Subject: [PATCH 07/11] move throw error to beggining of file --- parser/mpFuncCommon.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index ec68a88..3c91451 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -49,6 +49,15 @@ 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 @@ -521,15 +530,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 = ""; From 2d29fe79a3cf6e0211c7b0ebf3c1c5cee807af1d Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Sun, 29 Sep 2024 16:23:05 -0300 Subject: [PATCH 08/11] Add errors --- parser/mpFuncCommon.cpp | 28 ++++++++++++++++++++++------ parser/mpParserMessageProvider.cpp | 3 +++ parser/mpTypes.h | 4 ++++ value_test/mpError.cpp | 4 ++++ value_test/mpError.h | 4 ++++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/parser/mpFuncCommon.cpp b/parser/mpFuncCommon.cpp index 3c91451..34196c8 100644 --- a/parser/mpFuncCommon.cpp +++ b/parser/mpFuncCommon.cpp @@ -433,12 +433,12 @@ MUP_NAMESPACE_START return result; } - bool compare_variable_to_operand(string_type variable, string_type operand) { + 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'){ - //add error here + raise_error(ecNOT_BOOL_CASE, err_pos, a_pArg); } comparison_result = result.GetBool(); } else { @@ -455,17 +455,33 @@ MUP_NAMESPACE_START string_type variable = a_pArg[0]->GetString(); string_type operand, result, case_arguments; - for(int i=1; i < a_iArgc ; i++) { + 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()){ - //add error here + 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) || operand.compare("default") == 0){ + if(compare_variable_to_operand(variable, operand, i, a_pArg)) { *ret = get_case_value(result); - break; + 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); } } } diff --git a/parser/mpParserMessageProvider.cpp b/parser/mpParserMessageProvider.cpp index 73995cc..7a40c0e 100644 --- a/parser/mpParserMessageProvider.cpp +++ b/parser/mpParserMessageProvider.cpp @@ -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) diff --git a/parser/mpTypes.h b/parser/mpTypes.h index edcfbc3..219b848 100644 --- a/parser/mpTypes.h +++ b/parser/mpTypes.h @@ -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 diff --git a/value_test/mpError.cpp b/value_test/mpError.cpp index eb0d26d..90aa9b4 100644 --- a/value_test/mpError.cpp +++ b/value_test/mpError.cpp @@ -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 Date: Sun, 29 Sep 2024 16:23:17 -0300 Subject: [PATCH 09/11] Add missing tests --- tests.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests.sh b/tests.sh index c7c03f0..24545e5 100755 --- a/tests.sh +++ b/tests.sh @@ -326,5 +326,27 @@ 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", "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' + +# 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 ";"' + echo "All tests passed!" From c9fd9a55cd876049545f0a17c36e2a2a1d6a772a Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Sun, 29 Sep 2024 16:43:58 -0300 Subject: [PATCH 10/11] add missing tests --- tests.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests.sh b/tests.sh index 24545e5..89bab37 100755 --- a/tests.sh +++ b/tests.sh @@ -330,6 +330,7 @@ test_eval 'weekday("2024-09-17", "en", "one too many params")' 'Too many paramet # 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' @@ -341,6 +342,14 @@ 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' + # 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' From 6ec2d53bc2c1bcd3dbe172b8dd80e1819d89fd67 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Seifert Date: Sun, 29 Sep 2024 16:50:10 -0300 Subject: [PATCH 11/11] caseception --- tests.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests.sh b/tests.sh index 89bab37..9146225 100755 --- a/tests.sh +++ b/tests.sh @@ -349,6 +349,8 @@ 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'