neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

executor.c (5395B)


      1 #include <inttypes.h>
      2 #include <stdlib.h>
      3 
      4 #include "nvim/errors.h"
      5 #include "nvim/eval.h"
      6 #include "nvim/eval/executor.h"
      7 #include "nvim/eval/typval.h"
      8 #include "nvim/eval/typval_defs.h"
      9 #include "nvim/garray.h"
     10 #include "nvim/gettext_defs.h"
     11 #include "nvim/message.h"
     12 #include "nvim/strings.h"
     13 #include "nvim/types_defs.h"
     14 #include "nvim/vim_defs.h"
     15 
     16 #include "eval/executor.c.generated.h"
     17 
     18 /// Handle "blob1 += blob2".
     19 /// Returns OK or FAIL.
     20 static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op)
     21  FUNC_ATTR_NONNULL_ALL
     22 {
     23  if (*op != '+' || tv2->v_type != VAR_BLOB) {
     24    return FAIL;
     25  }
     26 
     27  // Blob += Blob
     28  if (tv2->vval.v_blob == NULL) {
     29    return OK;
     30  }
     31 
     32  if (tv1->vval.v_blob == NULL) {
     33    tv1->vval.v_blob = tv2->vval.v_blob;
     34    tv1->vval.v_blob->bv_refcount++;
     35    return OK;
     36  }
     37 
     38  blob_T *const b1 = tv1->vval.v_blob;
     39  blob_T *const b2 = tv2->vval.v_blob;
     40  const int len = tv_blob_len(b2);
     41 
     42  for (int i = 0; i < len; i++) {
     43    ga_append(&b1->bv_ga, tv_blob_get(b2, i));
     44  }
     45 
     46  return OK;
     47 }
     48 
     49 /// Handle "list1 += list2".
     50 /// Returns OK or FAIL.
     51 static int tv_op_list(typval_T *tv1, const typval_T *tv2, const char *op)
     52  FUNC_ATTR_NONNULL_ALL
     53 {
     54  if (*op != '+' || tv2->v_type != VAR_LIST) {
     55    return FAIL;
     56  }
     57 
     58  // List += List
     59  if (tv2->vval.v_list == NULL) {
     60    return OK;
     61  }
     62 
     63  if (tv1->vval.v_list == NULL) {
     64    tv1->vval.v_list = tv2->vval.v_list;
     65    tv_list_ref(tv1->vval.v_list);
     66  } else {
     67    tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
     68  }
     69 
     70  return OK;
     71 }
     72 
     73 /// Handle number operations:
     74 ///      nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
     75 ///
     76 /// Returns OK or FAIL.
     77 static int tv_op_number(typval_T *tv1, const typval_T *tv2, const char *op)
     78  FUNC_ATTR_NONNULL_ALL
     79 {
     80  varnumber_T n = tv_get_number(tv1);
     81  if (tv2->v_type == VAR_FLOAT) {
     82    float_T f = (float_T)n;
     83    if (*op == '%') {
     84      return FAIL;
     85    }
     86    switch (*op) {
     87    case '+':
     88      f += tv2->vval.v_float; break;
     89    case '-':
     90      f -= tv2->vval.v_float; break;
     91    case '*':
     92      f *= tv2->vval.v_float; break;
     93    case '/':
     94      f /= tv2->vval.v_float; break;
     95    }
     96    tv_clear(tv1);
     97    tv1->v_type = VAR_FLOAT;
     98    tv1->vval.v_float = f;
     99  } else {
    100    switch (*op) {
    101    case '+':
    102      n += tv_get_number(tv2); break;
    103    case '-':
    104      n -= tv_get_number(tv2); break;
    105    case '*':
    106      n *= tv_get_number(tv2); break;
    107    case '/':
    108      n = num_divide(n, tv_get_number(tv2)); break;
    109    case '%':
    110      n = num_modulus(n, tv_get_number(tv2)); break;
    111    }
    112    tv_clear(tv1);
    113    tv1->v_type = VAR_NUMBER;
    114    tv1->vval.v_number = n;
    115  }
    116 
    117  return OK;
    118 }
    119 
    120 /// Handle "str1 .= str2"
    121 /// Returns OK or FAIL.
    122 static int tv_op_string(typval_T *tv1, const typval_T *tv2, const char *op)
    123  FUNC_ATTR_NONNULL_ALL
    124 {
    125  if (tv2->v_type == VAR_FLOAT) {
    126    return FAIL;
    127  }
    128 
    129  // str .= str
    130  const char *tvs = tv_get_string(tv1);
    131  char numbuf[NUMBUFLEN];
    132  char *const s = concat_str(tvs, tv_get_string_buf(tv2, numbuf));
    133  tv_clear(tv1);
    134  tv1->v_type = VAR_STRING;
    135  tv1->vval.v_string = s;
    136 
    137  return OK;
    138 }
    139 
    140 /// Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
    141 /// and "tv1 .= tv2"
    142 /// Returns OK or FAIL.
    143 static int tv_op_nr_or_string(typval_T *tv1, const typval_T *tv2, const char *op)
    144  FUNC_ATTR_NONNULL_ALL
    145 {
    146  if (tv2->v_type == VAR_LIST) {
    147    return FAIL;
    148  }
    149 
    150  if (vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
    151    return tv_op_number(tv1, tv2, op);
    152  }
    153 
    154  return tv_op_string(tv1, tv2, op);
    155 }
    156 
    157 /// Handle "f1 += f2", "f1 -= f2", "f1 *= f2", "f1 /= f2".
    158 /// Returns OK or FAIL.
    159 static int tv_op_float(typval_T *tv1, const typval_T *tv2, const char *op)
    160  FUNC_ATTR_NONNULL_ALL
    161 {
    162  if (*op == '%' || *op == '.'
    163      || (tv2->v_type != VAR_FLOAT
    164          && tv2->v_type != VAR_NUMBER
    165          && tv2->v_type != VAR_STRING)) {
    166    return FAIL;
    167  }
    168 
    169  const float_T f = (tv2->v_type == VAR_FLOAT
    170                     ? tv2->vval.v_float
    171                     : (float_T)tv_get_number(tv2));
    172  switch (*op) {
    173  case '+':
    174    tv1->vval.v_float += f; break;
    175  case '-':
    176    tv1->vval.v_float -= f; break;
    177  case '*':
    178    tv1->vval.v_float *= f; break;
    179  case '/':
    180    tv1->vval.v_float /= f; break;
    181  }
    182 
    183  return OK;
    184 }
    185 
    186 /// Handle tv1 += tv2, -=, *=, /=,  %=, .=
    187 ///
    188 /// @param[in,out]  tv1  First operand, modified typval.
    189 /// @param[in]  tv2  Second operand.
    190 /// @param[in]  op  Used operator.
    191 ///
    192 /// @return OK or FAIL.
    193 int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *const op)
    194  FUNC_ATTR_NONNULL_ALL
    195 {
    196  // Can't do anything with a Funcref or Dict on the right.
    197  // v:true and friends only work with "..=".
    198  if (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_DICT
    199      || ((tv2->v_type == VAR_BOOL || tv2->v_type == VAR_SPECIAL) && *op == '.')) {
    200    semsg(_(e_letwrong), op);
    201    return FAIL;
    202  }
    203 
    204  int retval = FAIL;
    205 
    206  switch (tv1->v_type) {
    207  case VAR_DICT:
    208  case VAR_FUNC:
    209  case VAR_PARTIAL:
    210  case VAR_BOOL:
    211  case VAR_SPECIAL:
    212    break;
    213  case VAR_BLOB:
    214    retval = tv_op_blob(tv1, tv2, op);
    215    break;
    216  case VAR_LIST:
    217    retval = tv_op_list(tv1, tv2, op);
    218    break;
    219  case VAR_NUMBER:
    220  case VAR_STRING:
    221    retval = tv_op_nr_or_string(tv1, tv2, op);
    222    break;
    223  case VAR_FLOAT:
    224    retval = tv_op_float(tv1, tv2, op);
    225    break;
    226  case VAR_UNKNOWN:
    227    abort();
    228  }
    229 
    230  if (retval != OK) {
    231    semsg(_(e_letwrong), op);
    232  }
    233 
    234  return retval;
    235 }