diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 646 |
1 files changed, 369 insertions, 277 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8a1556320c..05d429c7d5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -176,7 +176,6 @@ static struct vimvar { VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_REG, "register", VAR_STRING, VV_RO), VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), @@ -211,13 +210,9 @@ static struct vimvar { VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), VV(VV_FALSE, "false", VAR_BOOL, VV_RO), VV(VV_TRUE, "true", VAR_BOOL, VV_RO), VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), VV(VV_TESTING, "testing", VAR_NUMBER, 0), VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), @@ -227,10 +222,17 @@ static struct vimvar { VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + // Neovim + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), }; #undef VV @@ -365,7 +367,7 @@ void eval_init(void) eval_msgpack_type_lists[i] = type_list; if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. - assert(false); + abort(); } } msgpack_types_dict->dv_lock = VAR_FIXED; @@ -455,14 +457,15 @@ void eval_clear(void) * Set an internal variable to a string value. Creates the variable if it does * not already exist. */ -void set_internal_string_var(char_u *name, char_u *value) +void set_internal_string_var(const char *name, char_u *value) + FUNC_ATTR_NONNULL_ARG(1) { - const typval_T tv = { + typval_T tv = { .v_type = VAR_STRING, .vval.v_string = value, }; - set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); + set_var(name, strlen(name), &tv, true); } static lval_T *redir_lval = NULL; @@ -522,9 +525,9 @@ var_redir_start( tv.v_type = VAR_STRING; tv.vval.v_string = (char_u *)""; if (append) { - set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"."); + set_var_lval(redir_lval, redir_endp, &tv, true, false, "."); } else { - set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"="); + set_var_lval(redir_lval, redir_endp, &tv, true, false, "="); } clear_lval(redir_lval); err = did_emsg; @@ -584,7 +587,7 @@ void var_redir_stop(void) redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, 0, FNE_CHECK_START); if (redir_endp != NULL && redir_lval->ll_name != NULL) { - set_var_lval(redir_lval, redir_endp, &tv, false, false, (char_u *)"."); + set_var_lval(redir_lval, redir_endp, &tv, false, false, "."); } clear_lval(redir_lval); } @@ -914,6 +917,17 @@ varnumber_T eval_to_number(char_u *expr) return retval; } +// Top level evaluation function. +// Returns an allocated typval_T with the result. +// Returns NULL when there is an error. +typval_T *eval_expr(char_u *arg) +{ + typval_T *tv = xmalloc(sizeof(*tv)); + if (eval0(arg, tv, NULL, true) == FAIL) { + XFREE_CLEAR(tv); + } + return tv; +} /* * Prepare v: variable "idx" to be used. @@ -1847,7 +1861,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, s = tv_get_string_chk(tv); // != NULL if number or string. } if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_type = get_option_value((char *)arg, &numval, (char_u **)&stringval, opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { @@ -1924,7 +1938,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); } else { - set_var_lval(&lv, p, tv, copy, is_const, op); + set_var_lval(&lv, p, tv, copy, is_const, (const char *)op); arg_end = p; } } @@ -2121,9 +2135,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } } - lp->ll_range = TRUE; - } else - lp->ll_range = FALSE; + lp->ll_range = true; + } else { + lp->ll_range = false; + } if (*p != ']') { if (!quiet) { @@ -2240,12 +2255,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - /* - * May need to find the item or absolute index for the second - * index of a range. - * When no index given: "lp->ll_empty2" is TRUE. - * Otherwise "lp->ll_n2" is set to the second index. - */ + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. tv_clear(&var2); @@ -2299,7 +2312,7 @@ void clear_lval(lval_T *lp) * "%" for "%=", "." for ".=" or "=" for "=". */ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, - int copy, const bool is_const, const char_u *op) + int copy, const bool is_const, const char *op) { int cc; listitem_T *ri; @@ -2326,7 +2339,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, TV_CSTRING) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, TV_CSTRING))) - && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { + && eexe_mod_op(&tv, rettv, op) == OK) { set_var(lp->ll_name, lp->ll_name_len, &tv, false); } tv_clear(&tv); @@ -2369,8 +2382,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, */ for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { if (op != NULL && *op != '=') { - eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), - (const char *)op); + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); } else { tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); @@ -2428,7 +2440,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, } if (op != NULL && *op != '=') { - eexe_mod_op(lp->ll_tv, rettv, (const char *)op); + eexe_mod_op(lp->ll_tv, rettv, op); goto notify; } else { tv_clear(lp->ll_tv); @@ -3007,7 +3019,8 @@ static size_t varnamebuflen = 0; /* * Function to concatenate a prefix and a variable name. */ -static char_u *cat_prefix_varname(int prefix, char_u *name) +char_u *cat_prefix_varname(int prefix, const char_u *name) + FUNC_ATTR_NONNULL_ALL { size_t len = STRLEN(name) + 3; @@ -3128,21 +3141,6 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) return matches; } -/* - * types for expressions. - */ -typedef enum { - TYPE_UNKNOWN = 0, - TYPE_EQUAL, // == - TYPE_NEQUAL, // != - TYPE_GREATER, // > - TYPE_GEQUAL, // >= - TYPE_SMALLER, // < - TYPE_SEQUAL, // <= - TYPE_MATCH, // =~ - TYPE_NOMATCH, // !~ -} exptype_T; - // TODO(ZyX-I): move to eval/expressions /* @@ -3419,11 +3417,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; char_u *p; - int i; - exptype_T type = TYPE_UNKNOWN; - bool type_is = false; // true for "is" and "isnot" + exprtype_T type = EXPR_UNKNOWN; int len = 2; - varnumber_T n1, n2; bool ic; /* @@ -3434,35 +3429,42 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) p = *arg; switch (p[0]) { - case '=': if (p[1] == '=') - type = TYPE_EQUAL; - else if (p[1] == '~') - type = TYPE_MATCH; + case '=': + if (p[1] == '=') { + type = EXPR_EQUAL; + } else if (p[1] == '~') { + type = EXPR_MATCH; + } break; - case '!': if (p[1] == '=') - type = TYPE_NEQUAL; - else if (p[1] == '~') - type = TYPE_NOMATCH; + case '!': + if (p[1] == '=') { + type = EXPR_NEQUAL; + } else if (p[1] == '~') { + type = EXPR_NOMATCH; + } break; - case '>': if (p[1] != '=') { - type = TYPE_GREATER; + case '>': + if (p[1] != '=') { + type = EXPR_GREATER; len = 1; - } else - type = TYPE_GEQUAL; + } else { + type = EXPR_GEQUAL; + } break; - case '<': if (p[1] != '=') { - type = TYPE_SMALLER; + case '<': + if (p[1] != '=') { + type = EXPR_SMALLER; len = 1; - } else - type = TYPE_SEQUAL; + } else { + type = EXPR_SEQUAL; + } break; case 'i': if (p[1] == 's') { if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { len = 5; } if (!isalnum(p[len]) && p[len] != '_') { - type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL; - type_is = true; + type = len == 2 ? EXPR_IS : EXPR_ISNOT; } } break; @@ -3471,7 +3473,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) /* * If there is a comparative operator, use it. */ - if (type != TYPE_UNKNOWN) { + if (type != EXPR_UNKNOWN) { // extra question mark appended: ignore case if (p[len] == '?') { ic = true; @@ -3489,173 +3491,11 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) tv_clear(rettv); return FAIL; } - if (evaluate) { - if (type_is && rettv->v_type != var2.v_type) { - /* For "is" a different type always means FALSE, for "notis" - * it means TRUE. */ - n1 = (type == TYPE_NEQUAL); - } else if (rettv->v_type == VAR_LIST || var2.v_type == VAR_LIST) { - if (type_is) { - n1 = (rettv->v_type == var2.v_type - && rettv->vval.v_list == var2.vval.v_list); - if (type == TYPE_NEQUAL) - n1 = !n1; - } else if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - if (rettv->v_type != var2.v_type) { - EMSG(_("E691: Can only compare List with List")); - } else { - EMSG(_("E692: Invalid operation for List")); - } - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } else { - // Compare two Lists for being equal or unequal. - n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false); - if (type == TYPE_NEQUAL) { - n1 = !n1; - } - } - } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { - if (type_is) { - n1 = (rettv->v_type == var2.v_type - && rettv->vval.v_dict == var2.vval.v_dict); - if (type == TYPE_NEQUAL) - n1 = !n1; - } else if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - if (rettv->v_type != var2.v_type) - EMSG(_("E735: Can only compare Dictionary with Dictionary")); - else - EMSG(_("E736: Invalid operation for Dictionary")); - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } else { - // Compare two Dictionaries for being equal or unequal. - n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, false); - if (type == TYPE_NEQUAL) { - n1 = !n1; - } - } - } else if (tv_is_func(*rettv) || tv_is_func(var2)) { - if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { - EMSG(_("E694: Invalid operation for Funcrefs")); - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if ((rettv->v_type == VAR_PARTIAL - && rettv->vval.v_partial == NULL) - || (var2.v_type == VAR_PARTIAL - && var2.vval.v_partial == NULL)) { - // when a partial is NULL assume not equal - n1 = false; - } else if (type_is) { - if (rettv->v_type == VAR_FUNC && var2.v_type == VAR_FUNC) { - // strings are considered the same if their value is - // the same - n1 = tv_equal(rettv, &var2, ic, false); - } else if (rettv->v_type == VAR_PARTIAL - && var2.v_type == VAR_PARTIAL) { - n1 = (rettv->vval.v_partial == var2.vval.v_partial); - } else { - n1 = false; - } - } else { - n1 = tv_equal(rettv, &var2, ic, false); - } - if (type == TYPE_NEQUAL) { - n1 = !n1; - } - } - /* - * If one of the two variables is a float, compare as a float. - * When using "=~" or "!~", always compare as string. - */ - else if ((rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) - && type != TYPE_MATCH && type != TYPE_NOMATCH) { - float_T f1, f2; + const int ret = typval_compare(rettv, &var2, type, ic); - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - } else { - f1 = tv_get_number(rettv); - } - if (var2.v_type == VAR_FLOAT) { - f2 = var2.vval.v_float; - } else { - f2 = tv_get_number(&var2); - } - n1 = false; - switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; - } - } - /* - * If one of the two variables is a number, compare as a number. - * When using "=~" or "!~", always compare as string. - */ - else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) - && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = tv_get_number(rettv); - n2 = tv_get_number(&var2); - switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; - } - } else { - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = tv_get_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) { - i = mb_strcmp_ic(ic, s1, s2); - } else { - i = 0; - } - n1 = false; - switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: { - n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); - if (type == TYPE_NOMATCH) { - n1 = !n1; - } - break; - } - case TYPE_UNKNOWN: break; // Avoid gcc warning. - } - } - tv_clear(rettv); tv_clear(&var2); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + return ret; } } @@ -4538,7 +4378,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, c = *option_end; *option_end = NUL; - opt_type = get_option_value((char_u *)(*arg), &numval, + opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); if (opt_type == -3) { // invalid name @@ -5280,22 +5120,18 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, if (ht_stack == NULL) { abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); } else { - ht_stack_T *newitem = try_malloc(sizeof(ht_stack_T)); - if (newitem == NULL) { - abort = true; - } else { - newitem->ht = &dd->dv_hashtab; - newitem->prev = *ht_stack; - *ht_stack = newitem; - } + ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T)); + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; } QUEUE *w = NULL; DictWatcher *watcher = NULL; - QUEUE_FOREACH(w, &dd->watchers) { + QUEUE_FOREACH(w, &dd->watchers, { watcher = tv_dict_watcher_node_data(w); set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); - } + }) } break; } @@ -5308,14 +5144,10 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, if (list_stack == NULL) { abort = set_ref_in_list(ll, copyID, ht_stack); } else { - list_stack_T *newitem = try_malloc(sizeof(list_stack_T)); - if (newitem == NULL) { - abort = true; - } else { - newitem->list = ll; - newitem->prev = *list_stack; - *list_stack = newitem; - } + list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; } } break; @@ -5874,26 +5706,53 @@ int assert_inrange(typval_T *argvars) FUNC_ATTR_NONNULL_ALL { bool error = false; - const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); - const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); - const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); - if (error) { - return 0; - } - if (actual < lower || actual > upper) { - garray_T ga; - prepare_assert_error(&ga); + if (argvars[0].v_type == VAR_FLOAT + || argvars[1].v_type == VAR_FLOAT + || argvars[2].v_type == VAR_FLOAT) { + const float_T flower = tv_get_float(&argvars[0]); + const float_T fupper = tv_get_float(&argvars[1]); + const float_T factual = tv_get_float(&argvars[2]); - char msg[55]; - vim_snprintf(msg, sizeof(msg), - "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", - lower, upper); - fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], - ASSERT_INRANGE); - assert_error(&ga); - ga_clear(&ga); - return 1; + if (factual < flower || factual > fupper) { + garray_T ga; + prepare_assert_error(&ga); + if (argvars[3].v_type != VAR_UNKNOWN) { + char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL); + ga_concat(&ga, tofree); + xfree(tofree); + } else { + char msg[80]; + vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g", + flower, fupper, factual); + ga_concat(&ga, (char_u *)msg); + } + assert_error(&ga); + ga_clear(&ga); + return 1; + } + } else { + const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); + + if (error) { + return 0; + } + if (actual < lower || actual > upper) { + garray_T ga; + prepare_assert_error(&ga); + + char msg[55]; + vim_snprintf(msg, sizeof(msg), + "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", + lower, upper); + fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], + ASSERT_INRANGE); + assert_error(&ga); + ga_clear(&ga); + return 1; + } } return 0; } @@ -5961,6 +5820,35 @@ static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, } } +int assert_beeps(typval_T *argvars, bool no_beep) + FUNC_ATTR_NONNULL_ALL +{ + const char *const cmd = tv_get_string_chk(&argvars[0]); + int ret = 0; + + called_vim_beep = false; + suppress_errthrow = true; + emsg_silent = false; + do_cmdline_cmd(cmd); + if (no_beep ? called_vim_beep : !called_vim_beep) { + garray_T ga; + prepare_assert_error(&ga); + if (no_beep) { + ga_concat(&ga, (const char_u *)"command did beep: "); + } else { + ga_concat(&ga, (const char_u *)"command did not beep: "); + } + ga_concat(&ga, (const char_u *)cmd); + assert_error(&ga); + ga_clear(&ga); + ret = 1; + } + + suppress_errthrow = false; + emsg_on_display = false; + return ret; +} + int assert_fails(typval_T *argvars) FUNC_ATTR_NONNULL_ALL { @@ -6214,6 +6102,7 @@ void common_function(typval_T *argvars, typval_T *rettv, // function(dict.MyFunc, [arg]) arg_pt = argvars[0].vval.v_partial; s = partial_name(arg_pt); + // TODO(bfredl): do the entire nlua_is_table_from_lua dance } else { // function('MyFunc', [arg], dict) s = (char_u *)tv_get_string(&argvars[0]); @@ -7313,7 +7202,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) char_u *name = nlua_register_table_as_callable(arg); if (name != NULL) { - func_ref(name); callback->data.funcref = vim_strsave(name); callback->type = kCallbackFuncref; } else { @@ -7951,8 +7839,8 @@ int get_id_len(const char **const arg) */ int get_name_len(const char **const arg, char **alias, - int evaluate, - int verbose) + bool evaluate, + bool verbose) { int len; @@ -8437,10 +8325,8 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) return oldval; } -/* - * Get the value of internal variable "name". - * Return OK or FAIL. - */ +// Get the value of internal variable "name". +// Return OK or FAIL. If OK is returned "rettv" must be cleared. int get_var_tv( const char *name, int len, // length of "name" @@ -8508,7 +8394,7 @@ static bool tv_is_luafunc(typval_T *tv) int check_luafunc_name(const char *str, bool paren) { const char *p = str; - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.') { + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { p++; } if (*p != (paren ? '(' : NUL)) { @@ -10697,3 +10583,209 @@ bool invoke_prompt_interrupt(void) tv_clear(&rettv); return true; } + +// Compare "typ1" and "typ2". Put the result in "typ1". +int typval_compare( + typval_T *typ1, // first operand + typval_T *typ2, // second operand + exprtype_T type, // operator + bool ic // ignore case +) + FUNC_ATTR_NONNULL_ALL +{ + varnumber_T n1, n2; + const bool type_is = type == EXPR_IS || type == EXPR_ISNOT; + + if (type_is && typ1->v_type != typ2->v_type) { + // For "is" a different type always means false, for "notis" + // it means true. + n1 = type == EXPR_ISNOT; + } else if (typ1->v_type == VAR_LIST || typ2->v_type == VAR_LIST) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_list == typ2->vval.v_list; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + EMSG(_("E691: Can only compare List with List")); + } else { + EMSG(_("E692: Invalid operation for List")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (typ1->v_type == VAR_DICT || typ2->v_type == VAR_DICT) { + if (type_is) { + n1 = typ1->v_type == typ2->v_type + && typ1->vval.v_dict == typ2->vval.v_dict; + if (type == EXPR_ISNOT) { + n1 = !n1; + } + } else if (typ1->v_type != typ2->v_type + || (type != EXPR_EQUAL && type != EXPR_NEQUAL)) { + if (typ1->v_type != typ2->v_type) { + EMSG(_("E735: Can only compare Dictionary with Dictionary")); + } else { + EMSG(_("E736: Invalid operation for Dictionary")); + } + tv_clear(typ1); + return FAIL; + } else { + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false); + if (type == EXPR_NEQUAL) { + n1 = !n1; + } + } + } else if (tv_is_func(*typ1) || tv_is_func(*typ2)) { + if (type != EXPR_EQUAL && type != EXPR_NEQUAL + && type != EXPR_IS && type != EXPR_ISNOT) { + EMSG(_("E694: Invalid operation for Funcrefs")); + tv_clear(typ1); + return FAIL; + } + if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL) + || (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) { + // when a partial is NULL assume not equal + n1 = false; + } else if (type_is) { + if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { + // strings are considered the same if their value is + // the same + n1 = tv_equal(typ1, typ2, ic, false); + } else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) { + n1 = typ1->vval.v_partial == typ2->vval.v_partial; + } else { + n1 = false; + } + } else { + n1 = tv_equal(typ1, typ2, ic, false); + } + if (type == EXPR_NEQUAL || type == EXPR_ISNOT) { + n1 = !n1; + } + } else if ((typ1->v_type == VAR_FLOAT || typ2->v_type == VAR_FLOAT) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a float, compare as a float. + // When using "=~" or "!~", always compare as string. + const float_T f1 = tv_get_float(typ1); + const float_T f2 = tv_get_float(typ2); + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: n1 = f1 == f2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: n1 = f1 != f2; break; + case EXPR_GREATER: n1 = f1 > f2; break; + case EXPR_GEQUAL: n1 = f1 >= f2; break; + case EXPR_SMALLER: n1 = f1 < f2; break; + case EXPR_SEQUAL: n1 = f1 <= f2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: break; // avoid gcc warning + } + } else if ((typ1->v_type == VAR_NUMBER || typ2->v_type == VAR_NUMBER) + && type != EXPR_MATCH && type != EXPR_NOMATCH) { + // If one of the two variables is a number, compare as a number. + // When using "=~" or "!~", always compare as string. + n1 = tv_get_number(typ1); + n2 = tv_get_number(typ2); + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: n1 = n1 == n2; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: n1 = n1 != n2; break; + case EXPR_GREATER: n1 = n1 > n2; break; + case EXPR_GEQUAL: n1 = n1 >= n2; break; + case EXPR_SMALLER: n1 = n1 < n2; break; + case EXPR_SEQUAL: n1 = n1 <= n2; break; + case EXPR_UNKNOWN: + case EXPR_MATCH: + case EXPR_NOMATCH: break; // avoid gcc warning + } + } else { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(typ1, buf1); + const char *const s2 = tv_get_string_buf(typ2, buf2); + int i; + if (type != EXPR_MATCH && type != EXPR_NOMATCH) { + i = mb_strcmp_ic(ic, s1, s2); + } else { + i = 0; + } + n1 = false; + switch (type) { + case EXPR_IS: + case EXPR_EQUAL: n1 = i == 0; break; + case EXPR_ISNOT: + case EXPR_NEQUAL: n1 = i != 0; break; + case EXPR_GREATER: n1 = i > 0; break; + case EXPR_GEQUAL: n1 = i >= 0; break; + case EXPR_SMALLER: n1 = i < 0; break; + case EXPR_SEQUAL: n1 = i <= 0; break; + + case EXPR_MATCH: + case EXPR_NOMATCH: + n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); + if (type == EXPR_NOMATCH) { + n1 = !n1; + } + break; + case EXPR_UNKNOWN: break; // avoid gcc warning + } + } + tv_clear(typ1); + typ1->v_type = VAR_NUMBER; + typ1->vval.v_number = n1; + return OK; +} + +char *typval_tostring(typval_T *arg) +{ + if (arg == NULL) { + return xstrdup("(does not exist)"); + } + return encode_tv2string(arg, NULL); +} + +bool var_exists(const char *var) + FUNC_ATTR_NONNULL_ALL +{ + char *tofree; + bool n = false; + + // get_name_len() takes care of expanding curly braces + const char *name = var; + const int len = get_name_len((const char **)&var, &tofree, true, false); + if (len > 0) { + typval_T tv; + + if (tofree != NULL) { + name = tofree; + } + n = get_var_tv(name, len, &tv, NULL, false, true) == OK; + if (n) { + // Handle d.key, l[idx], f(expr). + n = handle_subscript(&var, &tv, true, false) == OK; + if (n) { + tv_clear(&tv); + } + } + } + if (*var != NUL) { + n = false; + } + + xfree(tofree); + return n; +} |