Line data Source code
1 : // Copyright (c) 2019-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/prinbee
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // prinbee
20 : //
21 : #include <prinbee/pbql/lexer.h>
22 :
23 : #include <prinbee/exception.h>
24 :
25 :
26 : // self
27 : //
28 : #include "catch_main.h"
29 :
30 :
31 : // libuf8
32 : //
33 : #include <libutf8/libutf8.h>
34 :
35 :
36 : // snaplogger
37 : //
38 : #include <snaplogger/message.h>
39 :
40 :
41 : // snapdev
42 : //
43 : //#include <snapdev/hexadecimal_string.h>
44 : //#include <snapdev/math.h>
45 : //#include <snapdev/ostream_int128.h>
46 :
47 :
48 : // C++
49 : //
50 : #include <bitset>
51 : //#include <fstream>
52 : //#include <iomanip>
53 :
54 :
55 : // C
56 : //
57 : //#include <sys/stat.h>
58 : //#include <sys/types.h>
59 :
60 :
61 : // last include
62 : //
63 : #include <snapdev/poison.h>
64 :
65 :
66 :
67 : namespace
68 : {
69 :
70 :
71 :
72 :
73 :
74 : } // no name namespace
75 :
76 :
77 :
78 9 : CATCH_TEST_CASE("lexer", "[lexer] [pbql]")
79 : {
80 11 : CATCH_START_SECTION("lexer: verify tokens")
81 : {
82 : class script_t
83 : {
84 : public:
85 : struct value_t
86 : {
87 : std::string f_string = std::string();
88 : //uint512_t f_integer = uint512_t(); -- in this test, we do not want to verify super large numbers
89 : std::int64_t f_integer = 0;
90 : long double f_floating_point = 0.0;
91 : };
92 :
93 28 : void add_line(prinbee::pbql::token_t token, std::string const & s)
94 : {
95 28 : append_to_script(s);
96 28 : f_tokens.push_back(token);
97 28 : f_values.push_back(value_t());
98 28 : }
99 :
100 12 : void add_line(prinbee::pbql::token_t token, std::string const & s, std::string string)
101 : {
102 12 : append_to_script(s);
103 12 : f_tokens.push_back(token);
104 12 : value_t v;
105 12 : v.f_string = string;
106 12 : f_values.push_back(v);
107 24 : }
108 :
109 14 : void add_line(prinbee::pbql::token_t token, std::string const & s, std::int64_t integer)
110 : {
111 14 : append_to_script(s);
112 14 : value_t v;
113 14 : f_tokens.push_back(token);
114 14 : v.f_integer = integer;
115 14 : f_values.push_back(v);
116 28 : }
117 :
118 4 : void add_line(prinbee::pbql::token_t token, std::string const & s, long double floating_point)
119 : {
120 4 : append_to_script(s);
121 4 : f_tokens.push_back(token);
122 4 : value_t v;
123 4 : v.f_floating_point = floating_point;
124 4 : f_values.push_back(v);
125 8 : }
126 :
127 1 : void tokenize() const
128 : {
129 3 : std::string const filename("./lexer-tokens.pbql");
130 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(f_script, filename));
131 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
132 1 : lexer->set_input(in);
133 :
134 1 : CATCH_REQUIRE(f_tokens.size() == f_values.size());
135 59 : for(std::size_t idx(0); idx < f_tokens.size(); ++idx)
136 : {
137 58 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
138 58 : CATCH_REQUIRE(n->get_token() == f_tokens[idx]);
139 :
140 58 : prinbee::pbql::location const & loc(n->get_location());
141 58 : CATCH_REQUIRE(loc.get_filename() == filename);
142 58 : CATCH_REQUIRE(loc.get_column() == 1); // poor test for columns...
143 58 : CATCH_REQUIRE(loc.get_line() == static_cast<int>(idx + 1));
144 :
145 58 : CATCH_REQUIRE(n->get_string() == f_values[idx].f_string);
146 58 : CATCH_REQUIRE(n->get_integer() == f_values[idx].f_integer);
147 58 : bool const float_nearly_equal(SNAP_CATCH2_NAMESPACE::nearly_equal(n->get_floating_point(), f_values[idx].f_floating_point, 0.0L));
148 58 : if(!float_nearly_equal)
149 : {
150 0 : SNAP_LOG_FATAL
151 0 : << "floating points are not equal "
152 0 : << n->get_floating_point()
153 0 : << " vs "
154 0 : << f_values[idx].f_floating_point
155 : << SNAP_LOG_SEND;
156 : }
157 58 : CATCH_REQUIRE(float_nearly_equal);
158 58 : CATCH_REQUIRE(n->get_parent() == nullptr);
159 58 : CATCH_REQUIRE(n->get_children_size() == 0);
160 58 : }
161 :
162 : // after that we always get an end of file token
163 : //
164 11 : for(std::size_t count(0); count < 10; ++count)
165 : {
166 10 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
167 10 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
168 :
169 10 : prinbee::pbql::location const & loc(n->get_location());
170 10 : CATCH_REQUIRE(loc.get_filename() == filename);
171 10 : CATCH_REQUIRE(loc.get_column() == 1);
172 10 : CATCH_REQUIRE(loc.get_line() == static_cast<int>(f_tokens.size() + 1)); // EOF ends up on the last line + 1
173 :
174 10 : CATCH_REQUIRE(n->get_string() == std::string());
175 10 : CATCH_REQUIRE(n->get_integer() == 0);
176 10 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(n->get_floating_point(), 0.0L, 0.0L));
177 10 : CATCH_REQUIRE(n->get_parent() == nullptr);
178 10 : CATCH_REQUIRE(n->get_children_size() == 0);
179 10 : }
180 2 : }
181 :
182 : private:
183 58 : void append_to_script(std::string const & s)
184 : {
185 58 : f_script += s;
186 58 : if((rand() & 1) == 0)
187 : {
188 34 : f_script += '\r';
189 : }
190 58 : f_script += '\n';
191 58 : }
192 :
193 : std::string f_script = std::string();
194 : std::vector<prinbee::pbql::token_t> f_tokens = std::vector<prinbee::pbql::token_t>();
195 : std::vector<value_t> f_values = std::vector<value_t>();
196 : };
197 :
198 1 : script_t script;
199 :
200 : // WARNING: the '#' on the very first line / column is a special case
201 : // so try something else first
202 : //
203 3 : script.add_line(prinbee::pbql::token_t::TOKEN_MODULO, "%");
204 3 : script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_XOR, "#");
205 3 : script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_AND, "&");
206 3 : script.add_line(prinbee::pbql::token_t::TOKEN_OPEN_PARENTHESIS, "(");
207 3 : script.add_line(prinbee::pbql::token_t::TOKEN_CLOSE_PARENTHESIS, ")");
208 3 : script.add_line(prinbee::pbql::token_t::TOKEN_MULTIPLY, "*");
209 3 : script.add_line(prinbee::pbql::token_t::TOKEN_PLUS, "+");
210 3 : script.add_line(prinbee::pbql::token_t::TOKEN_COMMA, ",");
211 3 : script.add_line(prinbee::pbql::token_t::TOKEN_MINUS, "-");
212 3 : script.add_line(prinbee::pbql::token_t::TOKEN_DIVIDE, "/");
213 3 : script.add_line(prinbee::pbql::token_t::TOKEN_COLON, ":");
214 3 : script.add_line(prinbee::pbql::token_t::TOKEN_SEMI_COLON, ";");
215 3 : script.add_line(prinbee::pbql::token_t::TOKEN_EQUAL, "=");
216 3 : script.add_line(prinbee::pbql::token_t::TOKEN_ABSOLUTE_VALUE, "@");
217 3 : script.add_line(prinbee::pbql::token_t::TOKEN_POWER, "^");
218 3 : script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_OR, "|");
219 3 : script.add_line(prinbee::pbql::token_t::TOKEN_REGULAR_EXPRESSION, "~");
220 5 : script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER, "identifier", "identifier");
221 5 : script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER, "CAPS", "CAPS");
222 5 : script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER, "_123", "_123");
223 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "'\\no e\\fect'", "\\no e\\fect");
224 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'string\\n'", "string\n");
225 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "E'string\\r'", "string\r");
226 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\b\\f\\n\\r\\t'", "\b\f\n\r\t");
227 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\a\\g\\m\\s\\\\'", "agms\\");
228 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\58only 5'", "\058only 5");
229 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\339only 33'", "\339only 33");
230 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\xfg only f'", "\xfg only f");
231 5 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING, "e'\\xf: only f'", "\xf: only f");
232 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "123", 123L);
233 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0b11001010", 0xCAL);
234 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0B11001010", 0xCAL);
235 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "b'11001010'", 0xCAL);
236 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "B'11011110'", 0xDEL);
237 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0711", 711L); // this is not octal in SQL
238 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0o345", 0345L);
239 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0O346", 0346L);
240 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "o'365'", 0365L);
241 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "O'645'", 0645L);
242 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0xa9d1b1f", 0xa9d1b1fL);
243 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "0Xa3d1f1c", 0xa3d1f1cL);
244 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "x'a9d3b3f'", 0xa9d3b3fL);
245 3 : script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER, "X'a9d9d1f'", 0xa9d9d1fL);
246 3 : script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT, "5.12309", 5.12309L);
247 3 : script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT, "5.12309E3", 5123.09L);
248 3 : script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT, "7.83213e+3", 7832.13L);
249 3 : script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT, "7841.93e-3", 7.84193L);
250 3 : script.add_line(prinbee::pbql::token_t::TOKEN_NOT_EQUAL, "<>");
251 3 : script.add_line(prinbee::pbql::token_t::TOKEN_LESS, "<");
252 3 : script.add_line(prinbee::pbql::token_t::TOKEN_LESS_EQUAL, "<=");
253 3 : script.add_line(prinbee::pbql::token_t::TOKEN_GREATER, ">");
254 3 : script.add_line(prinbee::pbql::token_t::TOKEN_GREATER_EQUAL, ">=");
255 3 : script.add_line(prinbee::pbql::token_t::TOKEN_SQUARE_ROOT, "|/");
256 3 : script.add_line(prinbee::pbql::token_t::TOKEN_CUBE_ROOT, "||/");
257 3 : script.add_line(prinbee::pbql::token_t::TOKEN_SCOPE, "::");
258 3 : script.add_line(prinbee::pbql::token_t::TOKEN_SHIFT_LEFT, "<<");
259 3 : script.add_line(prinbee::pbql::token_t::TOKEN_SHIFT_RIGHT, ">>");
260 3 : script.add_line(prinbee::pbql::token_t::TOKEN_STRING_CONCAT, "||");
261 :
262 1 : script.tokenize();
263 1 : }
264 10 : CATCH_END_SECTION()
265 :
266 11 : CATCH_START_SECTION("lexer: binary 0 to 255")
267 : {
268 257 : for(int v(0); v < 256; ++v)
269 : {
270 256 : std::bitset<8> binary(v);
271 256 : std::stringstream ss;
272 256 : ss << "0b" << binary;
273 :
274 256 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-binary.pbql"));
275 256 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
276 256 : lexer->set_input(in);
277 :
278 256 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
279 256 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_INTEGER);
280 256 : CATCH_REQUIRE(n->get_integer() == v);
281 256 : }
282 : }
283 10 : CATCH_END_SECTION()
284 :
285 11 : CATCH_START_SECTION("lexer: octal characters 1 to 255")
286 : {
287 256 : for(int c(1); c < 256; ++c)
288 : {
289 255 : int const zeroes(c < 8 ? rand() % 3 : (c < 64 ? rand() % 2 : 0));
290 255 : std::stringstream ss;
291 255 : ss << (rand() & 1 ? 'e' : 'E')
292 255 : << "'\\"
293 255 : << std::oct
294 1020 : << std::string(zeroes, '0')
295 : << c
296 510 : << '\'';
297 :
298 255 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-octal-char.pbql"));
299 255 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
300 255 : lexer->set_input(in);
301 :
302 255 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
303 255 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
304 255 : if(c < 0x80)
305 : {
306 381 : CATCH_REQUIRE(n->get_string() == std::string(1, c));
307 : }
308 : else
309 : {
310 128 : std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
311 128 : CATCH_REQUIRE(n->get_string() == expected);
312 128 : }
313 255 : }
314 : }
315 10 : CATCH_END_SECTION()
316 :
317 11 : CATCH_START_SECTION("lexer: hexadecimal characters 1 to 255")
318 : {
319 256 : for(int c(1); c < 256; ++c)
320 : {
321 255 : std::stringstream ss;
322 255 : if((rand() & 1) != 0)
323 : {
324 114 : ss << std::uppercase;
325 : }
326 255 : ss << (rand() & 1 ? 'e' : 'E')
327 : << "'\\"
328 255 : << (rand() & 1 ? 'x' : 'X')
329 255 : << std::hex
330 255 : << (c < 16 ? ((rand() & 1) != 0 ? "" : "0") : "")
331 : << c
332 255 : << '\'';
333 :
334 255 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-hexadecimal-char.pbql"));
335 255 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
336 255 : lexer->set_input(in);
337 :
338 255 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
339 255 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
340 255 : if(c < 0x80)
341 : {
342 381 : CATCH_REQUIRE(n->get_string() == std::string(1, c));
343 : }
344 : else
345 : {
346 128 : std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
347 128 : CATCH_REQUIRE(n->get_string() == expected);
348 128 : }
349 255 : }
350 : }
351 10 : CATCH_END_SECTION()
352 :
353 11 : CATCH_START_SECTION("lexer: 4 digits unicode characters")
354 : {
355 63488 : for(int c(1); c < 0x10000; ++c)
356 : {
357 : // skip surrogates
358 : //
359 63487 : if(c == 0xD800)
360 : {
361 1 : c = 0xE000;
362 : }
363 63487 : std::stringstream ss;
364 63487 : if((rand() & 1) != 0)
365 : {
366 31684 : ss << std::uppercase;
367 : }
368 63487 : ss << (rand() & 1 ? 'e' : 'E')
369 63487 : << "'\\u"
370 63487 : << std::hex
371 : << std::setfill('0')
372 63487 : << std::setw(4)
373 : << c
374 63487 : << '\'';
375 :
376 63487 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-plane0-unicode-char.pbql"));
377 63487 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
378 63487 : lexer->set_input(in);
379 :
380 63487 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
381 63487 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
382 63487 : if(c < 0x80)
383 : {
384 381 : CATCH_REQUIRE(n->get_string() == std::string(1, c));
385 : }
386 : else
387 : {
388 63360 : std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
389 63360 : CATCH_REQUIRE(n->get_string() == expected);
390 63360 : }
391 63487 : }
392 : }
393 10 : CATCH_END_SECTION()
394 :
395 11 : CATCH_START_SECTION("lexer: 8 digits unicode characters")
396 : {
397 1001 : for(int count(0); count < 1000; ++count)
398 : {
399 1000 : int const c(SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE));
400 1000 : std::stringstream ss;
401 1000 : if((rand() & 1) != 0)
402 : {
403 507 : ss << std::uppercase;
404 : }
405 1000 : ss << (rand() & 1 ? 'e' : 'E')
406 1000 : << "'\\U"
407 1000 : << std::hex
408 : << std::setfill('0')
409 1000 : << std::setw(8)
410 : << c
411 1000 : << '\'';
412 :
413 1000 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-any-unicode-char.pbql"));
414 1000 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
415 1000 : lexer->set_input(in);
416 :
417 1000 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
418 1000 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
419 1000 : if(c < 0x80)
420 : {
421 0 : CATCH_REQUIRE(n->get_string() == std::string(1, c));
422 : }
423 : else
424 : {
425 1000 : std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
426 1000 : CATCH_REQUIRE(n->get_string() == expected);
427 1000 : }
428 1000 : }
429 : }
430 10 : CATCH_END_SECTION()
431 :
432 11 : CATCH_START_SECTION("lexer: hash comment at top of file (1 line)")
433 : {
434 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("# hash at the start is viewed as a comment!\nthis is not", "./lexer-hash-comment.pbql"));
435 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
436 1 : lexer->set_input(in);
437 :
438 1 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
439 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
440 1 : CATCH_REQUIRE(n->get_string() == "this");
441 1 : prinbee::pbql::location const & loc(n->get_location());
442 1 : CATCH_REQUIRE(loc.get_line() == 2);
443 :
444 1 : n = lexer->get_next_token();
445 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
446 1 : CATCH_REQUIRE(n->get_string() == "is");
447 :
448 1 : n = lexer->get_next_token();
449 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
450 1 : CATCH_REQUIRE(n->get_string() == "not");
451 :
452 1 : n = lexer->get_next_token();
453 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
454 1 : }
455 10 : CATCH_END_SECTION()
456 :
457 11 : CATCH_START_SECTION("lexer: hash comment at top of file (3 lines)")
458 : {
459 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(
460 : "#!/usr/bin/pbql -e\n"
461 : "# initialization script for website tables\n"
462 : "# and some default system data\n"
463 1 : "CREATE TABLE /* C-like comment */ magic;", "./lexer-hash-comment.pbql"));
464 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
465 1 : lexer->set_input(in);
466 :
467 1 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
468 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
469 1 : CATCH_REQUIRE(n->get_string() == "CREATE");
470 1 : prinbee::pbql::location const * loc(&n->get_location());
471 1 : CATCH_REQUIRE(loc->get_line() == 4);
472 1 : CATCH_REQUIRE(loc->get_column() == 2); // this is a known bug... the getc()+ungetc() generate a location bug
473 :
474 1 : n = lexer->get_next_token();
475 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
476 1 : CATCH_REQUIRE(n->get_string() == "TABLE");
477 1 : loc = &n->get_location();
478 1 : CATCH_REQUIRE(loc->get_line() == 4);
479 1 : CATCH_REQUIRE(loc->get_column() == 8);
480 :
481 1 : n = lexer->get_next_token();
482 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
483 1 : CATCH_REQUIRE(n->get_string() == "magic");
484 1 : loc = &n->get_location();
485 1 : CATCH_REQUIRE(loc->get_line() == 4);
486 1 : CATCH_REQUIRE(loc->get_column() == 35);
487 :
488 1 : n = lexer->get_next_token();
489 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_SEMI_COLON);
490 1 : loc = &n->get_location();
491 1 : CATCH_REQUIRE(loc->get_line() == 4);
492 1 : CATCH_REQUIRE(loc->get_column() == 41); // same bug as above, we've read the ';' then did an ungetc() which does not correct the column
493 :
494 1 : n = lexer->get_next_token();
495 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
496 1 : }
497 10 : CATCH_END_SECTION()
498 :
499 11 : CATCH_START_SECTION("lexer: dash-dash comment")
500 : {
501 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(
502 : "/* copyright notices\n"
503 : " * often go here\n"
504 : " */\n"
505 : "CREATE TABLE test ( -- list of columns below\n"
506 : " name TEXT,\n"
507 : " -- the name above should be limited in length\r\n"
508 : " email TEXT, -- email should include an '@' character\n"
509 : " address TEXT,\n"
510 : "-- comment from the start of the line\r"
511 : " age INTEGER /* and C-like /* comments can be */ nested */\n"
512 : "); /*** multi-asterisks ***/\n"
513 : "-- vim: comment\n"
514 1 : , "./lexer-hash-comment.pbql"));
515 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
516 1 : lexer->set_input(in);
517 :
518 : // "CREATE TABLE test ("
519 1 : prinbee::pbql::node::pointer_t n(lexer->get_next_token());
520 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
521 1 : CATCH_REQUIRE(n->get_string() == "CREATE");
522 1 : prinbee::pbql::location const * loc(&n->get_location());
523 1 : CATCH_REQUIRE(loc->get_line() == 4);
524 1 : CATCH_REQUIRE(loc->get_column() == 1);
525 :
526 1 : n = lexer->get_next_token();
527 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
528 1 : CATCH_REQUIRE(n->get_string() == "TABLE");
529 1 : loc = &n->get_location();
530 1 : CATCH_REQUIRE(loc->get_line() == 4);
531 1 : CATCH_REQUIRE(loc->get_column() == 8);
532 :
533 1 : n = lexer->get_next_token();
534 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
535 1 : CATCH_REQUIRE(n->get_string() == "test");
536 1 : loc = &n->get_location();
537 1 : CATCH_REQUIRE(loc->get_line() == 4);
538 1 : CATCH_REQUIRE(loc->get_column() == 14);
539 :
540 1 : n = lexer->get_next_token();
541 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_OPEN_PARENTHESIS);
542 1 : loc = &n->get_location();
543 1 : CATCH_REQUIRE(loc->get_line() == 4);
544 1 : CATCH_REQUIRE(loc->get_column() == 19);
545 :
546 : // "name TEXT,"
547 1 : n = lexer->get_next_token();
548 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
549 1 : CATCH_REQUIRE(n->get_string() == "name");
550 1 : loc = &n->get_location();
551 1 : CATCH_REQUIRE(loc->get_line() == 5);
552 1 : CATCH_REQUIRE(loc->get_column() == 3);
553 :
554 1 : n = lexer->get_next_token();
555 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
556 1 : CATCH_REQUIRE(n->get_string() == "TEXT");
557 1 : loc = &n->get_location();
558 1 : CATCH_REQUIRE(loc->get_line() == 5);
559 1 : CATCH_REQUIRE(loc->get_column() == 8);
560 :
561 1 : n = lexer->get_next_token();
562 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
563 1 : loc = &n->get_location();
564 1 : CATCH_REQUIRE(loc->get_line() == 5);
565 1 : CATCH_REQUIRE(loc->get_column() == 13);
566 :
567 : // "email TEXT,"
568 1 : n = lexer->get_next_token();
569 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
570 1 : CATCH_REQUIRE(n->get_string() == "email");
571 1 : loc = &n->get_location();
572 1 : CATCH_REQUIRE(loc->get_line() == 7);
573 1 : CATCH_REQUIRE(loc->get_column() == 3);
574 :
575 1 : n = lexer->get_next_token();
576 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
577 1 : CATCH_REQUIRE(n->get_string() == "TEXT");
578 1 : loc = &n->get_location();
579 1 : CATCH_REQUIRE(loc->get_line() == 7);
580 1 : CATCH_REQUIRE(loc->get_column() == 9);
581 :
582 1 : n = lexer->get_next_token();
583 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
584 1 : loc = &n->get_location();
585 1 : CATCH_REQUIRE(loc->get_line() == 7);
586 1 : CATCH_REQUIRE(loc->get_column() == 14);
587 :
588 : // "address TEXT,"
589 1 : n = lexer->get_next_token();
590 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
591 1 : CATCH_REQUIRE(n->get_string() == "address");
592 1 : loc = &n->get_location();
593 1 : CATCH_REQUIRE(loc->get_line() == 8);
594 1 : CATCH_REQUIRE(loc->get_column() == 3);
595 :
596 1 : n = lexer->get_next_token();
597 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
598 1 : CATCH_REQUIRE(n->get_string() == "TEXT");
599 1 : loc = &n->get_location();
600 1 : CATCH_REQUIRE(loc->get_line() == 8);
601 1 : CATCH_REQUIRE(loc->get_column() == 11);
602 :
603 1 : n = lexer->get_next_token();
604 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
605 1 : loc = &n->get_location();
606 1 : CATCH_REQUIRE(loc->get_line() == 8);
607 1 : CATCH_REQUIRE(loc->get_column() == 16);
608 :
609 : // "age INTEGER"
610 1 : n = lexer->get_next_token();
611 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
612 1 : CATCH_REQUIRE(n->get_string() == "age");
613 1 : loc = &n->get_location();
614 1 : CATCH_REQUIRE(loc->get_line() == 10);
615 1 : CATCH_REQUIRE(loc->get_column() == 3);
616 :
617 1 : n = lexer->get_next_token();
618 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
619 1 : CATCH_REQUIRE(n->get_string() == "INTEGER");
620 1 : loc = &n->get_location();
621 1 : CATCH_REQUIRE(loc->get_line() == 10);
622 1 : CATCH_REQUIRE(loc->get_column() == 7);
623 :
624 : // ");"
625 1 : n = lexer->get_next_token();
626 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_CLOSE_PARENTHESIS);
627 1 : loc = &n->get_location();
628 1 : CATCH_REQUIRE(loc->get_line() == 11);
629 1 : CATCH_REQUIRE(loc->get_column() == 1);
630 :
631 1 : n = lexer->get_next_token();
632 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_SEMI_COLON);
633 1 : loc = &n->get_location();
634 1 : CATCH_REQUIRE(loc->get_line() == 11);
635 1 : CATCH_REQUIRE(loc->get_column() == 2);
636 :
637 : // EOF
638 1 : n = lexer->get_next_token();
639 1 : CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
640 1 : }
641 10 : CATCH_END_SECTION()
642 9 : }
643 :
644 :
645 18 : CATCH_TEST_CASE("lexer_error", "[lexer] [pbql] [error]")
646 : {
647 20 : CATCH_START_SECTION("lexer_error: missing input")
648 : {
649 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
650 4 : CATCH_REQUIRE_THROWS_MATCHES(
651 : lexer->get_next_token()
652 : , prinbee::logic_error
653 : , Catch::Matchers::ExceptionMessage(
654 : "logic_error: input missing."));
655 1 : }
656 19 : CATCH_END_SECTION()
657 :
658 20 : CATCH_START_SECTION("lexer_error: invalid string (EOF)")
659 : {
660 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string not ended", "./lexer-bad-string.pbql"));
661 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
662 1 : lexer->set_input(in);
663 :
664 4 : CATCH_REQUIRE_THROWS_MATCHES(
665 : lexer->get_next_token()
666 : , prinbee::invalid_token
667 : , Catch::Matchers::ExceptionMessage(
668 : "prinbee_exception: unclosed string."));
669 1 : }
670 19 : CATCH_END_SECTION()
671 :
672 20 : CATCH_START_SECTION("lexer_error: invalid string (\\n)")
673 : {
674 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\ncut 1'", "./lexer-bad-string.pbql"));
675 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
676 1 : lexer->set_input(in);
677 :
678 4 : CATCH_REQUIRE_THROWS_MATCHES(
679 : lexer->get_next_token()
680 : , prinbee::invalid_token
681 : , Catch::Matchers::ExceptionMessage(
682 : "prinbee_exception: string cannot include a newline or carriage return character."));
683 1 : }
684 19 : CATCH_END_SECTION()
685 :
686 20 : CATCH_START_SECTION("lexer_error: invalid string (\\r)")
687 : {
688 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\rcut 2'", "./lexer-bad-string.pbql"));
689 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
690 1 : lexer->set_input(in);
691 :
692 4 : CATCH_REQUIRE_THROWS_MATCHES(
693 : lexer->get_next_token()
694 : , prinbee::invalid_token
695 : , Catch::Matchers::ExceptionMessage(
696 : "prinbee_exception: string cannot include a newline or carriage return character."));
697 1 : }
698 19 : CATCH_END_SECTION()
699 :
700 20 : CATCH_START_SECTION("lexer_error: invalid string (\\r\\n)")
701 : {
702 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\rcut 3'", "./lexer-bad-string.pbql"));
703 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
704 1 : lexer->set_input(in);
705 :
706 4 : CATCH_REQUIRE_THROWS_MATCHES(
707 : lexer->get_next_token()
708 : , prinbee::invalid_token
709 : , Catch::Matchers::ExceptionMessage(
710 : "prinbee_exception: string cannot include a newline or carriage return character."));
711 1 : }
712 19 : CATCH_END_SECTION()
713 :
714 20 : CATCH_START_SECTION("lexer_error: invalid string (escaped characters)")
715 : {
716 : struct invalid_escape_t
717 : {
718 : char const * const f_invalid_sequence = nullptr;
719 : int f_count = 0;
720 : int f_expected_count = 0;
721 : };
722 1 : constexpr invalid_escape_t const invalid_escapes[] =
723 : {
724 : { "\\xvoid", 0, 2 },
725 : { "\\uvoid", 0, 4 },
726 : { "\\u1", 1, 4 },
727 : { "\\u21", 2, 4 },
728 : { "\\u311", 3, 4 },
729 : { "\\Uvoid", 0, 8 },
730 : { "\\U1", 1, 8 },
731 : { "\\U21", 2, 8 },
732 : { "\\U311", 3, 8 },
733 : { "\\U4111", 4, 8 },
734 : { "\\U51111", 5, 8 },
735 : { "\\U611111", 6, 8 },
736 : { "\\U7111111", 7, 8 },
737 : };
738 14 : for(auto const & e : invalid_escapes)
739 : {
740 39 : std::string str("e'str: ");
741 13 : str += e.f_invalid_sequence;
742 13 : str += '\'';
743 13 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(str, "./lexer-bad-escape-sequence.pbql"));
744 13 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
745 13 : lexer->set_input(in);
746 :
747 26 : CATCH_REQUIRE_THROWS_MATCHES(
748 : lexer->get_next_token()
749 : , prinbee::invalid_token
750 : , Catch::Matchers::ExceptionMessage(
751 : "prinbee_exception: lexer::get_next_token() -- escape sequence needed "
752 : + std::to_string(e.f_expected_count)
753 : + " digits; found "
754 : + std::to_string(e.f_count)
755 : + " instead."));
756 13 : }
757 : }
758 19 : CATCH_END_SECTION()
759 :
760 20 : CATCH_START_SECTION("lexer_error: \\0 is not allowed in strings")
761 : {
762 : struct invalid_escape_t
763 : {
764 : char const * const f_invalid_sequence = nullptr;
765 : };
766 1 : constexpr invalid_escape_t const invalid_escapes[] =
767 : {
768 : { "octal null \\0 -- size of 1" },
769 : { "octal null \\00 -- size of 2" },
770 : { "octal null \\000 -- size of 3" },
771 : { "hexadecimal null \\x0 --size of 1" },
772 : { "hexadecimal null \\x00 --size of 2" },
773 : { "unicode \\u0000 -- size of 4" },
774 : { "unicode \\U00000000 -- size of 8" },
775 : };
776 8 : for(auto const & e : invalid_escapes)
777 : {
778 21 : std::string str("e'str: ");
779 7 : str += e.f_invalid_sequence;
780 7 : str += '\'';
781 7 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(str, "./lexer-bad-null-character.pbql"));
782 7 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
783 7 : lexer->set_input(in);
784 :
785 28 : CATCH_REQUIRE_THROWS_MATCHES(
786 : lexer->get_next_token()
787 : , prinbee::unexpected_token
788 : , Catch::Matchers::ExceptionMessage(
789 : "prinbee_exception: lexer::get_next_token() -- the NULL character is not allowed in strings."));
790 7 : }
791 : }
792 19 : CATCH_END_SECTION()
793 :
794 20 : CATCH_START_SECTION("lexer_error: missing */")
795 : {
796 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("/* C-Like comment must end with '*' and '/'", "./lexer-bad-c-comment.pbql"));
797 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
798 1 : lexer->set_input(in);
799 :
800 4 : CATCH_REQUIRE_THROWS_MATCHES(
801 : lexer->get_next_token()
802 : , prinbee::invalid_token
803 : , Catch::Matchers::ExceptionMessage(
804 : "prinbee_exception: end of script reached within a C-like comment (i.e. '*/' not found; depth: 1)."));
805 1 : }
806 19 : CATCH_END_SECTION()
807 :
808 20 : CATCH_START_SECTION("lexer_error: invalid floating point")
809 : {
810 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("7041.03e", "./lexer-bad-string.pbql"));
811 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
812 1 : lexer->set_input(in);
813 :
814 4 : CATCH_REQUIRE_THROWS_MATCHES(
815 : lexer->get_next_token()
816 : , prinbee::invalid_number
817 : , Catch::Matchers::ExceptionMessage(
818 : "prinbee_exception: invalid floating point number (7041.03e)."));
819 1 : }
820 19 : CATCH_END_SECTION()
821 :
822 20 : CATCH_START_SECTION("lexer_error: empty binary number")
823 : {
824 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0b", "./lexer-bad-binary.pbql"));
825 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
826 1 : lexer->set_input(in);
827 :
828 4 : CATCH_REQUIRE_THROWS_MATCHES(
829 : lexer->get_next_token()
830 : , prinbee::invalid_number
831 : , Catch::Matchers::ExceptionMessage(
832 : "prinbee_exception: a binary number needs at least one digit."));
833 1 : }
834 19 : CATCH_END_SECTION()
835 :
836 20 : CATCH_START_SECTION("lexer_error: binary does not accept 8 or 9")
837 : {
838 9 : for(int digit(2); digit < 10; ++digit)
839 : {
840 8 : std::string const bin("0b" + std::to_string(digit));
841 8 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(bin, "./lexer-bad-binary.pbql"));
842 8 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
843 8 : lexer->set_input(in);
844 :
845 32 : CATCH_REQUIRE_THROWS_MATCHES(
846 : lexer->get_next_token()
847 : , prinbee::invalid_number
848 : , Catch::Matchers::ExceptionMessage(
849 : "prinbee_exception: a binary number only supports binary digits (0 and 1)."));
850 8 : }
851 : }
852 19 : CATCH_END_SECTION()
853 :
854 20 : CATCH_START_SECTION("lexer_error: binary string not ending with quote")
855 : {
856 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("b'101 missing closing quote", "./lexer-bad-binary.pbql"));
857 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
858 1 : lexer->set_input(in);
859 :
860 4 : CATCH_REQUIRE_THROWS_MATCHES(
861 : lexer->get_next_token()
862 : , prinbee::invalid_number
863 : , Catch::Matchers::ExceptionMessage(
864 : "prinbee_exception: a binary string needs to end with a quote (')."));
865 1 : }
866 19 : CATCH_END_SECTION()
867 :
868 20 : CATCH_START_SECTION("lexer_error: empty octal number")
869 : {
870 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0o", "./lexer-bad-octal.pbql"));
871 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
872 1 : lexer->set_input(in);
873 :
874 4 : CATCH_REQUIRE_THROWS_MATCHES(
875 : lexer->get_next_token()
876 : , prinbee::invalid_number
877 : , Catch::Matchers::ExceptionMessage(
878 : "prinbee_exception: an octal number needs at least one digit after the \"0o\"."));
879 1 : }
880 19 : CATCH_END_SECTION()
881 :
882 20 : CATCH_START_SECTION("lexer_error: octal does not accept 8 or 9")
883 : {
884 1 : char const * const bad_octal[] = {
885 : "0o8",
886 : "0o9",
887 : };
888 3 : for(int idx(0); idx < 2; ++idx)
889 : {
890 2 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(bad_octal[idx], "./lexer-bad-octal.pbql"));
891 2 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
892 2 : lexer->set_input(in);
893 :
894 8 : CATCH_REQUIRE_THROWS_MATCHES(
895 : lexer->get_next_token()
896 : , prinbee::invalid_number
897 : , Catch::Matchers::ExceptionMessage(
898 : "prinbee_exception: an octal number cannot include digits 8 or 9."));
899 2 : }
900 : }
901 19 : CATCH_END_SECTION()
902 :
903 20 : CATCH_START_SECTION("lexer_error: octal string not ending with quote")
904 : {
905 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("o'123 missing closing quote", "./lexer-bad-octal.pbql"));
906 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
907 1 : lexer->set_input(in);
908 :
909 4 : CATCH_REQUIRE_THROWS_MATCHES(
910 : lexer->get_next_token()
911 : , prinbee::invalid_number
912 : , Catch::Matchers::ExceptionMessage(
913 : "prinbee_exception: an octal string needs to end with a quote (')."));
914 1 : }
915 19 : CATCH_END_SECTION()
916 :
917 20 : CATCH_START_SECTION("lexer_error: empty hexadecimal number")
918 : {
919 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0x", "./lexer-bad-hexadecimal.pbql"));
920 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
921 1 : lexer->set_input(in);
922 :
923 4 : CATCH_REQUIRE_THROWS_MATCHES(
924 : lexer->get_next_token()
925 : , prinbee::invalid_number
926 : , Catch::Matchers::ExceptionMessage(
927 : "prinbee_exception: a hexadecimal number needs at least one digit after the \"0x\"."));
928 1 : }
929 19 : CATCH_END_SECTION()
930 :
931 20 : CATCH_START_SECTION("lexer_error: hexadecimal string not ending with quote")
932 : {
933 1 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("x'f1a3 missing closing quote", "./lexer-bad-hexadecimal.pbql"));
934 1 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
935 1 : lexer->set_input(in);
936 :
937 4 : CATCH_REQUIRE_THROWS_MATCHES(
938 : lexer->get_next_token()
939 : , prinbee::invalid_number
940 : , Catch::Matchers::ExceptionMessage(
941 : "prinbee_exception: a hexadecimal string needs to end with a quote (')."));
942 1 : }
943 19 : CATCH_END_SECTION()
944 :
945 20 : CATCH_START_SECTION("lexer_error: unsupported characters")
946 : {
947 1 : char const unsupported_characters[] = {
948 : '`',
949 : '!',
950 : '$',
951 : '{',
952 : '}',
953 : '"',
954 : '?',
955 : };
956 :
957 16 : for(std::size_t idx(0); idx < std::size(unsupported_characters); ++idx)
958 : {
959 7 : char const buf[2] = {
960 7 : unsupported_characters[idx],
961 : '\0',
962 7 : };
963 7 : prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(buf, "./lexer-bad-character.pbql"));
964 7 : prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
965 7 : lexer->set_input(in);
966 :
967 28 : CATCH_REQUIRE_THROWS_MATCHES(
968 : lexer->get_next_token()
969 : , prinbee::unexpected_token
970 : , Catch::Matchers::ExceptionMessage(
971 : std::string("prinbee_exception: unexpected token (")
972 : + buf
973 : + ")."));
974 7 : }
975 : }
976 19 : CATCH_END_SECTION()
977 18 : }
978 :
979 :
980 :
981 : // vim: ts=4 sw=4 et
|