1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | #include "nvim/eval/typval.h" |
5 | #include "nvim/eval/executor.h" |
6 | #include "nvim/eval.h" |
7 | #include "nvim/message.h" |
8 | #include "nvim/vim.h" |
9 | #include "nvim/globals.h" |
10 | |
11 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
12 | # include "eval/executor.c.generated.h" |
13 | #endif |
14 | |
15 | static char *e_letwrong = N_("E734: Wrong variable type for %s=" ); |
16 | |
17 | char *e_listidx = N_("E684: list index out of range: %" PRId64); |
18 | |
19 | /// Hanle tv1 += tv2, -=, *=, /=, %=, .= |
20 | /// |
21 | /// @param[in,out] tv1 First operand, modified typval. |
22 | /// @param[in] tv2 Second operand. |
23 | /// @param[in] op Used operator. |
24 | /// |
25 | /// @return OK or FAIL. |
26 | int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, |
27 | const char *const op) |
28 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED |
29 | { |
30 | // Can't do anything with a Funcref, a Dict or special value on the right. |
31 | if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { |
32 | switch (tv1->v_type) { |
33 | case VAR_DICT: |
34 | case VAR_FUNC: |
35 | case VAR_PARTIAL: |
36 | case VAR_SPECIAL: { |
37 | break; |
38 | } |
39 | case VAR_LIST: { |
40 | if (*op != '+' || tv2->v_type != VAR_LIST) { |
41 | break; |
42 | } |
43 | // List += List |
44 | if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { |
45 | tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); |
46 | } |
47 | return OK; |
48 | } |
49 | case VAR_NUMBER: |
50 | case VAR_STRING: { |
51 | if (tv2->v_type == VAR_LIST) { |
52 | break; |
53 | } |
54 | if (vim_strchr((char_u *)"+-*/%" , *op) != NULL) { |
55 | // nr += nr or nr -= nr, nr *= nr, nr /= nr, nr %= nr |
56 | varnumber_T n = tv_get_number(tv1); |
57 | if (tv2->v_type == VAR_FLOAT) { |
58 | float_T f = (float_T)n; |
59 | |
60 | if (*op == '%') { |
61 | break; |
62 | } |
63 | switch (*op) { |
64 | case '+': f += tv2->vval.v_float; break; |
65 | case '-': f -= tv2->vval.v_float; break; |
66 | case '*': f *= tv2->vval.v_float; break; |
67 | case '/': f /= tv2->vval.v_float; break; |
68 | } |
69 | tv_clear(tv1); |
70 | tv1->v_type = VAR_FLOAT; |
71 | tv1->vval.v_float = f; |
72 | } else { |
73 | switch (*op) { |
74 | case '+': n += tv_get_number(tv2); break; |
75 | case '-': n -= tv_get_number(tv2); break; |
76 | case '*': n *= tv_get_number(tv2); break; |
77 | case '/': n = num_divide(n, tv_get_number(tv2)); break; |
78 | case '%': n = num_modulus(n, tv_get_number(tv2)); break; |
79 | } |
80 | tv_clear(tv1); |
81 | tv1->v_type = VAR_NUMBER; |
82 | tv1->vval.v_number = n; |
83 | } |
84 | } else { |
85 | // str .= str |
86 | if (tv2->v_type == VAR_FLOAT) { |
87 | break; |
88 | } |
89 | const char *tvs = tv_get_string(tv1); |
90 | char numbuf[NUMBUFLEN]; |
91 | char *const s = (char *)concat_str( |
92 | (const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, |
93 | numbuf)); |
94 | tv_clear(tv1); |
95 | tv1->v_type = VAR_STRING; |
96 | tv1->vval.v_string = (char_u *)s; |
97 | } |
98 | return OK; |
99 | } |
100 | case VAR_FLOAT: { |
101 | if (*op == '%' || *op == '.' |
102 | || (tv2->v_type != VAR_FLOAT |
103 | && tv2->v_type != VAR_NUMBER |
104 | && tv2->v_type != VAR_STRING)) { |
105 | break; |
106 | } |
107 | const float_T f = (tv2->v_type == VAR_FLOAT |
108 | ? tv2->vval.v_float |
109 | : (float_T)tv_get_number(tv2)); |
110 | switch (*op) { |
111 | case '+': tv1->vval.v_float += f; break; |
112 | case '-': tv1->vval.v_float -= f; break; |
113 | case '*': tv1->vval.v_float *= f; break; |
114 | case '/': tv1->vval.v_float /= f; break; |
115 | } |
116 | return OK; |
117 | } |
118 | case VAR_UNKNOWN: { |
119 | assert(false); |
120 | } |
121 | } |
122 | } |
123 | |
124 | EMSG2(_(e_letwrong), op); |
125 | return FAIL; |
126 | } |
127 | |