Line data Source code
1 : // Copyright (c) 2011-2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/as2js
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 : // as2js
20 : //
21 : #include <as2js/parser.h>
22 :
23 : #include <as2js/exception.h>
24 : #include <as2js/json.h>
25 : #include <as2js/message.h>
26 :
27 :
28 : // self
29 : //
30 : #include "catch_main.h"
31 :
32 :
33 : // C++
34 : //
35 : #include <cstring>
36 : #include <algorithm>
37 : #include <iomanip>
38 :
39 :
40 : // C
41 : //
42 : #include <unistd.h>
43 : #include <sys/stat.h>
44 :
45 :
46 : // last include
47 : //
48 : #include <snapdev/poison.h>
49 :
50 :
51 :
52 :
53 : namespace
54 : {
55 :
56 :
57 :
58 :
59 :
60 : //
61 : // JSON data used to test the parser, most of the work is in this table
62 : // these are long JSON strings! It is actually generated using the
63 : // json_to_string tool and the parser_data/*.json source files.
64 : //
65 : // Note: the top entries are arrays so we can execute programs in the
66 : // order we define them...
67 : //
68 : char const g_array[] =
69 : #include "parser_data/array.ci"
70 : ;
71 : char const g_basics[] =
72 : #include "parser_data/basics.ci"
73 : ;
74 : char const g_class[] =
75 : #include "parser_data/class.ci"
76 : ;
77 : char const g_enum[] =
78 : #include "parser_data/enum.ci"
79 : ;
80 : char const g_expression[] =
81 : #include "parser_data/expression.ci"
82 : ;
83 : char const g_if[] =
84 : #include "parser_data/if.ci"
85 : ;
86 : char const g_for[] =
87 : #include "parser_data/for.ci"
88 : ;
89 : char const g_function[] =
90 : #include "parser_data/function.ci"
91 : ;
92 : char const g_pragma[] =
93 : #include "parser_data/pragma.ci"
94 : ;
95 : char const g_regex[] =
96 : #include "parser_data/regex.ci"
97 : ;
98 : char const g_synchronized[] =
99 : #include "parser_data/synchronized.ci"
100 : ;
101 : char const g_switch[] =
102 : #include "parser_data/switch.ci"
103 : ;
104 : char const g_trycatch[] =
105 : #include "parser_data/trycatch.ci"
106 : ;
107 : char const g_type[] =
108 : #include "parser_data/type.ci"
109 : ;
110 : char const g_variable[] =
111 : #include "parser_data/variable.ci"
112 : ;
113 : char const g_while[] =
114 : #include "parser_data/while.ci"
115 : ;
116 : char const g_yield[] =
117 : #include "parser_data/yield.ci"
118 : ;
119 : // TODO: specialize all those parts!
120 : char const g_data[] =
121 : #include "parser_data/parser.ci"
122 : ;
123 :
124 :
125 :
126 :
127 :
128 :
129 :
130 :
131 : // This function runs all the tests defined in the
132 : // string 'data'
133 18 : void run_tests(char const *data, char const *filename)
134 : {
135 36 : std::string input_data(data);
136 :
137 18 : if(SNAP_CATCH2_NAMESPACE::g_save_parser_tests)
138 : {
139 0 : std::ofstream json_file;
140 0 : json_file.open(filename);
141 0 : CATCH_REQUIRE(json_file.is_open());
142 0 : json_file << "// To properly indent this JSON you may use http://json-indent.appspot.com/"
143 0 : << std::endl << data << std::endl;
144 0 : }
145 :
146 18 : as2js::input_stream<std::stringstream>::pointer_t in(std::make_shared<as2js::input_stream<std::stringstream>>());
147 18 : *in << input_data;
148 18 : as2js::json::pointer_t json_data(std::make_shared<as2js::json>());
149 36 : as2js::json::json_value::pointer_t json(json_data->parse(in));
150 :
151 : // verify that the parser() did not fail
152 : //
153 18 : CATCH_REQUIRE(json != nullptr);
154 18 : CATCH_REQUIRE(json->get_type() == as2js::json::json_value::type_t::JSON_TYPE_ARRAY);
155 :
156 36 : std::string const name_string("name");
157 36 : std::string const program_string("program");
158 36 : std::string const verbose_string("verbose");
159 36 : std::string const slow_string("slow");
160 36 : std::string const result_string("result");
161 36 : std::string const expected_messages_string("expected messages");
162 :
163 18 : as2js::json::json_value::array_t const& array(json->get_array());
164 18 : size_t const max_programs(array.size());
165 185 : for(size_t idx(0); idx < max_programs; ++idx)
166 : {
167 167 : as2js::json::json_value::pointer_t prog_obj(array[idx]);
168 167 : CATCH_REQUIRE(prog_obj->get_type() == as2js::json::json_value::type_t::JSON_TYPE_OBJECT);
169 167 : as2js::json::json_value::object_t const & prog(prog_obj->get_object());
170 :
171 167 : bool verbose(false);
172 167 : as2js::json::json_value::object_t::const_iterator verbose_it(prog.find(verbose_string));
173 167 : if(verbose_it != prog.end())
174 : {
175 5 : verbose = verbose_it->second->get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE;
176 : }
177 :
178 167 : bool slow(false);
179 167 : as2js::json::json_value::object_t::const_iterator slow_it(prog.find(slow_string));
180 167 : if(slow_it != prog.end())
181 : {
182 25 : slow = slow_it->second->get_type() == as2js::json::json_value::type_t::JSON_TYPE_TRUE;
183 : }
184 :
185 : // got a program, try to compile it with all the possible options
186 167 : as2js::json::json_value::pointer_t name(prog.find(name_string)->second);
187 167 : std::cout << " -- working on \"" << name->get_string() << "\" " << (slow ? "" : "...") << std::flush;
188 :
189 684199 : for(std::size_t opt(0); opt < (1ULL << SNAP_CATCH2_NAMESPACE::g_options_size); ++opt)
190 : {
191 684032 : if(slow && ((opt + 1) % 250) == 0)
192 : {
193 400 : std::cout << "." << std::flush;
194 : }
195 : //std::cerr << "\n***\n*** OPTIONS:";
196 684032 : as2js::options::pointer_t options(std::make_shared<as2js::options>());
197 8892416 : for(std::size_t o(0); o < SNAP_CATCH2_NAMESPACE::g_options_size; ++o)
198 : {
199 8208384 : if((opt & (1 << o)) != 0)
200 : {
201 4104192 : options->set_option(
202 4104192 : SNAP_CATCH2_NAMESPACE::g_options[o].f_option
203 4104192 : , options->get_option(SNAP_CATCH2_NAMESPACE::g_options[o].f_option)
204 4104192 : | SNAP_CATCH2_NAMESPACE::g_options[o].f_value);
205 : //std::cerr << " " << SNAP_CATCH2_NAMESPACE::g_options[o].f_name << "=" << SNAP_CATCH2_NAMESPACE::g_options[o].f_value;
206 : }
207 : }
208 : //std::cerr << "\n***\n";
209 :
210 684032 : as2js::json::json_value::pointer_t program_value(prog.find(program_string)->second);
211 684032 : std::string program_source(program_value->get_string());
212 : //std::cerr << "prog = [" << program_source << "]\n";
213 684032 : as2js::input_stream<std::stringstream>::pointer_t prog_text(std::make_shared<as2js::input_stream<std::stringstream>>());
214 684032 : *prog_text << program_source;
215 684032 : as2js::parser::pointer_t parser(std::make_shared<as2js::parser>(prog_text, options));
216 :
217 684032 : SNAP_CATCH2_NAMESPACE::test_callback tc(verbose);
218 :
219 684032 : as2js::json::json_value::object_t::const_iterator expected_msg_it(prog.find(expected_messages_string));
220 684032 : if(expected_msg_it != prog.end())
221 : {
222 : // the expected messages value must be an array
223 : //
224 602112 : as2js::message_level_t message_level(as2js::message_level_t::MESSAGE_LEVEL_INFO);
225 602112 : as2js::json::json_value::array_t const & msg_array(expected_msg_it->second->get_array());
226 602112 : size_t const max_msgs(msg_array.size());
227 2662400 : for(size_t j(0); j < max_msgs; ++j)
228 : {
229 2060288 : as2js::json::json_value::pointer_t message_value(msg_array[j]);
230 2060288 : as2js::json::json_value::object_t const & message(message_value->get_object());
231 :
232 2060288 : bool ignore_message(false);
233 :
234 : // if the "message level" is less than "info" then we need
235 : // to change the level otherwise we won't get that message
236 : //
237 2060288 : message_level = std::min(message_level, static_cast<as2js::message_level_t>(message.find("message level")->second->get_integer().get()));
238 :
239 2060288 : as2js::json::json_value::object_t::const_iterator const message_options_iterator(message.find("options"));
240 2060288 : if(message_options_iterator != message.end())
241 : {
242 : //{
243 : //as2js::json::json_value::object_t::const_iterator line_it(message.find("line #"));
244 : //if(line_it != message.end())
245 : //{
246 : // int64_t lines(line_it->second->get_integer().get());
247 : //std::cerr << "_________\nLine #" << lines << "\n";
248 : //}
249 : //else
250 : //std::cerr << "_________\nLine #<undefined>\n";
251 : //}
252 1093632 : std::string const message_options(message_options_iterator->second->get_string());
253 1093632 : for(char const * s(message_options.c_str()), *start(s);; ++s)
254 : {
255 27224064 : if(*s == ',' || *s == '|' || *s == '\0')
256 : {
257 2555904 : std::string const opt_name(start, s - start);
258 7696384 : for(std::size_t o(0); o < SNAP_CATCH2_NAMESPACE::g_options_size; ++o)
259 : {
260 7696384 : if(SNAP_CATCH2_NAMESPACE::g_options[o].f_name == opt_name)
261 : {
262 614400 : ignore_message = (opt & (1 << o)) != 0;
263 : //std::cerr << "+++ pos option [" << opt_name << "] " << ignore_message << "\n";
264 614400 : goto found_option;
265 : }
266 7081984 : else if(SNAP_CATCH2_NAMESPACE::g_options[o].f_neg_name == opt_name)
267 : {
268 663552 : ignore_message = (opt & (1 << o)) == 0;
269 : //std::cerr << "+++ neg option [" << opt_name << "] " << ignore_message << "\n";
270 663552 : goto found_option;
271 : }
272 : }
273 0 : std::cerr << "error: Option \"" << opt_name << "\" not found in our list of valid options\n";
274 0 : CATCH_REQUIRE("option name from JSON not found in SNAP_CATCH2_NAMESPACE::g_options" == nullptr);
275 :
276 1277952 : found_option:
277 1277952 : if(*s == '\0')
278 : {
279 909312 : break;
280 : }
281 368640 : if(*s == '|')
282 : {
283 294912 : if(ignore_message)
284 : {
285 147456 : break;
286 : }
287 : }
288 : else
289 : {
290 73728 : if(!ignore_message)
291 : {
292 36864 : break;
293 : }
294 : }
295 :
296 : // skip commas and pipes
297 : do
298 : {
299 184320 : ++s;
300 : }
301 184320 : while(*s == ',' || *s == '|');
302 184320 : start = s;
303 1277952 : }
304 26130432 : }
305 1093632 : }
306 :
307 2060288 : if(!ignore_message)
308 : {
309 1458176 : SNAP_CATCH2_NAMESPACE::test_callback::expected_t expected;
310 1458176 : expected.f_message_level = static_cast<as2js::message_level_t>(message.find("message level")->second->get_integer().get());
311 1458176 : expected.f_error_code = SNAP_CATCH2_NAMESPACE::str_to_error_code(message.find("error code")->second->get_string());
312 1458176 : expected.f_pos.set_filename("unknown-file");
313 1458176 : as2js::json::json_value::object_t::const_iterator func_it(message.find("function name"));
314 1458176 : if(func_it == message.end())
315 : {
316 1458176 : expected.f_pos.set_function("unknown-func");
317 : }
318 : else
319 : {
320 0 : expected.f_pos.set_function(func_it->second->get_string());
321 : }
322 1458176 : as2js::json::json_value::object_t::const_iterator line_it(message.find("line #"));
323 1458176 : if(line_it != message.end())
324 : {
325 1458176 : std::int64_t lines(line_it->second->get_integer().get());
326 11160576 : for(std::int64_t l(1); l < lines; ++l)
327 : {
328 9702400 : expected.f_pos.new_line();
329 : }
330 : }
331 1458176 : expected.f_message = message.find("message")->second->get_string();
332 : //std::cerr << " --- message [" << expected.f_message << "]\n";
333 1458176 : tc.f_expected.push_back(expected);
334 1458176 : }
335 2060288 : }
336 :
337 : // the default message level is INFO, don't change if we have
338 : // a higher level here; however, if we have a lower level,
339 : // change the message level in the as2js library
340 : //
341 602112 : if(message_level < as2js::message_level_t::MESSAGE_LEVEL_INFO)
342 : {
343 8192 : as2js::set_message_level(message_level);
344 : }
345 : }
346 :
347 1368064 : as2js::node::pointer_t root(parser->parse());
348 :
349 : // the result is object which can have children
350 : // which are represented by an array of objects
351 : //
352 684032 : SNAP_CATCH2_NAMESPACE::verify_parser_result(result_string, prog.find(result_string)->second, root, verbose, false);
353 :
354 684032 : tc.got_called();
355 :
356 684032 : set_message_level(static_cast<as2js::message_level_t>(as2js::message_level_t::MESSAGE_LEVEL_INFO));
357 684032 : }
358 :
359 167 : std::cout << " OK\n";
360 167 : }
361 :
362 18 : std::cout << "\n";
363 36 : }
364 :
365 :
366 : }
367 : // no name namespace
368 :
369 :
370 :
371 :
372 :
373 1 : CATCH_TEST_CASE("parser_array", "[parser][data]")
374 : {
375 1 : CATCH_START_SECTION("parser_array: verify JavaScript arrays")
376 : {
377 1 : run_tests(g_array, "test_parser_array.json");
378 : }
379 1 : CATCH_END_SECTION()
380 1 : }
381 :
382 :
383 1 : CATCH_TEST_CASE("parser_basics", "[parser]")
384 : {
385 1 : CATCH_START_SECTION("parser_basics: verify JavaScript basic elements")
386 : {
387 1 : run_tests(g_basics, "test_parser_basics.json");
388 : }
389 1 : CATCH_END_SECTION()
390 1 : }
391 :
392 :
393 1 : CATCH_TEST_CASE("parser_class", "[parser][instruction]")
394 : {
395 1 : CATCH_START_SECTION("parser_class: verify class extension")
396 : {
397 1 : run_tests(g_class, "test_parser_class.json");
398 : }
399 1 : CATCH_END_SECTION()
400 1 : }
401 :
402 :
403 1 : CATCH_TEST_CASE("parser_enum", "[parser][instruction]")
404 : {
405 1 : CATCH_START_SECTION("parser_enum: verify enum extension")
406 : {
407 1 : run_tests(g_enum, "test_parser_enum.json");
408 : }
409 1 : CATCH_END_SECTION()
410 1 : }
411 :
412 :
413 1 : CATCH_TEST_CASE("parser_expression", "[parser][expression]")
414 : {
415 1 : CATCH_START_SECTION("parser_expression: verify special expressions")
416 : {
417 1 : run_tests(g_expression, "test_parser_expression.json");
418 : }
419 1 : CATCH_END_SECTION()
420 1 : }
421 :
422 :
423 1 : CATCH_TEST_CASE("parser_for", "[parser][instruction]")
424 : {
425 1 : CATCH_START_SECTION("parser_for: verify JavaScript for loops")
426 : {
427 1 : run_tests(g_for, "test_parser_for.json");
428 : }
429 1 : CATCH_END_SECTION()
430 1 : }
431 :
432 :
433 1 : CATCH_TEST_CASE("parser_function", "[parser][function]")
434 : {
435 1 : CATCH_START_SECTION("parser_function: verify JavaScript functions")
436 : {
437 1 : run_tests(g_function, "test_parser_function.json");
438 : }
439 1 : CATCH_END_SECTION()
440 1 : }
441 :
442 :
443 1 : CATCH_TEST_CASE("parser_if", "[parser][instruction]")
444 : {
445 1 : CATCH_START_SECTION("parser_if: verify JavaScript if()/else")
446 : {
447 1 : run_tests(g_if, "test_parser_if.json");
448 : }
449 1 : CATCH_END_SECTION()
450 1 : }
451 :
452 :
453 1 : CATCH_TEST_CASE("parser_pragma", "[parser][instruction]")
454 : {
455 1 : CATCH_START_SECTION("parser_pragma: verify pragma extension")
456 : {
457 1 : run_tests(g_pragma, "test_parser_pragma.json");
458 : }
459 1 : CATCH_END_SECTION()
460 1 : }
461 :
462 :
463 1 : CATCH_TEST_CASE("parser_regex", "[parser][expression]")
464 : {
465 1 : CATCH_START_SECTION("parser_regex: verify regular expressions")
466 : {
467 1 : run_tests(g_regex, "test_parser_regex.json");
468 : }
469 1 : CATCH_END_SECTION()
470 1 : }
471 :
472 :
473 1 : CATCH_TEST_CASE("parser_synchronized", "[parser][synchronized]")
474 : {
475 1 : CATCH_START_SECTION("parser_synchronized: verify synchronized extension")
476 : {
477 1 : run_tests(g_synchronized, "test_parser_synchronized.json");
478 : }
479 1 : CATCH_END_SECTION()
480 1 : }
481 :
482 :
483 1 : CATCH_TEST_CASE("parser_switch", "[parser][instruction]")
484 : {
485 1 : CATCH_START_SECTION("parser_switch: verify JavaScript switch")
486 : {
487 1 : run_tests(g_switch, "test_parser_switch.json");
488 : }
489 1 : CATCH_END_SECTION()
490 1 : }
491 :
492 :
493 1 : CATCH_TEST_CASE("parser_try_catch", "[parser][instruction]")
494 : {
495 1 : CATCH_START_SECTION("parser_try_catch: verify JavaScript exception handling")
496 : {
497 1 : run_tests(g_trycatch, "test_parser_trycatch.json");
498 : }
499 1 : CATCH_END_SECTION()
500 1 : }
501 :
502 :
503 1 : CATCH_TEST_CASE("parser_type", "[parser][type]")
504 : {
505 1 : CATCH_START_SECTION("parser_type: verify type extensions")
506 : {
507 1 : run_tests(g_type, "test_parser_type.json");
508 : }
509 1 : CATCH_END_SECTION()
510 1 : }
511 :
512 :
513 1 : CATCH_TEST_CASE("parser_variable", "[parser][variable]")
514 : {
515 1 : CATCH_START_SECTION("parser_variable: verify JavaScript variable")
516 : {
517 1 : run_tests(g_variable, "test_parser_variable.json");
518 : }
519 1 : CATCH_END_SECTION()
520 1 : }
521 :
522 :
523 1 : CATCH_TEST_CASE("parser_while", "[parser][instruction]")
524 : {
525 1 : CATCH_START_SECTION("parser_while: verify JavaScript while")
526 : {
527 1 : run_tests(g_while, "test_parser_while.json");
528 : }
529 1 : CATCH_END_SECTION()
530 1 : }
531 :
532 :
533 1 : CATCH_TEST_CASE("parser_yield", "[parser][instruction]")
534 : {
535 1 : CATCH_START_SECTION("parser_yield: verify JavaScript yield")
536 : {
537 1 : run_tests(g_yield, "test_parser_yield.json");
538 : }
539 1 : CATCH_END_SECTION()
540 1 : }
541 :
542 :
543 : // TODO: remove once everything is "properly" typed/moved to separate files
544 1 : CATCH_TEST_CASE("parser_parser", "[parser][mixed]")
545 : {
546 1 : CATCH_START_SECTION("parser_parser: verify other parser functionality (still mixed)")
547 : {
548 1 : run_tests(g_data, "test_parser.json");
549 : }
550 1 : CATCH_END_SECTION()
551 1 : }
552 :
553 :
554 :
555 :
556 :
557 : // vim: ts=4 sw=4 et
|