vim-patch:8.2.1794: no falsy Coalescing operator

Problem:    No falsy Coalescing operator.
Solution:   Add the "??" operator.  Fix mistake with function argument count.

92f26c256e

Cherry-pick tv2bool() into eval/typval.c.
Cherry-pick *??* tag from Vim runtime.

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq
2023-04-14 11:00:17 +08:00
parent 7caf0eafd8
commit d6e2804ab4
5 changed files with 137 additions and 31 deletions

View File

@@ -2336,6 +2336,7 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
/// Handle top level expression:
/// expr2 ? expr1 : expr1
/// expr2 ?? expr1
///
/// "arg" must point to the first non-white of the expression.
/// "arg" is advanced to the next non-white after the recognized expression.
@@ -2352,6 +2353,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
char *p = *arg;
if (*p == '?') {
const bool op_falsy = p[1] == '?';
evalarg_T *evalarg_used = evalarg;
evalarg_T local_evalarg;
if (evalarg == NULL) {
@@ -2365,49 +2367,62 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
if (evaluate) {
bool error = false;
if (tv_get_number_chk(rettv, &error) != 0) {
if (op_falsy) {
result = tv2bool(rettv);
} else if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
tv_clear(rettv);
if (error || !op_falsy || !result) {
tv_clear(rettv);
}
if (error) {
return FAIL;
}
}
// Get the second variable. Recursive!
*arg = skipwhite(*arg + 1);
evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
if (eval1(arg, rettv, evalarg_used) == FAIL) {
evalarg_used->eval_flags = orig_flags;
return FAIL;
if (op_falsy) {
(*arg)++;
}
// Check for the ":".
p = *arg;
if (*p != ':') {
emsg(_("E109: Missing ':' after '?'"));
if (evaluate && result) {
tv_clear(rettv);
}
evalarg_used->eval_flags = orig_flags;
return FAIL;
}
// Get the third variable. Recursive!
*arg = skipwhite(*arg + 1);
evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
evalarg_used->eval_flags = (op_falsy ? !result : result)
? orig_flags : orig_flags & ~EVAL_EVALUATE;
typval_T var2;
if (eval1(arg, &var2, evalarg_used) == FAIL) {
if (evaluate && result) {
tv_clear(rettv);
}
evalarg_used->eval_flags = orig_flags;
return FAIL;
}
if (evaluate && !result) {
if (!op_falsy || !result) {
*rettv = var2;
}
if (!op_falsy) {
// Check for the ":".
p = *arg;
if (*p != ':') {
emsg(_("E109: Missing ':' after '?'"));
if (evaluate && result) {
tv_clear(rettv);
}
evalarg_used->eval_flags = orig_flags;
return FAIL;
}
// Get the third variable. Recursive!
*arg = skipwhite(*arg + 1);
evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
if (eval1(arg, &var2, evalarg_used) == FAIL) {
if (evaluate && result) {
tv_clear(rettv);
}
evalarg_used->eval_flags = orig_flags;
return FAIL;
}
if (evaluate && !result) {
*rettv = var2;
}
}
if (evalarg == NULL) {
clear_evalarg(&local_evalarg, NULL);
} else {

View File

@@ -4201,3 +4201,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
return res != NULL ? res : "";
}
/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
/// list, etc. Mostly like what JavaScript does, except that empty list and
/// empty dictionary are false.
bool tv2bool(const typval_T *const tv)
{
switch (tv->v_type) {
case VAR_NUMBER:
return tv->vval.v_number != 0;
case VAR_FLOAT:
return tv->vval.v_float != 0.0;
case VAR_PARTIAL:
return tv->vval.v_partial != NULL;
case VAR_FUNC:
case VAR_STRING:
return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
case VAR_LIST:
return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
case VAR_DICT:
return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
case VAR_BOOL:
return tv->vval.v_bool == kBoolVarTrue;
case VAR_SPECIAL:
return tv->vval.v_special == kSpecialVarNull;
case VAR_BLOB:
return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
case VAR_UNKNOWN:
break;
}
return false;
}