diff options
Diffstat (limited to 'src/nvim/eval/executor.c')
-rw-r--r-- | src/nvim/eval/executor.c | 325 |
1 files changed, 207 insertions, 118 deletions
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 1b8c057d7c..5b92f217d1 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -1,6 +1,7 @@ #include <inttypes.h> #include <stdlib.h> +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/executor.h" #include "nvim/eval/typval.h" @@ -20,6 +21,174 @@ char *e_list_index_out_of_range_nr = N_("E684: List index out of range: %" PRId64); +/// 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 +{ + if (*op != '+' || tv2->v_type != VAR_BLOB) { + return FAIL; + } + + // Blob += Blob + if (tv2->vval.v_blob == NULL) { + return OK; + } + + if (tv1->vval.v_blob == NULL) { + tv1->vval.v_blob = tv2->vval.v_blob; + tv1->vval.v_blob->bv_refcount++; + return OK; + } + + blob_T *const b1 = tv1->vval.v_blob; + blob_T *const b2 = tv2->vval.v_blob; + 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; +} + +/// 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) { + 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; +} + +/// 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 == '%') { + return FAIL; + } + switch (*op) { + case '+': + f += tv2->vval.v_float; break; + case '-': + f -= tv2->vval.v_float; break; + case '*': + f *= tv2->vval.v_float; break; + case '/': + f /= tv2->vval.v_float; break; + } + tv_clear(tv1); + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f; + } else { + switch (*op) { + case '+': + n += tv_get_number(tv2); break; + case '-': + n -= tv_get_number(tv2); break; + case '*': + n *= tv_get_number(tv2); break; + case '/': + n = num_divide(n, tv_get_number(tv2)); break; + case '%': + n = num_modulus(n, tv_get_number(tv2)); break; + } + tv_clear(tv1); + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n; + } + + 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)); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = s; + + return OK; +} + +/// 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)) { + return FAIL; + } + + const float_T f = (tv2->v_type == VAR_FLOAT + ? tv2->vval.v_float + : (float_T)tv_get_number(tv2)); + switch (*op) { + case '+': + tv1->vval.v_float += f; break; + case '-': + tv1->vval.v_float -= f; break; + case '*': + tv1->vval.v_float *= f; break; + case '/': + tv1->vval.v_float /= f; break; + } + + return OK; +} + /// Handle tv1 += tv2, -=, *=, /=, %=, .= /// /// @param[in,out] tv1 First operand, modified typval. @@ -28,125 +197,45 @@ char *e_list_index_out_of_range_nr /// /// @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 + FUNC_ATTR_NONNULL_ALL { - // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT - && tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) { - 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; - } - // Blob += Blob - if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL) { - 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++) { - ga_append(&b1->bv_ga, tv_blob_get(b2, i)); - } - } - return OK; - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) { - break; - } - // List += List - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { - 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 - varnumber_T n = tv_get_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = (float_T)n; - - if (*op == '%') { - break; - } - switch (*op) { - case '+': - f += tv2->vval.v_float; break; - case '-': - f -= tv2->vval.v_float; break; - case '*': - f *= tv2->vval.v_float; break; - case '/': - f /= tv2->vval.v_float; break; - } - tv_clear(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - switch (*op) { - case '+': - n += tv_get_number(tv2); break; - case '-': - n -= tv_get_number(tv2); break; - case '*': - n *= tv_get_number(tv2); break; - case '/': - n = num_divide(n, tv_get_number(tv2)); break; - case '%': - n = num_modulus(n, tv_get_number(tv2)); break; - } - tv_clear(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - // str .= str - if (tv2->v_type == VAR_FLOAT) { - break; - } - const char *tvs = tv_get_string(tv1); - char numbuf[NUMBUFLEN]; - 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: { - if (*op == '%' || *op == '.' - || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) { - break; - } - const float_T f = (tv2->v_type == VAR_FLOAT - ? tv2->vval.v_float - : (float_T)tv_get_number(tv2)); - switch (*op) { - case '+': - tv1->vval.v_float += f; break; - case '-': - tv1->vval.v_float -= f; break; - case '*': - tv1->vval.v_float *= f; break; - case '/': - tv1->vval.v_float /= f; break; - } - return OK; - } - case VAR_UNKNOWN: - abort(); - } + // 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); } - semsg(_(e_letwrong), op); - return FAIL; + return retval; } |