1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <inttypes.h>
#include <stdlib.h>
#include "nvim/eval.h"
#include "nvim/eval/executor.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/message.h"
#include "nvim/strings.h"
#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/executor.c.generated.h" // IWYU pragma: export
#endif
char *e_listidx = 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
{
// 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, (char)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();
}
}
semsg(_(e_letwrong), op);
return FAIL;
}
|