From fbf92524bb2bf8114ecddd68f48d3d94d7f24c8d Mon Sep 17 00:00:00 2001 From: John Kerl Date: Mon, 27 Jul 2015 09:23:26 -0400 Subject: [PATCH] nullable min/max --- c/mapping/lrec_evaluators.c | 36 ++++++++++++++++++++++++++++++++++-- c/mapping/mlr_val.c | 31 +++++++++++++++++++++++++++++++ c/mapping/mlr_val.h | 26 ++++++++++++++++++++++---- c/test/expected/out | 20 ++++++++++++++++++++ c/test/input/minmax.dkvp | 8 ++++++++ c/test/output/out | 20 ++++++++++++++++++++ c/test/run | 3 +++ 7 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 c/test/input/minmax.dkvp diff --git a/c/mapping/lrec_evaluators.c b/c/mapping/lrec_evaluators.c index 05e89e16b..0d668bff5 100644 --- a/c/mapping/lrec_evaluators.c +++ b/c/mapping/lrec_evaluators.c @@ -167,6 +167,38 @@ lrec_evaluator_t* lrec_evaluator_alloc_from_f_ff_func(mv_binary_func_t* pfunc, return pevaluator; } +mv_t lrec_evaluator_f_ff_nullable_func(lrec_t* prec, context_t* pctx, void* pvstate) { + lrec_evaluator_f_ff_state_t* pstate = pvstate; + mv_t val1 = pstate->parg1->pevaluator_func(prec, pctx, pstate->parg1->pvstate); + ERROR_OUT(val1); + mt_get_double_nullable(&val1); + if (val1.type != MT_DOUBLE && val1.type != MT_NULL) + return MV_ERROR; + + mv_t val2 = pstate->parg2->pevaluator_func(prec, pctx, pstate->parg2->pvstate); + ERROR_OUT(val2); + mt_get_double_nullable(&val2); + if (val2.type != MT_DOUBLE && val2.type != MT_NULL) + return MV_ERROR; + + return pstate->pfunc(&val1, &val2); +} + +lrec_evaluator_t* lrec_evaluator_alloc_from_f_ff_nullable_func(mv_binary_func_t* pfunc, + lrec_evaluator_t* parg1, lrec_evaluator_t* parg2) +{ + lrec_evaluator_f_ff_state_t* pstate = mlr_malloc_or_die(sizeof(lrec_evaluator_f_ff_state_t)); + pstate->pfunc = pfunc; + pstate->parg1 = parg1; + pstate->parg2 = parg2; + + lrec_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(lrec_evaluator_t)); + pevaluator->pvstate = pstate; + pevaluator->pevaluator_func = lrec_evaluator_f_ff_nullable_func; + + return pevaluator; +} + // ---------------------------------------------------------------- typedef struct _lrec_evaluator_s_s_state_t { mv_unary_func_t* pfunc; @@ -889,8 +921,8 @@ lrec_evaluator_t* lrec_evaluator_alloc_from_binary_func_name(char* fnnm, } else if (streq(fnnm, "<")) { return lrec_evaluator_alloc_from_b_xx_func(lt_op_func, parg1, parg2); } else if (streq(fnnm, "<=")) { return lrec_evaluator_alloc_from_b_xx_func(le_op_func, parg1, parg2); } else if (streq(fnnm, ".")) { return lrec_evaluator_alloc_from_s_ss_func(s_ss_dot_func, parg1, parg2); - } else if (streq(fnnm, "max")) { return lrec_evaluator_alloc_from_f_ff_func(f_ff_max_func, parg1, parg2); - } else if (streq(fnnm, "min")) { return lrec_evaluator_alloc_from_f_ff_func(f_ff_min_func, parg1, parg2); + } else if (streq(fnnm, "max")) { return lrec_evaluator_alloc_from_f_ff_nullable_func(f_ff_max_func, parg1, parg2); + } else if (streq(fnnm, "min")) { return lrec_evaluator_alloc_from_f_ff_nullable_func(f_ff_min_func, parg1, parg2); } else if (streq(fnnm, "pow")) { return lrec_evaluator_alloc_from_f_ff_func(f_ff_pow_func, parg1, parg2); } else if (streq(fnnm, "+")) { return lrec_evaluator_alloc_from_f_ff_func(f_ff_plus_func, parg1, parg2); } else if (streq(fnnm, "-")) { return lrec_evaluator_alloc_from_f_ff_func(f_ff_minus_func, parg1, parg2); diff --git a/c/mapping/mlr_val.c b/c/mapping/mlr_val.c index 98724a86f..84a3c43f6 100644 --- a/c/mapping/mlr_val.c +++ b/c/mapping/mlr_val.c @@ -115,6 +115,37 @@ void mt_get_double_strict(mv_t* pval) { // xxx else panic } +// ---------------------------------------------------------------- +// xxx merge with mt_get_double_string w/ a nullable parameter +void mt_get_double_nullable(mv_t* pval) { + if (pval->type == MT_NULL) + return; + if (pval->type == MT_ERROR) + return; + if (pval->type == MT_DOUBLE) + return; + if (pval->type == MT_STRING) { + double dblv; + if (*pval->u.strv == '\0') { + pval->type = MT_NULL; + pval->u.intv = 0; + } else if (!mlr_try_double_from_string(pval->u.strv, &dblv)) { + pval->type = MT_ERROR; + pval->u.intv = 0; + } else { + pval->type = MT_DOUBLE; + pval->u.dblv = dblv; + } + } else if (pval->type == MT_INT) { + pval ->type = MT_DOUBLE; + pval->u.dblv = (double)pval->u.intv; + } else if (pval->type == MT_BOOL) { + pval->type = MT_ERROR; + pval->u.intv = 0; + } + // xxx else panic +} + // ---------------------------------------------------------------- mv_t s_ss_dot_func(mv_t* pval1, mv_t* pval2) { int len1 = strlen(pval1->u.strv); diff --git a/c/mapping/mlr_val.h b/c/mapping/mlr_val.h index 2d302b78f..c646d1239 100644 --- a/c/mapping/mlr_val.h +++ b/c/mapping/mlr_val.h @@ -44,6 +44,11 @@ extern mv_t MV_ERROR; return MV_NULL; \ } +#define ERROR_OUT(val) { \ + if ((val).type == MT_ERROR) \ + return MV_ERROR; \ +} + // ---------------------------------------------------------------- char* mt_describe_type(int type); @@ -53,6 +58,7 @@ char* mt_describe_val(mv_t val); // xxx explain why one is void & the other isn't int mt_get_boolean_strict(mv_t* pval); void mt_get_double_strict(mv_t* pval); +void mt_get_double_nullable(mv_t* pval); // ---------------------------------------------------------------- typedef mv_t mv_zary_func_t(); @@ -137,12 +143,24 @@ static inline mv_t f_ff_divide_func(mv_t* pval1, mv_t* pval2) { return rv; } static inline mv_t f_ff_max_func(mv_t* pval1, mv_t* pval2) { - mv_t rv = {.type = MT_DOUBLE, .u.dblv = fmax(pval1->u.dblv, pval2->u.dblv)}; // xxx impl: null loses ... - return rv; + if (pval1->type == MT_NULL) { + return *pval2; + } else if (pval2->type == MT_NULL) { + return *pval1; + } else { + mv_t rv = {.type = MT_DOUBLE, .u.dblv = fmax(pval1->u.dblv, pval2->u.dblv)}; + return rv; + } } static inline mv_t f_ff_min_func(mv_t* pval1, mv_t* pval2) { - mv_t rv = {.type = MT_DOUBLE, .u.dblv = fmin(pval1->u.dblv, pval2->u.dblv)}; // xxx impl: null loses ... - return rv; + if (pval1->type == MT_NULL) { + return *pval2; + } else if (pval2->type == MT_NULL) { + return *pval1; + } else { + mv_t rv = {.type = MT_DOUBLE, .u.dblv = fmin(pval1->u.dblv, pval2->u.dblv)}; + return rv; + } } static inline mv_t f_ff_pow_func(mv_t* pval1, mv_t* pval2) { mv_t rv = {.type = MT_DOUBLE, .u.dblv = pow(pval1->u.dblv, pval2->u.dblv)}; diff --git a/c/test/expected/out b/c/test/expected/out index e21a5a4a3..8506fca4d 100644 --- a/c/test/expected/out +++ b/c/test/expected/out @@ -1517,6 +1517,26 @@ gmt,sec 2017-07-14T02:40:00Z,1500000000 2033-05-18T03:33:20Z,2000000000 +./test/../mlr put $z=min($x, $y) ./test/input/minmax.dkvp +x=1,y=2,z=1.000000 +x=1,y=,z=1.000000 +x=,y=,z= +x=,y=2,z=2.000000 +x=3,y=2,z=2.000000 +x=3,y=,z=3.000000 +x=,y=,z= +x=,y=2,z=2.000000 + +./test/../mlr put $z=max($x, $y) ./test/input/minmax.dkvp +x=1,y=2,z=2.000000 +x=1,y=,z=1.000000 +x=,y=,z= +x=,y=2,z=2.000000 +x=3,y=2,z=3.000000 +x=3,y=,z=3.000000 +x=,y=,z= +x=,y=2,z=2.000000 + ================================================================ CHAINING diff --git a/c/test/input/minmax.dkvp b/c/test/input/minmax.dkvp new file mode 100644 index 000000000..725ef2e5f --- /dev/null +++ b/c/test/input/minmax.dkvp @@ -0,0 +1,8 @@ +x=1,y=2 +x=1,y= +x=,y= +x=,y=2 +x=3,y=2 +x=3,y= +x=,y= +x=,y=2 diff --git a/c/test/output/out b/c/test/output/out index e21a5a4a3..8506fca4d 100644 --- a/c/test/output/out +++ b/c/test/output/out @@ -1517,6 +1517,26 @@ gmt,sec 2017-07-14T02:40:00Z,1500000000 2033-05-18T03:33:20Z,2000000000 +./test/../mlr put $z=min($x, $y) ./test/input/minmax.dkvp +x=1,y=2,z=1.000000 +x=1,y=,z=1.000000 +x=,y=,z= +x=,y=2,z=2.000000 +x=3,y=2,z=2.000000 +x=3,y=,z=3.000000 +x=,y=,z= +x=,y=2,z=2.000000 + +./test/../mlr put $z=max($x, $y) ./test/input/minmax.dkvp +x=1,y=2,z=2.000000 +x=1,y=,z=1.000000 +x=,y=,z= +x=,y=2,z=2.000000 +x=3,y=2,z=3.000000 +x=3,y=,z=3.000000 +x=,y=,z= +x=,y=2,z=2.000000 + ================================================================ CHAINING diff --git a/c/test/run b/c/test/run index 6ce99c16a..1786287fc 100755 --- a/c/test/run +++ b/c/test/run @@ -269,6 +269,9 @@ run_command $mlr filter -v '$x<$a*$b+-$c' /dev/null run_command $mlr --csv put '$gmt=sec2gmt($sec)' $indir/sec2gmt run_command $mlr --csv put '$sec=gmt2sec($gmt)' $indir/gmt2sec +run_command $mlr put '$z=min($x, $y)' $indir/minmax.dkvp +run_command $mlr put '$z=max($x, $y)' $indir/minmax.dkvp + # ---------------------------------------------------------------- announce CHAINING