mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 14:08:32 +00:00
vim-patch:partial:9.1.0411: too long functions in eval.c
Problem: too long functions in eval.c
Solution: refactor functions (Yegappan Lakshmanan)
closes: vim/vim#14755
4ceb4dc825
Skip the eval_expr_typval() changes.
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
337
src/nvim/eval.c
337
src/nvim/eval.c
@@ -154,6 +154,12 @@ typedef struct {
|
||||
int fi_byte_idx; // byte index in fi_string
|
||||
} forinfo_T;
|
||||
|
||||
typedef enum {
|
||||
GLV_FAIL,
|
||||
GLV_OK,
|
||||
GLV_STOP,
|
||||
} glv_status_T;
|
||||
|
||||
// values for vv_flags:
|
||||
#define VV_COMPAT 1 // compatible, also used without "v:"
|
||||
#define VV_RO 2 // read-only
|
||||
@@ -1383,6 +1389,200 @@ Object eval_foldtext(win_T *wp)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Get an Dict lval variable that can be assigned a value to: "name",
|
||||
/// "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc.
|
||||
/// "name" points to the start of the name.
|
||||
/// If "rettv" is not NULL it points to the value to be assigned.
|
||||
/// "unlet" is true for ":unlet": slightly different behavior when something is
|
||||
/// wrong; must end in space or cmd separator.
|
||||
///
|
||||
/// flags:
|
||||
/// GLV_QUIET: do not give error messages
|
||||
/// GLV_READ_ONLY: will not change the variable
|
||||
/// GLV_NO_AUTOLOAD: do not use script autoloading
|
||||
///
|
||||
/// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
|
||||
/// failure. Returns GLV_STOP to stop processing the characters following
|
||||
/// 'key_end'.
|
||||
static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int len, char **key_end,
|
||||
typval_T *var1, int flags, bool unlet, typval_T *rettv)
|
||||
{
|
||||
bool quiet = flags & GLV_QUIET;
|
||||
char *p = *key_end;
|
||||
|
||||
if (len == -1) {
|
||||
// "[key]": get key from "var1"
|
||||
key = (char *)tv_get_string(var1); // is number or string
|
||||
}
|
||||
lp->ll_list = NULL;
|
||||
|
||||
// a NULL dict is equivalent with an empty dict
|
||||
if (lp->ll_tv->vval.v_dict == NULL) {
|
||||
lp->ll_tv->vval.v_dict = tv_dict_alloc();
|
||||
lp->ll_tv->vval.v_dict->dv_refcount++;
|
||||
}
|
||||
lp->ll_dict = lp->ll_tv->vval.v_dict;
|
||||
|
||||
lp->ll_di = tv_dict_find(lp->ll_dict, key, len);
|
||||
|
||||
// When assigning to a scope dictionary check that a function and
|
||||
// variable name is valid (only variable name unless it is l: or
|
||||
// g: dictionary). Disallow overwriting a builtin function.
|
||||
if (rettv != NULL && lp->ll_dict->dv_scope != 0) {
|
||||
char prevval;
|
||||
if (len != -1) {
|
||||
prevval = key[len];
|
||||
key[len] = NUL;
|
||||
} else {
|
||||
prevval = 0; // Avoid compiler warning.
|
||||
}
|
||||
bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
|
||||
&& tv_is_func(*rettv)
|
||||
&& var_wrong_func_name(key, lp->ll_di == NULL))
|
||||
|| !valid_varname(key));
|
||||
if (len != -1) {
|
||||
key[len] = prevval;
|
||||
}
|
||||
if (wrong) {
|
||||
tv_clear(var1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv)
|
||||
&& len == -1 && rettv == NULL) {
|
||||
tv_clear(var1);
|
||||
semsg(e_illvar, "v:['lua']");
|
||||
return GLV_FAIL;
|
||||
}
|
||||
|
||||
if (lp->ll_di == NULL) {
|
||||
// Can't add "v:" or "a:" variable.
|
||||
if (lp->ll_dict == &vimvardict
|
||||
|| &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) {
|
||||
semsg(_(e_illvar), name);
|
||||
tv_clear(var1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
|
||||
// Key does not exist in dict: may need to add it.
|
||||
if (*p == '[' || *p == '.' || unlet) {
|
||||
if (!quiet) {
|
||||
semsg(_(e_dictkey), key);
|
||||
}
|
||||
tv_clear(var1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
if (len == -1) {
|
||||
lp->ll_newkey = xstrdup(key);
|
||||
} else {
|
||||
lp->ll_newkey = xmemdupz(key, (size_t)len);
|
||||
}
|
||||
tv_clear(var1);
|
||||
*key_end = p;
|
||||
return GLV_STOP;
|
||||
// existing variable, need to check if it can be changed
|
||||
} else if (!(flags & GLV_READ_ONLY)
|
||||
&& (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name))
|
||||
|| var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) {
|
||||
tv_clear(var1);
|
||||
return GLV_FAIL;
|
||||
}
|
||||
|
||||
tv_clear(var1);
|
||||
lp->ll_tv = &lp->ll_di->di_tv;
|
||||
|
||||
return GLV_OK;
|
||||
}
|
||||
|
||||
/// Get an blob lval variable that can be assigned a value to: "name",
|
||||
/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
|
||||
///
|
||||
/// 'var1' specifies the starting blob index and 'var2' specifies the ending
|
||||
/// blob index. If the first index is not specified in a range, then 'empty1'
|
||||
/// is true. If 'quiet' is true, then error messages are not displayed for
|
||||
/// invalid indexes.
|
||||
///
|
||||
/// The blob is returned in 'lp'. Returns OK on success and FAIL on failure.
|
||||
static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, bool quiet)
|
||||
{
|
||||
const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
|
||||
|
||||
// Get the number and item for the only or first index of the List.
|
||||
if (empty1) {
|
||||
lp->ll_n1 = 0;
|
||||
} else {
|
||||
// Is number or string.
|
||||
lp->ll_n1 = (int)tv_get_number(var1);
|
||||
}
|
||||
tv_clear(var1);
|
||||
|
||||
if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) {
|
||||
tv_clear(var2);
|
||||
return FAIL;
|
||||
}
|
||||
if (lp->ll_range && !lp->ll_empty2) {
|
||||
lp->ll_n2 = (int)tv_get_number(var2);
|
||||
tv_clear(var2);
|
||||
if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
lp->ll_blob = lp->ll_tv->vval.v_blob;
|
||||
lp->ll_tv = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Get a List lval variable that can be assigned a value to: "name",
|
||||
/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
|
||||
///
|
||||
/// 'var1' specifies the starting List index and 'var2' specifies the ending
|
||||
/// List index. If the first index is not specified in a range, then 'empty1'
|
||||
/// is true. If 'quiet' is true, then error messages are not displayed for
|
||||
/// invalid indexes.
|
||||
///
|
||||
/// The List is returned in 'lp'. Returns OK on success and FAIL on failure.
|
||||
static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, int flags,
|
||||
bool quiet)
|
||||
{
|
||||
// Get the number and item for the only or first index of the List.
|
||||
if (empty1) {
|
||||
lp->ll_n1 = 0;
|
||||
} else {
|
||||
// Is number or string.
|
||||
lp->ll_n1 = (int)tv_get_number(var1);
|
||||
}
|
||||
tv_clear(var1);
|
||||
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_list = lp->ll_tv->vval.v_list;
|
||||
lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
|
||||
if (lp->ll_li == NULL) {
|
||||
tv_clear(var2);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// 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 = (int)tv_get_number(var2); // Is number or string.
|
||||
tv_clear(var2);
|
||||
if (tv_list_check_range_index_two(lp->ll_list,
|
||||
&lp->ll_n1, lp->ll_li,
|
||||
&lp->ll_n2, quiet) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Get the lval of a list/dict/blob subitem starting at "p". Loop
|
||||
/// until no more [idx] or .key is following.
|
||||
///
|
||||
@@ -1391,9 +1591,9 @@ Object eval_foldtext(win_T *wp)
|
||||
/// @return A pointer to the character after the subscript on success or NULL on
|
||||
/// failure.
|
||||
static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht,
|
||||
dictitem_T *v, int unlet, int flags)
|
||||
dictitem_T *v, bool unlet, int flags)
|
||||
{
|
||||
int quiet = flags & GLV_QUIET;
|
||||
bool quiet = flags & GLV_QUIET;
|
||||
typval_T var1;
|
||||
var1.v_type = VAR_UNKNOWN;
|
||||
typval_T var2;
|
||||
@@ -1516,144 +1716,23 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv
|
||||
}
|
||||
|
||||
if (lp->ll_tv->v_type == VAR_DICT) {
|
||||
if (len == -1) {
|
||||
// "[key]": get key from "var1"
|
||||
key = (char *)tv_get_string(&var1); // is number or string
|
||||
}
|
||||
lp->ll_list = NULL;
|
||||
|
||||
// a NULL dict is equivalent with an empty dict
|
||||
if (lp->ll_tv->vval.v_dict == NULL) {
|
||||
lp->ll_tv->vval.v_dict = tv_dict_alloc();
|
||||
lp->ll_tv->vval.v_dict->dv_refcount++;
|
||||
}
|
||||
lp->ll_dict = lp->ll_tv->vval.v_dict;
|
||||
|
||||
lp->ll_di = tv_dict_find(lp->ll_dict, key, len);
|
||||
|
||||
// When assigning to a scope dictionary check that a function and
|
||||
// variable name is valid (only variable name unless it is l: or
|
||||
// g: dictionary). Disallow overwriting a builtin function.
|
||||
if (rettv != NULL && lp->ll_dict->dv_scope != 0) {
|
||||
char prevval;
|
||||
if (len != -1) {
|
||||
prevval = key[len];
|
||||
key[len] = NUL;
|
||||
} else {
|
||||
prevval = 0; // Avoid compiler warning.
|
||||
}
|
||||
bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE
|
||||
&& tv_is_func(*rettv)
|
||||
&& var_wrong_func_name(key, lp->ll_di == NULL))
|
||||
|| !valid_varname(key));
|
||||
if (len != -1) {
|
||||
key[len] = prevval;
|
||||
}
|
||||
if (wrong) {
|
||||
tv_clear(&var1);
|
||||
glv_status_T glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1,
|
||||
flags, unlet, rettv);
|
||||
if (glv_status == GLV_FAIL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv)
|
||||
&& len == -1 && rettv == NULL) {
|
||||
tv_clear(&var1);
|
||||
semsg(e_illvar, "v:['lua']");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lp->ll_di == NULL) {
|
||||
// Can't add "v:" or "a:" variable.
|
||||
if (lp->ll_dict == &vimvardict
|
||||
|| &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) {
|
||||
semsg(_(e_illvar), name);
|
||||
tv_clear(&var1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Key does not exist in dict: may need to add it.
|
||||
if (*p == '[' || *p == '.' || unlet) {
|
||||
if (!quiet) {
|
||||
semsg(_(e_dictkey), key);
|
||||
}
|
||||
tv_clear(&var1);
|
||||
return NULL;
|
||||
}
|
||||
if (len == -1) {
|
||||
lp->ll_newkey = xstrdup(key);
|
||||
} else {
|
||||
lp->ll_newkey = xmemdupz(key, (size_t)len);
|
||||
}
|
||||
tv_clear(&var1);
|
||||
if (glv_status == GLV_STOP) {
|
||||
break;
|
||||
// existing variable, need to check if it can be changed
|
||||
} else if (!(flags & GLV_READ_ONLY)
|
||||
&& (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name))
|
||||
|| var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) {
|
||||
tv_clear(&var1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tv_clear(&var1);
|
||||
lp->ll_tv = &lp->ll_di->di_tv;
|
||||
} else if (lp->ll_tv->v_type == VAR_BLOB) {
|
||||
// Get the number and item for the only or first index of the List.
|
||||
if (empty1) {
|
||||
lp->ll_n1 = 0;
|
||||
} else {
|
||||
// Is number or string.
|
||||
lp->ll_n1 = (int)tv_get_number(&var1);
|
||||
}
|
||||
tv_clear(&var1);
|
||||
|
||||
const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob);
|
||||
if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) {
|
||||
tv_clear(&var2);
|
||||
if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) {
|
||||
return NULL;
|
||||
}
|
||||
if (lp->ll_range && !lp->ll_empty2) {
|
||||
lp->ll_n2 = (int)tv_get_number(&var2);
|
||||
tv_clear(&var2);
|
||||
if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
lp->ll_blob = lp->ll_tv->vval.v_blob;
|
||||
lp->ll_tv = NULL;
|
||||
break;
|
||||
} else {
|
||||
// Get the number and item for the only or first index of the List.
|
||||
if (empty1) {
|
||||
lp->ll_n1 = 0;
|
||||
} else {
|
||||
// Is number or string.
|
||||
lp->ll_n1 = (int)tv_get_number(&var1);
|
||||
}
|
||||
tv_clear(&var1);
|
||||
|
||||
lp->ll_dict = NULL;
|
||||
lp->ll_list = lp->ll_tv->vval.v_list;
|
||||
lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
|
||||
if (lp->ll_li == NULL) {
|
||||
tv_clear(&var2);
|
||||
if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) {
|
||||
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.
|
||||
if (lp->ll_range && !lp->ll_empty2) {
|
||||
lp->ll_n2 = (int)tv_get_number(&var2); // Is number or string.
|
||||
tv_clear(&var2);
|
||||
if (tv_list_check_range_index_two(lp->ll_list,
|
||||
&lp->ll_n1, lp->ll_li,
|
||||
&lp->ll_n2, quiet) == FAIL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -21,67 +21,67 @@
|
||||
char *e_list_index_out_of_range_nr
|
||||
= N_("E684: List index out of range: %" PRId64);
|
||||
|
||||
/// Handle tv1 += tv2, -=, *=, /=, %=, .=
|
||||
///
|
||||
/// @param[in,out] tv1 First operand, modified typval.
|
||||
/// @param[in] tv2 Second operand.
|
||||
/// @param[in] op Used operator.
|
||||
///
|
||||
/// @return OK or FAIL.
|
||||
int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *const op)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED
|
||||
/// Handle "blob1 += blob2".
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Can't do anything with a Funcref or Dict on the right.
|
||||
// v:true and friends only work with "..=".
|
||||
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
|
||||
&& ((tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) || *op == '.')) {
|
||||
switch (tv1->v_type) {
|
||||
case VAR_DICT:
|
||||
case VAR_FUNC:
|
||||
case VAR_PARTIAL:
|
||||
case VAR_BOOL:
|
||||
case VAR_SPECIAL:
|
||||
break;
|
||||
case VAR_BLOB:
|
||||
if (*op != '+' || tv2->v_type != VAR_BLOB) {
|
||||
break;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Blob += Blob
|
||||
if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) {
|
||||
if (tv1->vval.v_blob == NULL || tv2->vval.v_blob == NULL) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
blob_T *const b1 = tv1->vval.v_blob;
|
||||
blob_T *const b2 = tv2->vval.v_blob;
|
||||
for (int i = 0; i < tv_blob_len(b2); i++) {
|
||||
const int len = tv_blob_len(b2);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ga_append(&b1->bv_ga, tv_blob_get(b2, i));
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
case VAR_LIST:
|
||||
if (*op != '+' || tv2->v_type != VAR_LIST) {
|
||||
break;
|
||||
}
|
||||
|
||||
/// Handle "list1 += list2".
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_list(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (*op != '+' || tv2->v_type != VAR_LIST) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// List += List
|
||||
if (tv2->vval.v_list != NULL) {
|
||||
if (tv2->vval.v_list == NULL) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (tv1->vval.v_list == NULL) {
|
||||
tv1->vval.v_list = tv2->vval.v_list;
|
||||
tv_list_ref(tv1->vval.v_list);
|
||||
} else {
|
||||
tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
case VAR_NUMBER:
|
||||
case VAR_STRING:
|
||||
if (tv2->v_type == VAR_LIST) {
|
||||
break;
|
||||
}
|
||||
if (vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
|
||||
// nr += nr or nr -= nr, nr *= nr, nr /= nr, nr %= nr
|
||||
|
||||
/// Handle number operations:
|
||||
/// nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
|
||||
///
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_number(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
varnumber_T n = tv_get_number(tv1);
|
||||
if (tv2->v_type == VAR_FLOAT) {
|
||||
float_T f = (float_T)n;
|
||||
|
||||
if (*op == '%') {
|
||||
break;
|
||||
return FAIL;
|
||||
}
|
||||
switch (*op) {
|
||||
case '+':
|
||||
@@ -113,27 +113,59 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
|
||||
tv1->v_type = VAR_NUMBER;
|
||||
tv1->vval.v_number = n;
|
||||
}
|
||||
} else {
|
||||
// str .= str
|
||||
if (tv2->v_type == VAR_FLOAT) {
|
||||
break;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Handle "str1 .= str2"
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_string(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (tv2->v_type == VAR_FLOAT) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// str .= str
|
||||
const char *tvs = tv_get_string(tv1);
|
||||
char numbuf[NUMBUFLEN];
|
||||
char *const s =
|
||||
concat_str(tvs, tv_get_string_buf(tv2, numbuf));
|
||||
char *const s = concat_str(tvs, tv_get_string_buf(tv2, numbuf));
|
||||
tv_clear(tv1);
|
||||
tv1->v_type = VAR_STRING;
|
||||
tv1->vval.v_string = s;
|
||||
}
|
||||
|
||||
return OK;
|
||||
case VAR_FLOAT: {
|
||||
}
|
||||
|
||||
/// Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
|
||||
/// and "tv1 .= tv2"
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_nr_or_string(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (tv2->v_type == VAR_LIST) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
|
||||
return tv_op_number(tv1, tv2, op);
|
||||
}
|
||||
|
||||
return tv_op_string(tv1, tv2, op);
|
||||
}
|
||||
|
||||
/// Handle "f1 += f2", "f1 -= f2", "f1 *= f2", "f1 /= f2".
|
||||
/// Returns OK or FAIL.
|
||||
static int tv_op_float(typval_T *tv1, const typval_T *tv2, const char *op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (*op == '%' || *op == '.'
|
||||
|| (tv2->v_type != VAR_FLOAT
|
||||
&& tv2->v_type != VAR_NUMBER
|
||||
&& tv2->v_type != VAR_STRING)) {
|
||||
break;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
const float_T f = (tv2->v_type == VAR_FLOAT
|
||||
? tv2->vval.v_float
|
||||
: (float_T)tv_get_number(tv2));
|
||||
@@ -147,13 +179,57 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
|
||||
case '/':
|
||||
tv1->vval.v_float /= f; break;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
case VAR_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle tv1 += tv2, -=, *=, /=, %=, .=
|
||||
///
|
||||
/// @param[in,out] tv1 First operand, modified typval.
|
||||
/// @param[in] tv2 Second operand.
|
||||
/// @param[in] op Used operator.
|
||||
///
|
||||
/// @return OK or FAIL.
|
||||
int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *const op)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Can't do anything with a Funcref or Dict on the right.
|
||||
// v:true and friends only work with "..=".
|
||||
if (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_DICT
|
||||
|| ((tv2->v_type == VAR_BOOL || tv2->v_type == VAR_SPECIAL) && *op == '.')) {
|
||||
semsg(_(e_letwrong), op);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
int retval = FAIL;
|
||||
|
||||
switch (tv1->v_type) {
|
||||
case VAR_DICT:
|
||||
case VAR_FUNC:
|
||||
case VAR_PARTIAL:
|
||||
case VAR_BOOL:
|
||||
case VAR_SPECIAL:
|
||||
break;
|
||||
case VAR_BLOB:
|
||||
retval = tv_op_blob(tv1, tv2, op);
|
||||
break;
|
||||
case VAR_LIST:
|
||||
retval = tv_op_list(tv1, tv2, op);
|
||||
break;
|
||||
case VAR_NUMBER:
|
||||
case VAR_STRING:
|
||||
retval = tv_op_nr_or_string(tv1, tv2, op);
|
||||
break;
|
||||
case VAR_FLOAT:
|
||||
retval = tv_op_float(tv1, tv2, op);
|
||||
break;
|
||||
case VAR_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (retval != OK) {
|
||||
semsg(_(e_letwrong), op);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
Reference in New Issue
Block a user