diff --git a/c/dsl/function_manager.c b/c/dsl/function_manager.c index 663a5b0c3..714e19b05 100644 --- a/c/dsl/function_manager.c +++ b/c/dsl/function_manager.c @@ -232,6 +232,7 @@ static function_lookup_t FUNCTION_LOOKUP_TABLE[] = { {FUNC_CLASS_STRING, ".", 2,0, "String concatenation."}, {FUNC_CLASS_STRING, "gsub", 3,0, "Example: '$name=gsub($name, \"old\", \"new\")'\n(replace all)."}, {FUNC_CLASS_STRING, "regextract", 2,0, "Example: '$name=regextract($name, \"[A-Z]{3}[0-9]{2}\")'\n."}, + {FUNC_CLASS_STRING, "regextract_or_else", 2,0, "Example: '$name=regextract_or_else($name, \"[A-Z]{3}[0-9]{2}\", \"default\")'\n."}, {FUNC_CLASS_STRING, "strlen", 1,0, "String length."}, {FUNC_CLASS_STRING, "sub", 3,0, "Example: '$name=sub($name, \"old\", \"new\")'\n(replace once)."}, {FUNC_CLASS_STRING, "ssub", 3,0, "Like sub but does no regexing. No characters are special."}, @@ -989,7 +990,8 @@ static rval_evaluator_t* construct_builtin_function_callsite_evaluator( int is_regexy = streq(function_name, "sub") || - streq(function_name, "gsub"); + streq(function_name, "gsub") || + streq(function_name, "regextract_or_else"); if (is_regexy && type2 == MD_AST_NODE_TYPE_STRING_LITERAL) { // sub/gsub-regex special case: @@ -1275,6 +1277,8 @@ static rval_evaluator_t* fmgr_alloc_evaluator_from_ternary_func_name(char* fnnm, return rval_evaluator_alloc_from_s_sss_func(gsub_no_precomp_func, parg1, parg2, parg3); } else if (streq(fnnm, "ssub")) { return rval_evaluator_alloc_from_s_sss_func(s_sss_ssub_func, parg1, parg2, parg3); + } else if (streq(fnnm, "regextract_or_else")) { + return rval_evaluator_alloc_from_s_sss_func(regextract_or_else_no_precomp_func, parg1, parg2, parg3); } else if (streq(fnnm, "logifit")) { return rval_evaluator_alloc_from_f_fff_func(f_fff_logifit_func, parg1, parg2, parg3); } else if (streq(fnnm, "madd")) { @@ -1299,6 +1303,8 @@ static rval_evaluator_t* fmgr_alloc_evaluator_from_ternary_regex_arg2_func_name( return rval_evaluator_alloc_from_x_srs_func(sub_precomp_func, parg1, regex_string, ignore_case, parg3); } else if (streq(fnnm, "gsub")) { return rval_evaluator_alloc_from_x_srs_func(gsub_precomp_func, parg1, regex_string, ignore_case, parg3); + } else if (streq(fnnm, "regextract_or_else")) { + return rval_evaluator_alloc_from_x_ses_func(regextract_or_else_precomp_func, parg1, regex_string, ignore_case, parg3); } else { return NULL; } } diff --git a/c/dsl/rval_evaluators.h b/c/dsl/rval_evaluators.h index 162e7a277..214127095 100644 --- a/c/dsl/rval_evaluators.h +++ b/c/dsl/rval_evaluators.h @@ -177,6 +177,9 @@ rval_evaluator_t* rval_evaluator_alloc_from_s_sss_func(mv_ternary_func_t* pfunc, rval_evaluator_t* rval_evaluator_alloc_from_x_srs_func(mv_ternary_arg2_regex_func_t* pfunc, rval_evaluator_t* parg1, char* regex_string, int ignore_case, rval_evaluator_t* parg3); +rval_evaluator_t* rval_evaluator_alloc_from_x_ses_func(mv_ternary_arg2_regextract_func_t* pfunc, + rval_evaluator_t* parg1, char* regex_string, int ignore_case, rval_evaluator_t* parg3); + // ================================================================ // rval_list_evaluators.c // ================================================================ diff --git a/c/dsl/rval_func_evaluators.c b/c/dsl/rval_func_evaluators.c index 6c02399c5..248a79c48 100644 --- a/c/dsl/rval_func_evaluators.c +++ b/c/dsl/rval_func_evaluators.c @@ -1359,3 +1359,59 @@ rval_evaluator_t* rval_evaluator_alloc_from_x_srs_func(mv_ternary_arg2_regex_fun return pevaluator; } + +// ---------------------------------------------------------------- +typedef struct _rval_evaluator_x_ses_state_t { + mv_ternary_arg2_regextract_func_t* pfunc; + rval_evaluator_t* parg1; + regex_t regex; + rval_evaluator_t* parg3; +} rval_evaluator_x_ses_state_t; + +static mv_t rval_evaluator_x_ses_func(void* pvstate, variables_t* pvars) { + rval_evaluator_x_ses_state_t* pstate = pvstate; + + mv_t val1 = pstate->parg1->pprocess_func(pstate->parg1->pvstate, pvars); + NULL_OR_ERROR_OUT_FOR_STRINGS(val1); + if (!mv_is_string_or_empty(&val1)) + return mv_error(); + + mv_t val3 = pstate->parg3->pprocess_func(pstate->parg3->pvstate, pvars); + NULL_OR_ERROR_OUT_FOR_STRINGS(val3); + if (!mv_is_string_or_empty(&val3)) { + mv_free(&val3); + return mv_error(); + } + + return pstate->pfunc(&val1, &pstate->regex, &val3); +} + +static void rval_evaluator_x_ses_free(rval_evaluator_t* pevaluator) { + rval_evaluator_x_ses_state_t* pstate = pevaluator->pvstate; + pstate->parg1->pfree_func(pstate->parg1); + regfree(&pstate->regex); + pstate->parg3->pfree_func(pstate->parg3); + free(pstate); + free(pevaluator); +} + +rval_evaluator_t* rval_evaluator_alloc_from_x_ses_func(mv_ternary_arg2_regextract_func_t* pfunc, + rval_evaluator_t* parg1, char* regex_string, int ignore_case, rval_evaluator_t* parg3) +{ + rval_evaluator_x_ses_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_x_ses_state_t)); + pstate->pfunc = pfunc; + + pstate->parg1 = parg1; + + int cflags = ignore_case ? REG_ICASE : 0; + regcomp_or_die(&pstate->regex, regex_string, cflags); + + pstate->parg3 = parg3; + + rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t)); + pevaluator->pvstate = pstate; + pevaluator->pprocess_func = rval_evaluator_x_ses_func; + pevaluator->pfree_func = rval_evaluator_x_ses_free; + + return pevaluator; +} diff --git a/c/lib/mvfuncs.h b/c/lib/mvfuncs.h index b486cb4ba..f3a7c3ea7 100644 --- a/c/lib/mvfuncs.h +++ b/c/lib/mvfuncs.h @@ -49,6 +49,7 @@ typedef mv_t mv_binary_arg2_regex_func_t(mv_t* pval1, regex_t* pregex, string_bu typedef mv_t mv_binary_arg2_regextract_func_t(mv_t* pval1, regex_t* pregex); typedef mv_t mv_ternary_func_t(mv_t* pval1, mv_t* pval2, mv_t* pval3); typedef mv_t mv_ternary_arg2_regex_func_t(mv_t* pval1, regex_t* pregex, string_builder_t* psb, mv_t* pval3); +typedef mv_t mv_ternary_arg2_regextract_func_t(mv_t* pval1, regex_t* pregex, mv_t* pval3); // ---------------------------------------------------------------- static inline mv_t b_b_not_func(mv_t* pval1) {