/src/CMake/Source/cmExprParserHelper.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmExprParserHelper.h" |
4 | | |
5 | | #include <iostream> |
6 | | #include <stdexcept> |
7 | | #include <utility> |
8 | | |
9 | | #include "cmExprLexer.h" |
10 | | #include "cmStringAlgorithms.h" |
11 | | |
12 | | int cmExpr_yyparse(yyscan_t yyscanner); |
13 | | // |
14 | | cmExprParserHelper::cmExprParserHelper() |
15 | 1.85k | { |
16 | 1.85k | this->FileLine = -1; |
17 | 1.85k | this->FileName = nullptr; |
18 | 1.85k | this->Result = 0; |
19 | 1.85k | } |
20 | | |
21 | 1.85k | cmExprParserHelper::~cmExprParserHelper() = default; |
22 | | |
23 | | int cmExprParserHelper::ParseString(char const* str, int verb) |
24 | 1.85k | { |
25 | 1.85k | if (!str) { |
26 | 0 | return 0; |
27 | 0 | } |
28 | | // printf("Do some parsing: %s\n", str); |
29 | | |
30 | 1.85k | this->Verbose = verb; |
31 | 1.85k | this->InputBuffer = str; |
32 | 1.85k | this->InputBufferPos = 0; |
33 | 1.85k | this->CurrentLine = 0; |
34 | | |
35 | 1.85k | this->Result = 0; |
36 | | |
37 | 1.85k | yyscan_t yyscanner; |
38 | 1.85k | cmExpr_yylex_init(&yyscanner); |
39 | 1.85k | cmExpr_yyset_extra(this, yyscanner); |
40 | | |
41 | 1.85k | try { |
42 | 1.85k | int res = cmExpr_yyparse(yyscanner); |
43 | 1.85k | if (res != 0) { |
44 | 244 | std::string e = |
45 | 244 | cmStrCat("cannot parse the expression: \"", this->InputBuffer, |
46 | 244 | "\": ", this->ErrorString, '.'); |
47 | 244 | this->SetError(std::move(e)); |
48 | 244 | } |
49 | 1.85k | } catch (std::runtime_error const& fail) { |
50 | 9 | std::string e = cmStrCat("cannot evaluate the expression: \"", |
51 | 9 | this->InputBuffer, "\": ", fail.what(), '.'); |
52 | 9 | this->SetError(std::move(e)); |
53 | 75 | } catch (std::out_of_range const&) { |
54 | 75 | std::string e = |
55 | 75 | cmStrCat("cannot evaluate the expression: \"", this->InputBuffer, |
56 | 75 | "\": a numeric value is out of range."); |
57 | 75 | this->SetError(std::move(e)); |
58 | 75 | } catch (...) { |
59 | 0 | std::string e = |
60 | 0 | cmStrCat("cannot parse the expression: \"", this->InputBuffer, "\"."); |
61 | 0 | this->SetError(std::move(e)); |
62 | 0 | } |
63 | 1.85k | cmExpr_yylex_destroy(yyscanner); |
64 | 1.85k | if (!this->ErrorString.empty()) { |
65 | 328 | return 0; |
66 | 328 | } |
67 | | |
68 | 1.53k | if (this->Verbose) { |
69 | 0 | std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]" |
70 | 0 | << std::endl; |
71 | 0 | } |
72 | 1.53k | return 1; |
73 | 1.85k | } |
74 | | |
75 | | int cmExprParserHelper::LexInput(char* buf, int maxlen) |
76 | 177k | { |
77 | | // std::cout << "JPLexInput "; |
78 | | // std::cout.write(buf, maxlen); |
79 | | // std::cout << std::endl; |
80 | 177k | if (maxlen < 1) { |
81 | 0 | return 0; |
82 | 0 | } |
83 | 177k | if (this->InputBufferPos < this->InputBuffer.size()) { |
84 | 175k | buf[0] = this->InputBuffer[this->InputBufferPos++]; |
85 | 175k | if (buf[0] == '\n') { |
86 | 321 | this->CurrentLine++; |
87 | 321 | } |
88 | 175k | return (1); |
89 | 175k | } |
90 | 1.74k | buf[0] = '\n'; |
91 | 1.74k | return (0); |
92 | 177k | } |
93 | | |
94 | | void cmExprParserHelper::Error(char const* str) |
95 | 244 | { |
96 | 244 | unsigned long pos = static_cast<unsigned long>(this->InputBufferPos); |
97 | 244 | this->ErrorString = cmStrCat(str, " (", pos, ')'); |
98 | 244 | } |
99 | | |
100 | | void cmExprParserHelper::Warning(std::string str) |
101 | 8.57k | { |
102 | 8.57k | this->WarningString = cmStrCat(this->WarningString, std::move(str), '\n'); |
103 | 8.57k | } |
104 | | |
105 | | void cmExprParserHelper::UnexpectedChar(char c) |
106 | 25.8k | { |
107 | 25.8k | unsigned long pos = static_cast<unsigned long>(this->InputBufferPos); |
108 | 25.8k | this->WarningString = cmStrCat( |
109 | 25.8k | this->WarningString, "Unexpected character in expression at position ", |
110 | 25.8k | pos, ": ", c, '\n'); |
111 | 25.8k | } |
112 | | |
113 | | void cmExprParserHelper::SetResult(std::int64_t value) |
114 | 1.56k | { |
115 | 1.56k | this->Result = value; |
116 | 1.56k | } |
117 | | |
118 | | void cmExprParserHelper::SetError(std::string errorString) |
119 | 328 | { |
120 | 328 | this->ErrorString = std::move(errorString); |
121 | 328 | } |
122 | | |
123 | | std::int64_t cmExprParserHelper::ShL(std::int64_t l, std::int64_t r) |
124 | 4.68k | { |
125 | 4.68k | if (l < 0) { |
126 | 1.20k | this->Warning( |
127 | 1.20k | cmStrCat("left shift of negative value in:\n ", l, " << ", r)); |
128 | 1.20k | } |
129 | 4.68k | if (r < 0) { |
130 | 1.90k | this->Warning( |
131 | 1.90k | cmStrCat("shift exponent is negative in:\n ", l, " << ", r)); |
132 | 1.90k | r &= 0x3F; |
133 | 1.90k | } |
134 | 4.68k | if (r >= 64) { |
135 | 1.02k | this->Warning( |
136 | 1.02k | cmStrCat("shift exponent is too large in:\n ", l, " << ", r)); |
137 | 1.02k | r &= 0x3F; |
138 | 1.02k | } |
139 | 4.68k | return static_cast<std::int64_t>(static_cast<std::uint64_t>(l) << r); |
140 | 4.68k | } |
141 | | |
142 | | std::int64_t cmExprParserHelper::ShR(std::int64_t l, std::int64_t r) |
143 | 1.63k | { |
144 | 1.63k | if (r < 0) { |
145 | 561 | this->Warning( |
146 | 561 | cmStrCat("shift exponent is negative in:\n ", l, " >> ", r)); |
147 | 561 | r &= 0x3F; |
148 | 561 | } |
149 | 1.63k | if (r >= 64) { |
150 | 711 | this->Warning( |
151 | 711 | cmStrCat("shift exponent is too large in:\n ", l, " >> ", r)); |
152 | 711 | r &= 0x3F; |
153 | 711 | } |
154 | 1.63k | return l >> r; |
155 | 1.63k | } |
156 | | |
157 | | std::int64_t cmExprParserHelper::Add(std::int64_t l, std::int64_t r) |
158 | 919 | { |
159 | 919 | std::int64_t sum; |
160 | 919 | if (this->AddOverflow(l, r, &sum)) { |
161 | 379 | this->Warning(cmStrCat("signed integer overflow in:\n ", l, " + ", r)); |
162 | 379 | } |
163 | 919 | return sum; |
164 | 919 | } |
165 | | |
166 | | std::int64_t cmExprParserHelper::Sub(std::int64_t l, std::int64_t r) |
167 | 2.58k | { |
168 | 2.58k | std::int64_t diff; |
169 | 2.58k | if (this->SubOverflow(l, r, &diff)) { |
170 | 489 | this->Warning(cmStrCat("signed integer overflow in:\n ", l, " - ", r)); |
171 | 489 | } |
172 | 2.58k | return diff; |
173 | 2.58k | } |
174 | | |
175 | | std::int64_t cmExprParserHelper::Mul(std::int64_t l, std::int64_t r) |
176 | 952 | { |
177 | 952 | std::int64_t prod; |
178 | 952 | if (this->MulOverflow(l, r, &prod)) { |
179 | 453 | this->Warning(cmStrCat("signed integer overflow in:\n ", l, " * ", r)); |
180 | 453 | } |
181 | 952 | return prod; |
182 | 952 | } |
183 | | |
184 | | std::int64_t cmExprParserHelper::Div(std::int64_t l, std::int64_t r) |
185 | 1.44k | { |
186 | 1.44k | if (r == 0) { |
187 | 2 | throw std::overflow_error("divide by zero"); |
188 | 2 | } |
189 | 1.44k | if (l == INT64_MIN && r == -1) { |
190 | 1 | throw std::overflow_error("signed integer overflow in division"); |
191 | 1 | } |
192 | 1.44k | return l / r; |
193 | 1.44k | } |
194 | | |
195 | | std::int64_t cmExprParserHelper::Mod(std::int64_t l, std::int64_t r) |
196 | 1.42k | { |
197 | 1.42k | if (r == 0) { |
198 | 4 | throw std::overflow_error("modulo by zero"); |
199 | 4 | } |
200 | 1.41k | if (l == INT64_MIN && r == -1) { |
201 | 2 | throw std::overflow_error("signed integer overflow in modulo"); |
202 | 2 | } |
203 | 1.41k | return l % r; |
204 | 1.41k | } |
205 | | |
206 | | std::int64_t cmExprParserHelper::Neg(std::int64_t x) |
207 | 6.78k | { |
208 | 6.78k | if (static_cast<std::uint64_t>(x) == 0x8000000000000000) { |
209 | 1.83k | this->Warning(cmStrCat("signed integer cannot negate:\n ", x)); |
210 | 1.83k | return x; |
211 | 1.83k | } |
212 | 4.94k | return -x; |
213 | 6.78k | } |
214 | | |
215 | | // The __has_builtin preprocessor check was added in Clang 2.6 and GCC 10. |
216 | | // The __builtin_X_overflow intrinsics were added in Clang 3.4 and GCC 5. |
217 | | #ifndef __has_builtin |
218 | | # if defined(__GNUC__) && __GNUC__ >= 5 |
219 | | # define __has_builtin(x) 1 |
220 | | # else |
221 | | # define __has_builtin(x) 0 |
222 | | # endif |
223 | | #endif |
224 | | |
225 | | bool cmExprParserHelper::AddOverflow(long l, long r, long* p) |
226 | 919 | { |
227 | 919 | #if __has_builtin(__builtin_saddl_overflow) |
228 | 919 | return __builtin_saddl_overflow(l, r, p); |
229 | | #else |
230 | | *p = l + r; |
231 | | return false; |
232 | | #endif |
233 | 919 | } |
234 | | |
235 | | bool cmExprParserHelper::AddOverflow(long long l, long long r, long long* p) |
236 | 0 | { |
237 | 0 | #if __has_builtin(__builtin_saddll_overflow) |
238 | 0 | return __builtin_saddll_overflow(l, r, p); |
239 | | #else |
240 | | *p = l + r; |
241 | | return false; |
242 | | #endif |
243 | 0 | } |
244 | | |
245 | | bool cmExprParserHelper::SubOverflow(long l, long r, long* p) |
246 | 2.58k | { |
247 | 2.58k | #if __has_builtin(__builtin_ssubl_overflow) |
248 | 2.58k | return __builtin_ssubl_overflow(l, r, p); |
249 | | #else |
250 | | *p = l - r; |
251 | | return false; |
252 | | #endif |
253 | 2.58k | } |
254 | | |
255 | | bool cmExprParserHelper::SubOverflow(long long l, long long r, long long* p) |
256 | 0 | { |
257 | 0 | #if __has_builtin(__builtin_ssubll_overflow) |
258 | 0 | return __builtin_ssubll_overflow(l, r, p); |
259 | | #else |
260 | | *p = l - r; |
261 | | return false; |
262 | | #endif |
263 | 0 | } |
264 | | |
265 | | bool cmExprParserHelper::MulOverflow(long l, long r, long* p) |
266 | 952 | { |
267 | 952 | #if __has_builtin(__builtin_smull_overflow) |
268 | 952 | return __builtin_smull_overflow(l, r, p); |
269 | | #else |
270 | | *p = l * r; |
271 | | return false; |
272 | | #endif |
273 | 952 | } |
274 | | |
275 | | bool cmExprParserHelper::MulOverflow(long long l, long long r, long long* p) |
276 | 0 | { |
277 | 0 | #if __has_builtin(__builtin_smulll_overflow) |
278 | 0 | return __builtin_smulll_overflow(l, r, p); |
279 | | #else |
280 | | *p = l * r; |
281 | | return false; |
282 | | #endif |
283 | 0 | } |