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
15static char *e_letwrong = N_("E734: Wrong variable type for %s=");
16
17char *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.
26int 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