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 : // self
20 : //
21 : #include "catch_main.h"
22 :
23 : #include "tests/compiler_data/class_all_operators_overload.h"
24 :
25 :
26 : // as2js
27 : //
28 : #include <as2js/compiler.h>
29 : #include <as2js/exception.h>
30 : #include <as2js/json.h>
31 : #include <as2js/message.h>
32 : #include <as2js/parser.h>
33 :
34 :
35 : // snapdev
36 : //
37 : #include <snapdev/string_replace_many.h>
38 :
39 :
40 : // C++
41 : //
42 : #include <algorithm>
43 : #include <climits>
44 : #include <cstring>
45 : #include <iomanip>
46 :
47 :
48 : // C
49 : //
50 : #include <unistd.h>
51 : #include <sys/stat.h>
52 :
53 :
54 : // last include
55 : //
56 : #include <snapdev/poison.h>
57 :
58 :
59 :
60 : namespace
61 : {
62 :
63 :
64 :
65 : struct result_t {
66 : as2js::node_t f_type = as2js::node_t::NODE_UNKNOWN;
67 : char const * const f_call_instance = nullptr;
68 : char const * const f_call_type = nullptr;
69 : };
70 :
71 :
72 : #pragma GCC diagnostic push
73 : #pragma GCC diagnostic ignored "-Wpedantic"
74 : constexpr result_t const g_expected_results[] =
75 : {
76 : { // ++a
77 : .f_type = as2js::node_t::NODE_CALL,
78 : .f_call_instance = "++x",
79 : .f_call_type = "OperatorClass",
80 : },
81 : { // --a
82 : .f_type = as2js::node_t::NODE_CALL,
83 : .f_call_instance = "--x",
84 : .f_call_type = "OperatorClass",
85 : },
86 : { // a := -b
87 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
88 : .f_call_instance = "-",
89 : .f_call_type = "OperatorClass",
90 : },
91 : { // a := +b
92 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
93 : .f_call_instance = "+",
94 : .f_call_type = "OperatorClass",
95 : },
96 : { // a := !b
97 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
98 : .f_call_instance = "!",
99 : .f_call_type = "Boolean",
100 : },
101 : { // a := ~b
102 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
103 : .f_call_instance = "~",
104 : .f_call_type = "OperatorClass",
105 : },
106 : { // a++
107 : .f_type = as2js::node_t::NODE_CALL,
108 : .f_call_instance = "x++",
109 : .f_call_type = "OperatorClass",
110 : },
111 : { // a++
112 : .f_type = as2js::node_t::NODE_CALL,
113 : .f_call_instance = "x--",
114 : .f_call_type = "OperatorClass",
115 : },
116 : { // a := b()
117 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
118 : .f_call_instance = "()",
119 : .f_call_type = "OperatorClass",
120 : },
121 : { // a := b(c)
122 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
123 : .f_call_instance = "()",
124 : .f_call_type = "OperatorClass",
125 : },
126 : { // a := b(-33.57)
127 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
128 : .f_call_instance = "()",
129 : .f_call_type = "OperatorClass",
130 : },
131 : { // a := b("param1")
132 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
133 : .f_call_instance = "()",
134 : .f_call_type = "OperatorClass",
135 : },
136 : { // a := b(15, "param2", c)
137 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
138 : .f_call_instance = "()",
139 : .f_call_type = "Boolean",
140 : },
141 : { // a := b[1]
142 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
143 : .f_call_instance = "[]",
144 : .f_call_type = "OperatorClass",
145 : },
146 : { // a := b["index"]
147 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
148 : .f_call_instance = "[]",
149 : .f_call_type = "OperatorClass",
150 : },
151 : { // a := b ** c
152 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
153 : .f_call_instance = "**",
154 : .f_call_type = "OperatorClass",
155 : },
156 : { // a := b ~= /magic/
157 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
158 : .f_call_instance = "~=",
159 : .f_call_type = "Boolean",
160 : },
161 : { // a := b ~! /magic/
162 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
163 : .f_call_instance = "~!",
164 : .f_call_type = "Boolean",
165 : },
166 : { // a := b * c
167 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
168 : .f_call_instance = "*",
169 : .f_call_type = "OperatorClass",
170 : },
171 : { // a := b / c
172 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
173 : .f_call_instance = "/",
174 : .f_call_type = "OperatorClass",
175 : },
176 : { // a := b % c
177 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
178 : .f_call_instance = "%",
179 : .f_call_type = "OperatorClass",
180 : },
181 : { // a := b + c
182 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
183 : .f_call_instance = "+",
184 : .f_call_type = "OperatorClass",
185 : },
186 : { // a := b - c
187 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
188 : .f_call_instance = "-",
189 : .f_call_type = "OperatorClass",
190 : },
191 : { // a := b << 3
192 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
193 : .f_call_instance = "<<",
194 : .f_call_type = "OperatorClass",
195 : },
196 : { // a := b >> 3
197 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
198 : .f_call_instance = ">>",
199 : .f_call_type = "OperatorClass",
200 : },
201 : { // a := b >>> 3
202 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
203 : .f_call_instance = ">>>",
204 : .f_call_type = "OperatorClass",
205 : },
206 : { // a := b <% 3
207 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
208 : .f_call_instance = "<%",
209 : .f_call_type = "OperatorClass",
210 : },
211 : { // a := b >% 3
212 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
213 : .f_call_instance = ">%",
214 : .f_call_type = "OperatorClass",
215 : },
216 : { // a := b < c
217 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
218 : .f_call_instance = "<",
219 : .f_call_type = "Boolean",
220 : },
221 : { // a := b <= c
222 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
223 : .f_call_instance = "<=",
224 : .f_call_type = "Boolean",
225 : },
226 : { // a := b > c
227 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
228 : .f_call_instance = ">",
229 : .f_call_type = "Boolean",
230 : },
231 : { // a := b >= c
232 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
233 : .f_call_instance = ">=",
234 : .f_call_type = "Boolean",
235 : },
236 : { // a := b == c
237 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
238 : .f_call_instance = "==",
239 : .f_call_type = "Boolean",
240 : },
241 : { // a := b === c
242 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
243 : .f_call_instance = "===",
244 : .f_call_type = "Boolean",
245 : },
246 : { // a := b ≈ c
247 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
248 : .f_call_instance = "≈",
249 : .f_call_type = "Boolean",
250 : },
251 : { // a := b != c
252 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
253 : .f_call_instance = "!=",
254 : .f_call_type = "Boolean",
255 : },
256 : { // a := b !== c
257 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
258 : .f_call_instance = "!==",
259 : .f_call_type = "Boolean",
260 : },
261 : { // a := b <=> c
262 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
263 : .f_call_instance = "<=>",
264 : .f_call_type = "CompareResult",
265 : },
266 : { // a := b ~~ c
267 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
268 : .f_call_instance = "~~",
269 : .f_call_type = "Boolean",
270 : },
271 : { // a := b & c
272 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
273 : .f_call_instance = "&",
274 : .f_call_type = "OperatorClass",
275 : },
276 : { // a := b ^ c
277 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
278 : .f_call_instance = "^",
279 : .f_call_type = "OperatorClass",
280 : },
281 : { // a := b | c
282 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
283 : .f_call_instance = "|",
284 : .f_call_type = "OperatorClass",
285 : },
286 : { // a := b && c
287 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
288 : .f_call_instance = "&&",
289 : .f_call_type = "OperatorClass",
290 : },
291 : { // a := b ^^ c
292 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
293 : .f_call_instance = "^^",
294 : .f_call_type = "OperatorClass",
295 : },
296 : { // a := b || c
297 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
298 : .f_call_instance = "||",
299 : .f_call_type = "OperatorClass",
300 : },
301 : { // a := b <? c
302 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
303 : .f_call_instance = "<?",
304 : .f_call_type = "OperatorClass",
305 : },
306 : { // a := b >? c
307 : .f_type = as2js::node_t::NODE_ASSIGNMENT,
308 : .f_call_instance = ">?",
309 : .f_call_type = "OperatorClass",
310 : },
311 : { // a += b
312 : .f_type = as2js::node_t::NODE_CALL,
313 : .f_call_instance = "+=",
314 : .f_call_type = "OperatorClass",
315 : },
316 : { // a &= b
317 : .f_type = as2js::node_t::NODE_CALL,
318 : .f_call_instance = "&=",
319 : .f_call_type = "OperatorClass",
320 : },
321 : { // a |= b
322 : .f_type = as2js::node_t::NODE_CALL,
323 : .f_call_instance = "|=",
324 : .f_call_type = "OperatorClass",
325 : },
326 : { // a ^= b
327 : .f_type = as2js::node_t::NODE_CALL,
328 : .f_call_instance = "^=",
329 : .f_call_type = "OperatorClass",
330 : },
331 : { // a /= b
332 : .f_type = as2js::node_t::NODE_CALL,
333 : .f_call_instance = "/=",
334 : .f_call_type = "OperatorClass",
335 : },
336 : { // a &&= b
337 : .f_type = as2js::node_t::NODE_CALL,
338 : .f_call_instance = "&&=",
339 : .f_call_type = "OperatorClass",
340 : },
341 : { // a ||= b
342 : .f_type = as2js::node_t::NODE_CALL,
343 : .f_call_instance = "||=",
344 : .f_call_type = "OperatorClass",
345 : },
346 : { // a ^^= b
347 : .f_type = as2js::node_t::NODE_CALL,
348 : .f_call_instance = "^^=",
349 : .f_call_type = "OperatorClass",
350 : },
351 : { // a >?= b
352 : .f_type = as2js::node_t::NODE_CALL,
353 : .f_call_instance = ">?=",
354 : .f_call_type = "OperatorClass",
355 : },
356 : { // a <?= b
357 : .f_type = as2js::node_t::NODE_CALL,
358 : .f_call_instance = "<?=",
359 : .f_call_type = "OperatorClass",
360 : },
361 : { // a %= b
362 : .f_type = as2js::node_t::NODE_CALL,
363 : .f_call_instance = "%=",
364 : .f_call_type = "OperatorClass",
365 : },
366 : { // a *= b
367 : .f_type = as2js::node_t::NODE_CALL,
368 : .f_call_instance = "*=",
369 : .f_call_type = "OperatorClass",
370 : },
371 : { // a **= b
372 : .f_type = as2js::node_t::NODE_CALL,
373 : .f_call_instance = "**=",
374 : .f_call_type = "OperatorClass",
375 : },
376 : { // a <%= 3
377 : .f_type = as2js::node_t::NODE_CALL,
378 : .f_call_instance = "<%=",
379 : .f_call_type = "OperatorClass",
380 : },
381 : { // a >%= 3
382 : .f_type = as2js::node_t::NODE_CALL,
383 : .f_call_instance = ">%=",
384 : .f_call_type = "OperatorClass",
385 : },
386 : { // a <<= 3
387 : .f_type = as2js::node_t::NODE_CALL,
388 : .f_call_instance = "<<=",
389 : .f_call_type = "OperatorClass",
390 : },
391 : { // a >>= 3
392 : .f_type = as2js::node_t::NODE_CALL,
393 : .f_call_instance = ">>=",
394 : .f_call_type = "OperatorClass",
395 : },
396 : { // a >>>= 3
397 : .f_type = as2js::node_t::NODE_CALL,
398 : .f_call_instance = ">>>=",
399 : .f_call_type = "OperatorClass",
400 : },
401 : { // a -= b
402 : .f_type = as2js::node_t::NODE_CALL,
403 : .f_call_instance = "-=",
404 : .f_call_type = "OperatorClass",
405 : },
406 : { // a := b, c
407 : .f_type = as2js::node_t::NODE_CALL,
408 : .f_call_instance = ",",
409 : .f_call_type = "OperatorClass",
410 : },
411 : };
412 : #pragma GCC diagnostic pop
413 :
414 : constexpr std::size_t const g_expected_results_size = std::size(g_expected_results);
415 :
416 :
417 :
418 :
419 : bool g_created_files = false;
420 :
421 :
422 :
423 :
424 : //class input_retriever
425 : // : public as2js::input_retriever
426 : //{
427 : //public:
428 : // virtual as2js::base_stream::pointer_t retrieve(std::string const & filename) override
429 : // {
430 : // if(filename == "")
431 : // {
432 : // }
433 : //
434 : // return as2js::base_stream::pointer_t();
435 : // }
436 : //
437 : //};
438 :
439 :
440 : std::string g_current_working_directory;
441 :
442 :
443 : //void init_compiler(as2js::compiler & compiler)
444 : //{
445 : // // The .rc file cannot be captured by the input retriever
446 : // // so instead we create a file in the current directory
447 : //
448 : // // setup an input retriever which in most cases just returns nullptr
449 : // //
450 : // compiler.set_input_retriever(std::make_shared<input_retriever>());
451 : //}
452 :
453 :
454 1 : void init_rc()
455 : {
456 1 : g_created_files = true;
457 :
458 : // we recreate because in the clean up we may end up deleting that
459 : // folder (even though it's already created by the catch_db_init()
460 : // function which happens before this call)
461 : //
462 1 : if(mkdir("as2js", 0700) != 0)
463 : {
464 1 : if(errno != EEXIST)
465 : {
466 0 : CATCH_REQUIRE(!"could not create directory as2js");
467 : }
468 : // else -- we already created it, that's fine
469 : }
470 :
471 : // The .rc file cannot be captured by the input retriever
472 : // so instead we create a file in the current directory
473 : //
474 3 : std::string const safe_cwd(snapdev::string_replace_many(
475 : g_current_working_directory
476 4 : , { { "'", "\\'" } }));
477 1 : std::ofstream out("as2js/as2js.rc");
478 : out << "// rc test file\n"
479 : "{\n"
480 1 : " 'scripts': '" << SNAP_CATCH2_NAMESPACE::g_source_dir() << "/scripts',\n"
481 : " 'db': '" << safe_cwd << "/test.db',\n"
482 : " 'temporary_variable_name': '@temp$'\n"
483 1 : "}\n";
484 :
485 1 : CATCH_REQUIRE(!!out);
486 2 : }
487 :
488 :
489 :
490 :
491 : }
492 : // no name namespace
493 :
494 :
495 : namespace SNAP_CATCH2_NAMESPACE
496 : {
497 :
498 :
499 :
500 : } // namespace SNAP_CATCH2_NAMESPACE
501 :
502 :
503 :
504 :
505 :
506 1 : CATCH_TEST_CASE("compiler_all_operators", "[compiler][valid]")
507 : {
508 1 : CATCH_START_SECTION("compiler_all_operators: user class with all possible operators")
509 : {
510 : // get source code
511 : //
512 2 : std::string const program_source(as2js_tests::class_all_operators_overload, as2js_tests::class_all_operators_overload_size);
513 :
514 : // prepare input stream
515 : //
516 1 : as2js::input_stream<std::stringstream>::pointer_t prog_text(std::make_shared<as2js::input_stream<std::stringstream>>());
517 1 : prog_text->get_position().set_filename("tests/compiler_data/class_all_operators_overload.ajs");
518 1 : *prog_text << program_source;
519 :
520 : // parse the input
521 : //
522 1 : as2js::options::pointer_t options(std::make_shared<as2js::options>());
523 1 : as2js::parser::pointer_t parser(std::make_shared<as2js::parser>(prog_text, options));
524 1 : init_rc();
525 1 : as2js::node::pointer_t root(parser->parse());
526 :
527 : // run the compiler
528 : //
529 2 : as2js::compiler compiler(options);
530 1 : CATCH_REQUIRE(compiler.compile(root) == 0);
531 :
532 : // find nodes of interest and verify they are or not marked with the
533 : // "native" flag as expected
534 : //
535 1 : as2js::node::pointer_t operator_class(root->find_descendent(
536 : as2js::node_t::NODE_CLASS
537 1 : , [](as2js::node::pointer_t n)
538 : {
539 1 : return n->get_string() == "OperatorClass";
540 2 : }));
541 1 : CATCH_REQUIRE(operator_class != nullptr);
542 :
543 1 : as2js::node::pointer_t call;
544 1 : as2js::node::pointer_t assignment;
545 68 : for(std::size_t i(0); i < g_expected_results_size; ++i)
546 : {
547 67 : if(i == 0)
548 : {
549 2 : call = root->find_descendent(
550 1 : g_expected_results[i].f_type
551 1 : , [&operator_class](as2js::node::pointer_t n)
552 : {
553 1 : return n->get_type_node() == operator_class;
554 1 : });
555 : }
556 66 : else if(assignment != nullptr)
557 : {
558 43 : call = assignment->get_parent()->find_next_child(assignment, g_expected_results[i].f_type);
559 : }
560 : else
561 : {
562 23 : call = call->get_parent()->find_next_child(call, g_expected_results[i].f_type);
563 : }
564 67 : CATCH_REQUIRE(call != nullptr);
565 : //std::cerr << i + 1 << ". checking " << g_expected_results[i].f_call_instance << " with call at " << call.get() << "\n";
566 :
567 67 : if(g_expected_results[i].f_type != as2js::node_t::NODE_CALL)
568 : {
569 43 : assignment = call;
570 86 : call = assignment->find_descendent(
571 : as2js::node_t::NODE_CALL
572 43 : , [](as2js::node::pointer_t)
573 : {
574 : // some return a Boolean, not the OperatorClass
575 : //return n->get_type_node() == operator_class;
576 43 : return true;
577 43 : });
578 43 : CATCH_REQUIRE(call != nullptr);
579 :
580 43 : CATCH_REQUIRE(assignment->get_type_node() == operator_class);
581 : }
582 : else
583 : {
584 24 : assignment.reset();
585 : }
586 :
587 67 : CATCH_REQUIRE_FALSE(call->get_attribute(as2js::attribute_t::NODE_ATTR_NATIVE));
588 :
589 67 : CATCH_REQUIRE(call->get_instance() != nullptr);
590 67 : CATCH_REQUIRE(call->get_instance()->get_string() == g_expected_results[i].f_call_instance);
591 :
592 : // the return type is generally operator_class, but a few functions
593 : // return something else such as Boolean
594 : //
595 67 : as2js::node::pointer_t check_type(call->get_type_node());
596 67 : CATCH_REQUIRE(check_type != nullptr);
597 67 : CATCH_REQUIRE(check_type->get_string() == g_expected_results[i].f_call_type);
598 67 : }
599 :
600 : // if someone was to make the expected results array empty, this
601 : // would be triggered, otherwise it cannot happen
602 : //
603 1 : CATCH_REQUIRE(call != nullptr);
604 :
605 : // no more calls or we have a problem in our test or the library
606 : //
607 1 : call = call->get_parent()->find_next_child(call, as2js::node_t::NODE_CALL);
608 1 : if(g_expected_results[g_expected_results_size - 1].f_type == as2js::node_t::NODE_CALL)
609 : {
610 1 : CATCH_REQUIRE(call != nullptr);
611 1 : as2js::node::pointer_t id(call->find_descendent(
612 : as2js::node_t::NODE_IDENTIFIER
613 1 : , [](as2js::node::pointer_t n)
614 : {
615 1 : return n->get_string() == "console";
616 2 : }));
617 1 : CATCH_REQUIRE(id != nullptr);
618 1 : }
619 : else
620 : {
621 0 : CATCH_REQUIRE(call == nullptr);
622 : }
623 1 : }
624 1 : CATCH_END_SECTION()
625 1 : }
626 :
627 :
628 :
629 : // vim: ts=4 sw=4 et
|