regen
[bison.git] / tests / c++.at
bloba9d393c2c1c60dd1ae4f02d2bef7ab588fe9e056
1 # Checking the C++ Features.                    -*- Autotest -*-
3 # Copyright (C) 2004-2005, 2007-2015, 2018-2021 Free Software
4 # Foundation, Inc.
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.
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.
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/>.
19 AT_BANNER([[C++ Features.]])
22 ## -------------------------- ##
23 ## C++ Locations Unit Tests.  ##
24 ## -------------------------- ##
26 AT_SETUP([C++ Locations Unit Tests])
28 AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "lalr1.cc"])
29 AT_DATA_GRAMMAR([[input.y]],
30 [[%code {#include <sstream>}
31 %locations
32 %debug
33 %skeleton "lalr1.cc"
34 %code
36 ]AT_YYERROR_DECLARE[
37 ]AT_YYLEX_DECLARE[
40 exp: %empty;
42 ]AT_YYERROR_DEFINE[
43 ]AT_YYLEX_DEFINE[
45 template <typename T>
46 bool
47 check (const T& in, const std::string& s)
49   const static bool verbose = getenv ("DEBUG");
50   std::stringstream os;
51   os << in;
52   if (os.str () == s)
53     {
54       if (verbose)
55         std::cerr << os.str () << '\n';
56       return true;
57     }
58   else
59     {
60       std::cerr << "fail: " << os.str () << ", expected: " << s << '\n';
61       return false;
62     }
65 int
66 main (void)
68   const std::string fn = "foo.txt";
69   int fail = 0;
70   ]AT_YYLTYPE[ loc (&fn);  fail += check (loc, "foo.txt:1.1");
71                            fail += check (loc + 10, "foo.txt:1.1-10");
72   loc += 10;               fail += check (loc, "foo.txt:1.1-10");
73   loc += -5;               fail += check (loc, "foo.txt:1.1-5");
74                            fail += check (loc - 5, "foo.txt:1.1");
75   loc -= 5;                fail += check (loc, "foo.txt:1.1");
76   // Check that we don't go below.
77   // https://lists.gnu.org/r/bug-bison/2013-02/msg00000.html
78   loc -= 10;         fail += check (loc, "foo.txt:1.1");
80   loc.columns (10); loc.lines (10); fail += check (loc, "foo.txt:1.1-11.0");
81   loc.lines (-2);                   fail += check (loc, "foo.txt:1.1-9.0");
82   loc.lines (-10);                  fail += check (loc, "foo.txt:1.1");
84   ]AT_YYLTYPE[ loc2 (YY_NULLPTR, 5, 10);
85                    fail += check (loc2, "5.10");
86                    fail += check (loc + loc2, "foo.txt:1.1-5.9");
87   loc += loc2;     fail += check (loc, "foo.txt:1.1-5.9");
88   return !fail;
90 ]])
92 AT_FOR_EACH_CXX([
93   AT_FULL_COMPILE([input])
94   AT_PARSER_CHECK([input], 0)
96 AT_BISON_OPTION_POPDEFS
97 AT_CLEANUP
100 ## -------------------------------------- ##
101 ## C++ Variant-based Symbols Unit Tests.  ##
102 ## -------------------------------------- ##
104 # Not checking the grammar, only the variants and variant based
105 # symbols.
107 AT_SETUP([C++ Variant-based Symbols Unit Tests])
109 AT_KEYWORDS([variant])
111 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %debug $1])
112 # Store strings and integers in a vector of strings.
113 AT_DATA_GRAMMAR([list.yy],
114 [[%skeleton "lalr1.cc"
115 %define api.value.type variant
116 %define parse.assert
117 %debug
119 %code provides
121   ]AT_YYLEX_DECLARE[
124 %token <int> INT "int"
125 %type <std::vector<int>> exp
127 %printer { yyo << $$; } <int>
128 %printer
129   {
130     for (std::vector<int>::const_iterator i = $$.begin (); i != $$.end (); ++i)
131       {
132         if (i != $$.begin ())
133           yyo << ", ";
134         yyo << *i;
135       }
136   } <std::vector<int>>
138 %code requires { #include <vector> }
139 %code { int yylex (yy::parser::semantic_type* lvalp); }
141 // A hack which relies on internal hooks to check stack_symbol_type,
142 // which is private.
143 %code yy_bison_internal_hook {
144   public:
145     typedef stack_symbol_type yy_stack_symbol_type;
146     typedef stack_type yy_stack_type;
150 exp: "int" { $$.push_back ($1); }
152 ]AT_YYERROR_DEFINE[
153 ]AT_YYLEX_DEFINE[
155 template <typename Exp, typename Eff>
156 void assert_eq (const Exp& exp, const Eff& eff)
158   if (getenv ("DEBUG"))
159     std::cerr << "Assert: " << exp << " == " << eff << '\n';
160   if (exp != eff)
161     std::cerr << "Assertion failed: " << exp << " != " << eff << '\n';
164 int main()
166   using yy::parser;
167   // symbol_type: construction, accessor.
168   {
169     parser::symbol_type s = parser::make_INT (12);
170     assert_eq (s.kind (), parser::symbol_kind::S_INT);
171     assert_eq (parser::symbol_name (s.kind ()), std::string ("\"int\""));
172     assert_eq (s.name (), std::string ("\"int\""));
173     assert_eq (s.value.as<int> (), 12);
174   }
176   // symbol_type: move constructor.
177 #if 201103L <= YY_CPLUSPLUS
178   {
179     auto s = parser::make_INT (42);
180     auto s2 = std::move (s);
181     assert_eq (s2.value.as<int> (), 42);
182     // Used to crash here, because s was improperly cleared, and
183     // its destructor tried to delete its (moved) value.
184   }
185 #endif
187   // symbol_type: copy constructor.
188   {
189     parser::symbol_type s = parser::make_INT (51);
190     parser::symbol_type s2 = s;
191     assert_eq (s.value.as<int> (), 51);
192     assert_eq (s2.value.as<int> (), 51);
193   }
195   // stack_symbol_type: construction, accessor.
196   typedef parser::yy_stack_symbol_type stack_symbol_type;
197   {
198 #if 201103L <= YY_CPLUSPLUS
199     auto ss = stack_symbol_type (1, parser::make_INT(123));
200 #else
201     parser::symbol_type s = parser::make_INT (123);
202     stack_symbol_type ss(1, s);
203 #endif
204     assert_eq (ss.value.as<int> (), 123);
205   }
207   // Pushing on the stack.
208   // Sufficiently many so that it will be resized.
209   // Probably 3 times (starting at 200).
210   {
211     parser::yy_stack_type st;
212     const int mucho = 1700;
213     const int int_reduction_state = 1; // Read list.output to find it.
214     for (int i = 0; i < mucho; ++i)
215       {
216 #if 201103L <= YY_CPLUSPLUS
217         st.push(stack_symbol_type{int_reduction_state,
218                                   parser::make_INT (i)});
219 #else
220         parser::symbol_type s = parser::make_INT (i);
221         stack_symbol_type ss (int_reduction_state, s);
222         st.push (ss);
223 #endif
224       }
225     for (int i = mucho - 1; 0 <= i; --i)
226       {
227         assert_eq (st[0].value.as<int>(), i);
228         st.pop ();
229       }
230   }
234 AT_BISON_CHECK([[-o list.cc list.yy]])
235 AT_FOR_EACH_CXX([
236   AT_COMPILE_CXX([list])
237   AT_PARSER_CHECK([list])
240 AT_BISON_OPTION_POPDEFS
241 AT_CLEANUP
243 ## --------------------------------------------------- ##
244 ## Multiple occurrences of $n and api.value.automove.  ##
245 ## --------------------------------------------------- ##
247 AT_SETUP([Multiple occurrences of $n and api.value.automove])
249 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
251 AT_DATA_GRAMMAR([input.yy],
252 [[%skeleton "lalr1.cc"
253 %define api.value.automove
254 %token <int> NUMBER "number"
255 %type <int> exp
257 exp:
258   "number"          { $$ = $1; $$; }
259 | "twice" exp       { $$ = $2 + $2; }
260 | "thrice" exp[val] { $$ = $2 + $val + $2; }
263 AT_BISON_CHECK([[-fcaret input.yy]], [0], [],
264 [[input.yy:16.33-34: warning: multiple occurrences of $2 with api.value.automove [-Wother]
265    16 | | "twice" exp       { $$ = $2 + $2; }
266       |                                 ^~
267 input.yy:17.33-36: warning: multiple occurrences of $2 with api.value.automove [-Wother]
268    17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
269       |                                 ^~~~
270 input.yy:17.40-41: warning: multiple occurrences of $2 with api.value.automove [-Wother]
271    17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
272       |                                        ^~
275 AT_BISON_OPTION_POPDEFS
276 AT_CLEANUP
279 ## ---------- ##
280 ## Variants.  ##
281 ## ---------- ##
283 # Check that the variants are properly supported, including in error
284 # recovery.
286 # AT_TEST([DIRECTIVES])
287 # ---------------------
288 # Check the support of variants in C++, with the additional DIRECTIVES.
289 m4_pushdef([AT_TEST],
290 [AT_SETUP([Variants $1])
292 AT_KEYWORDS([variant])
294 AT_BISON_OPTION_PUSHDEFS([%debug $1])
295 # Store strings and integers in a vector of strings.
296 AT_DATA_GRAMMAR([list.y],
297 [[%debug
298 %define api.value.type variant
299 ]m4_bpatsubst([$1], [\\n], [
302 %code top // code for the .cc file.
304 #include <cstdlib> // abort, getenv
305 #include <iostream>
306 #include <vector>
307 #include <sstream>
308 #include <string>
310   class string
311   {
312     public:
313       string () {}
315       string (const std::string& s)
316         : val_(s)
317       {}
319       string (const string& s)
320         : val_(s.val_)
321       {}
323       string& operator= (const string& s)
324       {
325         val_ = s.val_;
326         return *this;
327       }
329 #if defined __cplusplus && 201103L <= __cplusplus
330       string (string&& s) noexcept
331         : val_(std::move(s.val_))
332       {
333         s.val_.clear();
334       }
336       string& operator= (string&& s)
337       {
338         val_ = std::move(s.val_);
339         s.val_.clear ();
340         return *this;
341       }
342 #endif
344       friend
345       std::ostream& operator<< (std::ostream& o, const string& s)
346       {
347         return o << s.val_;
348       }
350     private:
351       std::string val_;
352   };
354   typedef std::vector<string> strings_type;
356   namespace yy
357   {
358     // Must be available early, as is used in %destructor.
359     std::ostream&
360     operator<<(std::ostream& o, const strings_type& s)
361     {
362       o << '(';
363       for (strings_type::const_iterator i = s.begin (); i != s.end (); ++i)
364         {
365           if (i != s.begin ())
366             o << ", ";
367           o << *i;
368         }
369       return o << ')';
370     }
371   }
374 %code // code for the .cc file.
376   namespace yy
377   {
378     static
379     ]AT_YYLEX_PROTOTYPE[;
381     // Conversion to string.
382     template <typename T>
383       inline
384       string
385       to_string (const T& t)
386     {
387       std::ostringstream o;
388       o << t;
389       return string (o.str ());
390     }
391   }
394 %token <::string> TEXT;
395 %token <int> NUMBER;
396 %token END_OF_FILE 0;
397 %token COMMA ","
399 // Starting with :: to ensure we don't output "<::" which starts by the
400 // digraph for the left square bracket.
401 %type <::string> item;
402 // Using the template type to exercise its parsing.
403 %type <::std::vector<string>> list;
405 %printer { yyo << $$; } <int> <::string> <::std::vector<string>>;
406 %destructor { std::cerr << "Destroy: " << $$ << '\n'; } <*>;
407 %destructor { std::cerr << "Destroy: \"" << $$ << "\"\n"; } <::string>;
410 result:
411   list          { std::cout << $][1 << '\n'; }
414 list:
415   item          { $$.push_back ($][1); }
416 | list "," item { $$ = $][1; $$.push_back ($][3); }
417 | list error    { $$ = $][1; }
420 item:
421   TEXT          { $$ = $][1; }
422 | NUMBER        { int v = $][1; if (v == 3) YYERROR; else $$ = to_string (v); }
425 ]AT_TOKEN_CTOR_IF([],
426 [[#ifdef TWO_STAGE_BUILD
427 # define BUILD(Type, Value) build<Type> () = Value
428 #else
429 # define BUILD(Type, Value) build (Value)
430 #endif
431 ]])[
432 #define STAGE_MAX 5
433 namespace yy
435   static
436   ]AT_YYLEX_PROTOTYPE[
437   {
438     // The 5 is a syntax error whose recovery requires that we discard
439     // the lookahead.  This tests a regression, see
440     // <https://savannah.gnu.org/support/?108481>.
441     static char const *input = "0,1,2,3,45,6";
442     switch (int stage = *input++)
443     {
444       case 0:]AT_TOKEN_CTOR_IF([[
445         return parser::make_END_OF_FILE (]AT_LOCATION_IF([location ()])[);]],
446 [AT_LOCATION_IF([
447         *llocp = location ();])[
448         return ]AT_TOKEN([END_OF_FILE])[;]])[
450       case ',':]AT_TOKEN_CTOR_IF([[
451         return parser::make_COMMA (]AT_LOCATION_IF([location ()])[);]],
452 [AT_LOCATION_IF([
453         *llocp = location ();])[
454         return ]AT_TOKEN([COMMA])[;]])[
456       default:
457         stage = stage - '0';
458         if (stage % 2)
459          {]AT_TOKEN_CTOR_IF([[
460            return parser::make_NUMBER (stage]AT_LOCATION_IF([, location ()])[);]], [[
461            lvalp->BUILD (int, stage);]AT_LOCATION_IF([
462            *llocp = location ();])[
463            return ]AT_TOKEN(NUMBER)[;]])[
464          }
465        else
466          {]AT_TOKEN_CTOR_IF([[
467            return parser::make_TEXT (to_string (stage)]AT_LOCATION_IF([, location ()])[);]], [[
468            lvalp->BUILD (string, to_string (stage));]AT_LOCATION_IF([
469            *llocp = location ();])[
470            return ]AT_TOKEN([TEXT])[;]])[
471          }
472     }
473   }
476 ]AT_YYERROR_DEFINE[
477 ]AT_MAIN_DEFINE[
480 AT_DATA_SOURCE([[modern.cc]],
481 [[#include <iostream>
482 int main()
484 #if defined __cplusplus && 201103L <= __cplusplus
485   std::cout << "Modern C++: " << __cplusplus << '\n';
486   return 0;
487 #else
488   std::cout << "Legac++\n";
489   return 1;
490 #endif
494 AT_FOR_EACH_CXX([
495 AT_FULL_COMPILE([list])
497 # Are we compiling with modern C++ enabled?
498 AT_COMPILE_CXX([modern])
499 here=. # Pacify cfg.mk's sc_at_parser_check.
500 AT_CHECK([$here/modern], [ignore], [ignore])
501 if test $at_status = 0; then
502   modern=true
503 else
504   modern=false
507 if AT_AUTOMOVE_IF([$modern], [false]); then
508   AT_PARSER_CHECK([list], 0,
509 [[(0, 1, 2, 4, 6)
511 [[Destroy: ""
512 Destroy: ""
513 Destroy: 1
514 Destroy: ""
515 Destroy: ()
516 Destroy: ""
517 Destroy: ""
518 Destroy: ()
519 Destroy: ""
520 Destroy: 3
521 Destroy: ()
522 Destroy: ""
523 Destroy: ""
524 Destroy: ()
525 Destroy: ()
526 Destroy: 5
527 Destroy: ()
528 Destroy: ""
529 Destroy: ""
530 Destroy: ()
531 Destroy: (0, 1, 2, 4, 6)
533 else
534   AT_PARSER_CHECK([list], 0,
535 [[(0, 1, 2, 4, 6)
537 [[Destroy: "0"
538 Destroy: "0"
539 Destroy: 1
540 Destroy: "1"
541 Destroy: (0)
542 Destroy: "2"
543 Destroy: "2"
544 Destroy: (0, 1)
545 Destroy: ""
546 Destroy: 3
547 Destroy: (0, 1, 2)
548 Destroy: "4"
549 Destroy: "4"
550 Destroy: (0, 1, 2)
551 Destroy: (0, 1, 2, 4)
552 Destroy: 5
553 Destroy: (0, 1, 2, 4)
554 Destroy: "6"
555 Destroy: "6"
556 Destroy: (0, 1, 2, 4)
557 Destroy: (0, 1, 2, 4, 6)
562 AT_BISON_OPTION_POPDEFS
563 AT_CLEANUP
566 AT_TEST([[%skeleton "lalr1.cc"]])
567 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert]])
568 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.value.automove]])
569 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %locations]])
570 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]])
571 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor]])
572 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_}]])
573 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations]])
574 AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations %define api.value.automove]])
576 m4_popdef([AT_TEST])
580 ## ------------------------------------ ##
581 ## Variants and Typed Midrule Actions.  ##
582 ## ------------------------------------ ##
584 AT_SETUP([Variants and Typed Midrule Actions])
586 # See https://lists.gnu.org/r/bug-bison/2017-06/msg00000.html.
588 # Check that typed midrule actions behave properly (pre-construction
589 # of $$ before the user action, support of %printer and %destructor,
590 # etc.).
592 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
594 AT_DATA_GRAMMAR([[input.y]],
595 [[%skeleton "lalr1.cc"
596 %header
598 %debug
599 %define parse.assert
600 %define api.value.type variant
601 %define api.token.constructor
602 %define parse.error verbose
604 %code
606   #include <iostream>
607   namespace yy
608   {
609     static yy::parser::symbol_type yylex();
610   }
613 %token <int> NUMBER;
614 %type <int> expr;
615 %token EOI 0;
616 %printer { yyo << $$; } <int>;
617 %destructor { std::cerr << "destroy: " << $$ << '\n'; } <int>
619 expr:
620   NUMBER { $$ = $1 * 10; }
621 | expr <int>{ $$ = 20; } NUMBER
622   {
623     std::cerr << "expr: " << $1 << ' ' << $2 << ' ' << $3 << '\n';
624     $$ = 40;
625   }
629 namespace yy
631   parser::symbol_type yylex()
632   {
633     static int loc = 0;
634     switch (loc++)
635       {
636       case 0:
637         return parser::make_NUMBER (1);
638       case 1:
639         return parser::make_NUMBER (30);
640       default:
641         return parser::make_EOI ();
642       }
643   }
645   void parser::error(const std::string& message)
646   {
647     std::cerr << message << '\n';
648   }
651 int main()
653   yy::parser p;
654   p.set_debug_level (1);
655   return p.parse();
659 AT_FOR_EACH_CXX([
660 AT_FULL_COMPILE([[input]])
661 # This used to print "Discarding 'a'." again at the end.
662 AT_PARSER_CHECK([[input]], [[0]], [[]],
663 [[Starting parse
664 Entering state 0
665 Stack now 0
666 Reading a token
667 Next token is token NUMBER (1)
668 Shifting token NUMBER (1)
669 Entering state 1
670 Stack now 0 1
671 Reducing stack by rule 1 (line 34):
672    $1 = token NUMBER (1)
673 -> $$ = nterm expr (10)
674 destroy: 1
675 Entering state 2
676 Stack now 0 2
677 Reading a token
678 Next token is token NUMBER (30)
679 Reducing stack by rule 2 (line 35):
680 -> $$ = nterm @1 (20)
681 Entering state 4
682 Stack now 0 2 4
683 Next token is token NUMBER (30)
684 Shifting token NUMBER (30)
685 Entering state 5
686 Stack now 0 2 4 5
687 Reducing stack by rule 3 (line 35):
688    $1 = nterm expr (10)
689    $2 = nterm @1 (20)
690    $3 = token NUMBER (30)
691 expr: 10 20 30
692 -> $$ = nterm expr (40)
693 destroy: 30
694 destroy: 20
695 destroy: 10
696 Entering state 2
697 Stack now 0 2
698 Reading a token
699 Next token is token EOI ()
700 Shifting token EOI ()
701 Entering state 3
702 Stack now 0 2 3
703 Stack now 0 2 3
704 Cleanup: popping token EOI ()
705 Cleanup: popping nterm expr (40)
706 destroy: 40
710 AT_BISON_OPTION_POPDEFS
711 AT_CLEANUP
714 ## ----------------------- ##
715 ## Doxygen Documentation.  ##
716 ## ----------------------- ##
718 m4_define([AT_CHECK_DOXYGEN],
719 [m4_case([$1],
720          [Public],  [m4_pushdef([AT_DOXYGEN_PRIVATE], [NO])],
721          [Private], [m4_pushdef([AT_DOXYGEN_PRIVATE], [YES])],
722          [m4_fatal([invalid argument: $1])])
723 AT_SETUP([Doxygen $1 Documentation])
725 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %locations])
726 AT_DATA([input.yy],
727 [[%require "3.2"
728 %skeleton "lalr1.cc"
729 %locations
730 %header
731 %debug
733 exp: %empty;
735 ]AT_YYERROR_DEFINE[
738 AT_BISON_CHECK([-o input.cc input.yy])
740 AT_DATA([Doxyfile],
741 [# The PROJECT_NAME tag is a single word (or a sequence of words
742 # surrounded by quotes) that should identify the project.
743 PROJECT_NAME = "Bison C++ Parser"
745 # The QUIET tag can be used to turn on/off the messages that are
746 # generated by doxygen. Possible values are YES and NO. If left blank
747 # NO is used.
748 QUIET = YES
750 # The WARNINGS tag can be used to turn on/off the warning messages
751 # that are generated by doxygen. Possible values are YES and NO. If
752 # left blank NO is used.
753 WARNINGS     = YES
754 # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate
755 # warnings for undocumented members. If EXTRACT_ALL is set to YES then
756 # this flag will automatically be disabled.
757 WARN_IF_UNDOCUMENTED   = YES
758 # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings
759 # for potential errors in the documentation, such as not documenting
760 # some parameters in a documented function, or documenting parameters
761 # that don't exist or using markup commands wrongly.
762 WARN_IF_DOC_ERROR      = YES
763 # The WARN_FORMAT tag determines the format of the warning messages
764 # that doxygen can produce. The string should contain the $file,
765 # $line, and $text tags, which will be replaced by the file and line
766 # number from which the warning originated and the warning text.
767 WARN_FORMAT            = "$file:$line: $text"
769 # If the EXTRACT_ALL tag is set to YES doxygen will assume all
770 # entities in documentation are documented, even if no documentation
771 # was available.  Private class members and static file members will
772 # be hidden unless the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set
773 # to YES
774 EXTRACT_ALL            = YES
776 # If the EXTRACT_PRIVATE tag is set to YES all private members of a
777 # class will be included in the documentation.
778 EXTRACT_PRIVATE        = AT_DOXYGEN_PRIVATE
780 # If the EXTRACT_STATIC tag is set to YES all static members of a file
781 # will be included in the documentation.
782 EXTRACT_STATIC         = AT_DOXYGEN_PRIVATE
785 AT_REQUIRE([doxygen --version], 0, ignore)
786 AT_CHECK([doxygen], 0, [], [ignore])
788 AT_BISON_OPTION_POPDEFS
789 AT_CLEANUP
791 m4_popdef([AT_DOXYGEN_PRIVATE])
792 ])# AT_CHECK_DOXYGEN
794 AT_CHECK_DOXYGEN([Public])
795 AT_CHECK_DOXYGEN([Private])
798 ## ------------ ##
799 ## Namespaces.  ##
800 ## ------------ ##
802 # AT_TEST(NAMESPACE-DECL, [COMPILE-ERROR])
803 # ----------------------------------------
804 # See if Bison can handle %define namespace "NAMESPACE-DECL".  If COMPILE-ERROR
805 # is specified, then Bison should accept the input, but compilation will fail,
806 # so don't check compilation.
807 m4_pushdef([AT_TEST],
808 [AT_BISON_OPTION_PUSHDEFS([%language "C++" %define api.namespace {$1}])
809 AT_DATA_GRAMMAR([[input.yy]],
810 [[%language "C++"
811 %define api.namespace {]$1[}
812 %union { int i; }
813 %locations
815 %code {
816   int yylex (]$1[::parser::semantic_type *lval, const ]$1[::parser::location_type*) {
817     lval->i = 3;
818     return 0;
819   }
824 start: ;
828 void
829 ]$1[::parser::error (const ]$1[::parser::location_type &loc,
830                      const std::string &msg)
832   std::cerr << "At " << loc << ": " << msg << '\n';
835 ]AT_MAIN_DEFINE[
839 AT_BISON_CHECK([[-o input.cc input.yy]])
841 m4_if([$#], [1],
842 [AT_FOR_EACH_CXX([
843   AT_COMPILE_CXX([[input]])
844   AT_PARSER_CHECK([[input]])])])
845 AT_BISON_OPTION_POPDEFS
848 AT_SETUP([[Relative namespace references]])
849 AT_TEST([[foo]])
850 AT_TEST([[foo::bar]])
851 AT_TEST([[foo::bar::baz]])
852 AT_CLEANUP
854 AT_SETUP([[Absolute namespace references]])
855 AT_TEST([[::foo]])
856 AT_TEST([[::foo::bar]])
857 AT_TEST([[::foo::bar::baz]])
858 AT_TEST([[@tb@::foo]])
859 AT_TEST([[  @tb@ ::foo::bar]])
860 AT_TEST([[  ::foo::bar::baz]])
861 AT_CLEANUP
863 AT_SETUP([[Syntactically invalid namespace references]])
864 AT_TEST([[:foo:bar]], [[-]])
865 AT_TEST([[foo: :bar]], [[-]])
866 # This one is interesting because '[3]' is encoded as '@<:@3@:>@', which
867 # contains single occurrences of ':'.
868 AT_TEST([[foo[3]::bar::baz]], [[-]])
869 AT_TEST([[foo::bar,baz]], [[-]])
870 AT_TEST([[foo::bar::(baz /* Pacify Emacs ) */]], [[-]])
871 AT_CLEANUP
873 m4_popdef([AT_TEST])
876 ## -------------------------------------- ##
877 ## Syntax error discarding no lookahead.  ##
878 ## -------------------------------------- ##
880 # After a syntax error, lalr1.cc used to not check whether there
881 # actually is a lookahead before discarding the lookahead.  As a result,
882 # it mistakenly invoked the destructor for the previous lookahead.
884 AT_SETUP([[Syntax error discarding no lookahead]])
886 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
888 AT_DATA_GRAMMAR([[input.y]],
889 [[%skeleton "lalr1.cc"
891 %code {
892   #include <string>
893   int yylex (yy::parser::semantic_type *);
894   #define USE(Args)
897 %define parse.error verbose
899 %nonassoc 'a' ;
901 %destructor {
902   std::cerr << "Discarding 'a'.\n";
903 } 'a'
907 start: error-reduce consistent-error 'a' { USE ($3); };
909 error-reduce:
910   'a' 'a' consistent-error 'a' { USE (($1, $2, $4)); }
911 | 'a' error { std::cerr << "Reducing 'a'.\n"; USE ($1); }
914 consistent-error:
915   'a'
916 | %empty %prec 'a'
919 // Provide another context in which all rules are useful so that this
920 // test case looks a little more realistic.
921 start: 'b' consistent-error ;
926 yylex (yy::parser::semantic_type *)
928   static char const *input = "aa";
929   return *input++;
932 void
933 yy::parser::error (const std::string &m)
935   std::cerr << m << '\n';
938 ]AT_MAIN_DEFINE[
941 AT_FOR_EACH_CXX([
942 AT_FULL_COMPILE([[input]])
943 # This used to print "Discarding 'a'." again at the end.
944 AT_PARSER_CHECK([[input]], [[1]], [[]],
945 [[syntax error
946 Discarding 'a'.
947 Reducing 'a'.
951 AT_BISON_OPTION_POPDEFS
952 AT_CLEANUP
955 ## --------------------------- ##
956 ## Syntax error as exception.  ##
957 ## --------------------------- ##
959 # AT_TEST([BISON-DIRECTIVES = ''])
960 m4_pushdef([AT_TEST],
961 [AT_SETUP([[Syntax error as exception: $1]])
963 AT_BISON_OPTION_PUSHDEFS([$1 %debug])
965 AT_DATA_GRAMMAR([[input.yy]],
966 [[$1
967 %header
969 %code
971   #include <cstdlib>
972   int yylex (yy::parser::semantic_type *);
975 %define parse.error verbose
976 %define parse.trace
979 start: with-recovery | '!' without-recovery;
981 with-recovery:
982   %empty
983 | with-recovery item
984 | with-recovery error   { std::cerr << "caught error\n"; }
987 without-recovery:
988   %empty
989 | without-recovery item
992 item:
993   'a'
994 | 's'
995   {
996     throw syntax_error ("invalid expression");
997   }
1001 void
1002 yy::parser::error (const std::string &m)
1004   std::cerr << "error: " << m << '\n';
1006 ]AT_MAIN_DEFINE[
1009 # Another file to check syntax_error's linkage.
1010 AT_DATA_SOURCE([scan.cc],
1011 [[#include <cstdio>  // getchar
1012 #include "input.hh"
1014 // 'a': valid item, 's': syntax error, 'l': lexical error.
1016 yylex (yy::parser::semantic_type *lval)
1018   switch (int res = getchar ())
1019   {
1020     // Don't choke on echo's \n.
1021     case '\n':
1022       return yylex (lval);
1023     case 'l':
1024       throw yy::parser::syntax_error ("invalid character");
1025     default:
1026       return res;
1027   }
1031 AT_BISON_CHECK([[-o input.cc input.yy]])
1033 AT_FOR_EACH_CXX([
1034   AT_LANG_COMPILE([[input]], [[input.cc scan.cc]])
1036   # Leave enough valid tokens to make sure we recovered from the
1037   # previous error, otherwise we might hide some error messages
1038   # (discarded during error recoevery).
1039   echo "asaaalaa" >in
1040   AT_PARSER_CHECK([[input < in]], [[0]], [[]],
1041 [[error: invalid expression
1042 caught error
1043 error: invalid character
1044 caught error
1047   echo "!as" >in
1048   AT_PARSER_CHECK([[input < in]], [1], [],
1049 [[error: invalid expression
1052   echo "!al" >in
1053   AT_PARSER_CHECK([[input < in]], [1], [],
1054 [[error: invalid character
1057 ]) # AT_FOR_EACH_CXX
1059 AT_BISON_OPTION_POPDEFS
1060 AT_CLEANUP
1063 AT_TEST([%skeleton "lalr1.cc"])
1064 AT_TEST([%skeleton "glr.cc"])
1065 AT_TEST([%skeleton "glr2.cc"])
1067 m4_popdef([AT_TEST])
1071 ## ------------------ ##
1072 ## Exception safety.  ##
1073 ## ------------------ ##
1075 # AT_TEST([BISON-DIRECTIVES = ''], [WITH-RECOVERY = "with"])
1076 # ----------------------------------------------------------
1077 # Check that no object is leaked when exceptions are thrown.
1078 # WITH-RECOVERY = "with" or "without".
1079 m4_pushdef([AT_TEST],
1080 [AT_SETUP([[Exception safety $2 error recovery $1]])
1082 AT_SKIP_IF_EXCEPTION_SUPPORT_IS_POOR
1084 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1])
1086 AT_DATA_GRAMMAR([[input.yy]],
1087 [[%skeleton "lalr1.cc"
1088 %debug
1089 %define parse.error verbose
1091 %code requires
1093   #include <cassert>
1094   #include <cstdlib> // size_t and getenv.
1095   #include <iostream>
1096   #include <set>
1097   #include <string>
1099   bool debug = false;
1101   /// A class that tracks its instances.
1102   struct Object
1103   {
1104     char val;
1106     Object ()
1107       : val ('?')
1108     {
1109       log (this, "Object::Object");
1110       Object::instances.insert (this);
1111     }
1113     Object (const Object& that)
1114       : val (that.val)
1115     {
1116       log (this, "Object::Object");
1117       Object::instances.insert (this);
1118     }
1120     Object (char v)
1121       : val (v)
1122     {
1123       log (this, "Object::Object");
1124       Object::instances.insert (this);
1125     }
1127     ~Object ()
1128     {
1129       log (this, "Object::~Object");
1130       objects::iterator i = instances.find (this);
1131       // Make sure this object is alive.
1132       assert (i != instances.end ());
1133       Object::instances.erase (i);
1134     }
1136     Object& operator= (const Object& that)
1137     {
1138       val = that.val;
1139       return *this;
1140     }
1142     Object& operator= (char v)
1143     {
1144       val = v;
1145       return *this;
1146     }
1148     // Static part.
1149     typedef std::set<const Object*> objects;
1150     static objects instances;
1152     static bool
1153     empty ()
1154     {
1155       return instances.empty ();
1156     }
1158     static void
1159     log (Object const *o, const std::string& msg)
1160     {
1161       if (debug)
1162         {
1163           if (o)
1164             std::cerr << o << "->";
1165           std::cerr << msg << " {";
1166           const char* sep = " ";
1167           for (objects::const_iterator i = instances.begin(),
1168                                        i_end = instances.end();
1169                i != i_end;
1170                ++i)
1171             {
1172               std::cerr << sep << *i;
1173               sep = ", ";
1174             }
1175           std::cerr << " }\n";
1176         }
1177     }
1178   };
1181 %code
1183   #include <cassert>
1184   #include <cstring> // strchr
1185   #include <stdexcept>
1186   int yylex (yy::parser::semantic_type *);
1187   Object::objects Object::instances;
1188   static char const *input;
1191 ]AT_VARIANT_IF([[
1192 %printer
1194   yyo << &$$ << " '" << $$.val << '\'';
1195   if ($$.val == 'p')
1196     throw std::runtime_error ("printer");
1197 } <Object>;
1199 %token <Object> 'a' 'E' 'e' 'p' 'R' 's' 'T'
1200 %type  <Object> list item
1201 ]], [[
1202 %union
1204   Object *obj;
1206 %destructor { delete $$; } <obj>;
1207 %printer
1209   yyo << $$ << " '" << $$->val << '\'';
1210   if ($$->val == 'p')
1211     throw std::runtime_error ("printer");
1212 } <obj>;
1214 %token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T'
1215 %type  <obj> list item
1216 ]])[
1218 %initial-action
1220   if (strchr (input, 'i'))
1221     throw std::runtime_error ("initial-action");
1226 start: list {]AT_VARIANT_IF([], [ delete $][1]; )[};
1228 list:
1229   item       { $$ = $][1; }
1230   // Right recursion to load the stack.
1231 | item list  { $$ = $][1; ]AT_VARIANT_IF([], [delete $][2]; )[}
1234 item:
1235   'a'     { $$ = $][1; }
1236 | 'e'     { YY_USE ($$); YY_USE ($][1); error ("syntax error"); }
1237 // Not just 'E', otherwise we reduce when 'E' is the lookahead, and
1238 // then the stack is emptied, defeating the point of the test.
1239 | 'E' 'a' { YY_USE ($][1); $$ = $][2; }
1240 | 'R'     { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYERROR; }
1241 | 'p'     { $$ = $][1; }
1242 | 's'     { $$ = $][1; throw std::runtime_error ("reduction"); }
1243 | 'T'     { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYABORT; }
1244 ]m4_if([$2], [with],
1245 [[| error   { $$ = ]AT_VARIANT_IF([], [new ])[Object ('R'); yyerrok; }]])[
1250 yylex (yy::parser::semantic_type *lvalp)
1252   // 'a': no error.
1253   // 'e': user action calls error.
1254   // 'E': syntax error, with yyerror that throws.
1255   // 'i': initial action throws.
1256   // 'l': yylex throws.
1257   // 'R': call YYERROR in the action
1258   // 's': reduction throws.
1259   // 'T': call YYABORT in the action
1260   switch (char res = *input++)
1261   {
1262   case 'l':
1263     throw std::runtime_error ("yylex");
1264   default:
1265     lvalp->]AT_VARIANT_IF([build<Object> (res)],
1266                           [obj = new Object (res)])[;
1267     goto zero;
1268   zero:
1269   case 0:
1270     return res;
1271   }
1274 /* A C++ error reporting function.  */
1275 void
1276 yy::parser::error (const std::string& m)
1278   throw std::runtime_error (m);
1282 main (int argc, const char *argv[])
1284   switch (argc)
1285   {
1286     case 2:
1287       input = argv[1];
1288       break;
1289     case 3:
1290       assert (std::string(argv[1]) == "--debug");
1291       debug = 1;
1292       input = argv[2];
1293       break;
1294     default:
1295       abort ();
1296   }
1298   yy::parser parser;
1299   debug |= !!getenv ("YYDEBUG");
1300   parser.set_debug_level (debug);
1301   int res = 2;
1302   try
1303   {
1304     res = parser.parse ();
1305   }
1306   catch (const std::exception& e)
1307   {
1308     std::cerr << "exception caught: " << e.what () << '\n';
1309   }
1310   catch (...)
1311   {
1312     std::cerr << "unknown exception caught\n";
1313   }
1314   Object::log (YY_NULLPTR, "end");
1315   assert (Object::empty());
1316   return res;
1319 AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
1321 AT_FOR_EACH_CXX([
1322 AT_COMPILE_CXX([[input]])
1324 AT_PARSER_CHECK([[input aaaas]], [[2]], [[]],
1325 [[exception caught: reduction
1328 AT_PARSER_CHECK([[input aaaal]], [[2]], [[]],
1329 [[exception caught: yylex
1332 AT_PARSER_CHECK([[input i]], [[2]], [[]],
1333 [[exception caught: initial-action
1336 AT_PARSER_CHECK([[input aaaap]])
1338 AT_PARSER_CHECK([[input --debug aaaap]], [[2]], [[]], [[stderr]])
1339 AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
1341 AT_PARSER_CHECK([[input aaaae]], [[2]], [[]],
1342 [[exception caught: syntax error
1345 AT_PARSER_CHECK([[input aaaaE]], [[2]], [[]],
1346 [[exception caught: syntax error, unexpected end of file, expecting 'a'
1349 AT_PARSER_CHECK([[input aaaaT]], [[1]])
1351 AT_PARSER_CHECK([[input aaaaR]], [m4_if([$2], [with], [0], [1])])
1354 AT_BISON_OPTION_POPDEFS
1356 AT_CLEANUP
1359 AT_TEST([], [with])
1360 AT_TEST([], [without])
1361 AT_TEST([%define api.value.type variant], [with])
1362 AT_TEST([%define api.value.type variant], [without])
1364 m4_popdef([AT_TEST])
1366 ## ------------------------------------- ##
1367 ## C++ GLR parser identifier shadowing.  ##
1368 ## ------------------------------------- ##
1370 AT_SETUP([[C++ GLR parser identifier shadowing]])
1372 AT_BISON_OPTION_PUSHDEFS([%skeleton "glr.cc"])
1373 AT_DATA_GRAMMAR([input.yy], [
1374 %skeleton "glr.cc"
1376 %union
1378   int ival;
1381 %token <ival> ZERO;
1383 %code
1385   int yylex (yy::parser::semantic_type *lvalp);
1389 exp: ZERO
1393 int yylex (yy::parser::semantic_type *lvalp)
1395   // Note: this argument is unused, but named on purpose.  There used to be a
1396   // bug with a macro that erroneously expanded this identifier to
1397   // yystackp->yyval.
1398   YY_USE (lvalp);
1399   return ]AT_TOKEN([ZERO])[;
1402 void yy::parser::error (std::string const&)
1405 int main ()
1409 AT_BISON_CHECK([[-o input.cc input.yy]])
1410 AT_FOR_EACH_CXX([AT_COMPILE_CXX([[input]])])
1412 AT_BISON_OPTION_POPDEFS
1413 AT_CLEANUP
1417 ## ------------------ ##
1418 ## Shared locations.  ##
1419 ## ------------------ ##
1421 AT_SETUP([Shared locations])
1423 # AT_TEST([PREFIX], [DIRECTIVES])
1424 # -------------------------------
1425 # Generate and compile to *.o.  Make sure there is no (allowed) YY*
1426 # nor yy* identifiers in the header after applying api.prefix.  Check
1427 # that headers can be compiled by a C++ compiler.
1428 m4_pushdef([AT_TEST],
1429 [AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %define api.namespace {$1} $2])
1430 AT_LOC_PUSHDEF([begin.line], [begin.column], [end.line], [end.column])
1431 AT_DATA_GRAMMAR([$1.yy],
1432 [[%skeleton "lalr1.cc"
1433 %define api.namespace {$1}
1435 %code {
1436   ]AT_YYERROR_DECLARE[
1437   ]AT_YYLEX_DECLARE[
1440 exp: '0';
1442 ]AT_YYERROR_DEFINE[
1443 ]AT_YYLEX_DEFINE(["0"])[
1446 AT_BISON_CHECK([-fcaret -o $1.cc $1.yy])
1447 AT_LANG_COMPILE([$1.o], [], [-Iinclude])
1449 AT_LOC_POPDEF
1450 AT_BISON_OPTION_POPDEFS
1453 mkdir -p include/ast
1455 AT_TEST([x1],
1456         [%header
1457          %locations
1458          %define api.location.file "include/ast/loc.hh"
1459          %define api.location.include {<ast/loc.hh>}])
1461 # Check the CPP guard and Doxyen comments.
1462 AT_CHECK([sed -ne '/INCLUDED/p;/\\file/{p;n;p;}' include/ast/loc.hh], [],
1463 [[ ** \file ast/loc.hh
1464  ** Define the x1::location class.
1465 #ifndef YY_YY_AST_LOC_HH_INCLUDED
1466 # define YY_YY_AST_LOC_HH_INCLUDED
1467 #endif // !YY_YY_AST_LOC_HH_INCLUDED
1470 AT_TEST([x2],
1471         [%header
1472          %locations
1473          %code requires {#include <ast/loc.hh>}
1474          %define api.location.type {x1::location}])
1476 m4_popdef([AT_TEST])
1478 AT_DATA([main.cc],
1479 [AT_DATA_SOURCE_PROLOGUE
1480 [#include "x1.hh"
1481 #include "x2.hh"
1483 #define RUN(S)                                  \
1484   do {                                          \
1485     S::parser parser;                           \
1486     int res = parser.parse();                   \
1487     if (res)                                    \
1488       std::cerr << #S": " << res << '\n';       \
1489   } while (false)
1492 main (void)
1494   RUN(x1);
1495   RUN(x2);
1497 ]])# main.cc
1500 AT_COMPILE_CXX([parser], [[x[12].o main.cc]], [-Iinclude])
1501 AT_PARSER_CHECK([parser], [0])
1503 AT_CLEANUP
1507 ## ---------------- ##
1508 ## Default action.  ##
1509 ## ---------------- ##
1511 # In C++ we generate explicitly the code for the default action
1512 # instead of simply copying blindly the semantic value buffer.  This
1513 # is important when copying raw memory is not enough, as exemplified
1514 # by move-only types.
1516 AT_SETUP([Default action])
1517 AT_KEYWORDS([action])
1519 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"
1520 %define api.token.constructor
1521 %define api.value.type variant])
1523 AT_DATA_GRAMMAR([test.y],
1524 [[%code requires {
1525 #include <memory> // unique_ptr
1527 %code {
1528   ]AT_YYERROR_DECLARE[
1529   ]AT_YYLEX_DECLARE[
1531 ]AT_BISON_OPTIONS[
1532 %define api.value.automove
1533 %token ONE TWO EOI 0
1534 %type <std::unique_ptr<int>> ONE TWO one two one.opt two.opt
1536 exp: one.opt two.opt { std::cout << *$][1 << ", " << *$][2 << '\n'; }
1537 one.opt: one | %empty {}
1538 two.opt: two | %empty {}
1539 one: ONE
1540 two: TWO
1542 ]AT_YYERROR_DEFINE[
1543 ]AT_YYLEX_DEFINE(["12"],
1544 [ if (res == '1')
1545     return yy::parser::make_ONE (std::make_unique<int> (10));
1546   else if (res == '2')
1547     return yy::parser::make_TWO (std::make_unique<int> (20));
1548   else
1549     return yy::parser::make_EOI ();
1551 ]AT_MAIN_DEFINE[
1554 AT_LANG_FOR_EACH_STD([
1555   AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])
1556   AT_FULL_COMPILE([[test]], [], [], [], [-fcaret])
1557   AT_PARSER_CHECK([[test]], 0, [[10, 20
1561 AT_BISON_OPTION_POPDEFS
1563 AT_CLEANUP