1 # Checking the C++ Features. -*- Autotest -*-
3 # Copyright (C) 2004-2005, 2007-2015, 2018-2021 Free Software
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>}
47 check (const T& in, const std::string& s)
49 const static bool verbose = getenv ("DEBUG");
55 std::cerr << os.str () << '\n';
60 std::cerr << "fail: " << os.str () << ", expected: " << s << '\n';
68 const std::string fn = "foo.txt";
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");
93 AT_FULL_COMPILE([input])
94 AT_PARSER_CHECK([input], 0)
96 AT_BISON_OPTION_POPDEFS
100 ## -------------------------------------- ##
101 ## C++ Variant-based Symbols Unit Tests. ##
102 ## -------------------------------------- ##
104 # Not checking the grammar, only the variants and variant based
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
124 %token <int> INT "int"
125 %type <std::vector<int>> exp
127 %printer { yyo << $$; } <int>
130 for (std::vector<int>::const_iterator i = $$.begin (); i != $$.end (); ++i)
132 if (i != $$.begin ())
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,
143 %code yy_bison_internal_hook {
145 typedef stack_symbol_type yy_stack_symbol_type;
146 typedef stack_type yy_stack_type;
150 exp: "int" { $$.push_back ($1); }
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';
161 std::cerr << "Assertion failed: " << exp << " != " << eff << '\n';
167 // symbol_type: construction, accessor.
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);
176 // symbol_type: move constructor.
177 #if 201103L <= YY_CPLUSPLUS
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.
187 // symbol_type: copy constructor.
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);
195 // stack_symbol_type: construction, accessor.
196 typedef parser::yy_stack_symbol_type stack_symbol_type;
198 #if 201103L <= YY_CPLUSPLUS
199 auto ss = stack_symbol_type (1, parser::make_INT(123));
201 parser::symbol_type s = parser::make_INT (123);
202 stack_symbol_type ss(1, s);
204 assert_eq (ss.value.as<int> (), 123);
207 // Pushing on the stack.
208 // Sufficiently many so that it will be resized.
209 // Probably 3 times (starting at 200).
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)
216 #if 201103L <= YY_CPLUSPLUS
217 st.push(stack_symbol_type{int_reduction_state,
218 parser::make_INT (i)});
220 parser::symbol_type s = parser::make_INT (i);
221 stack_symbol_type ss (int_reduction_state, s);
225 for (int i = mucho - 1; 0 <= i; --i)
227 assert_eq (st[0].value.as<int>(), i);
234 AT_BISON_CHECK([[-o list.cc list.yy]])
236 AT_COMPILE_CXX([list])
237 AT_PARSER_CHECK([list])
240 AT_BISON_OPTION_POPDEFS
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"
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; }
267 input.yy:17.33-36: warning: multiple occurrences of $2 with api.value.automove [-Wother]
268 17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
270 input.yy:17.40-41: warning: multiple occurrences of $2 with api.value.automove [-Wother]
271 17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
275 AT_BISON_OPTION_POPDEFS
283 # Check that the variants are properly supported, including in error
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],
298 %define api.value.type variant
299 ]m4_bpatsubst([$1], [\\n], [
302 %code top // code for the .cc file.
304 #include <cstdlib> // abort, getenv
315 string (const std::string& s)
319 string (const string& s)
323 string& operator= (const string& s)
329 #if defined __cplusplus && 201103L <= __cplusplus
330 string (string&& s) noexcept
331 : val_(std::move(s.val_))
336 string& operator= (string&& s)
338 val_ = std::move(s.val_);
345 std::ostream& operator<< (std::ostream& o, const string& s)
354 typedef std::vector<string> strings_type;
358 // Must be available early, as is used in %destructor.
360 operator<<(std::ostream& o, const strings_type& s)
363 for (strings_type::const_iterator i = s.begin (); i != s.end (); ++i)
374 %code // code for the .cc file.
379 ]AT_YYLEX_PROTOTYPE[;
381 // Conversion to string.
382 template <typename T>
385 to_string (const T& t)
387 std::ostringstream o;
389 return string (o.str ());
394 %token <::string> TEXT;
396 %token END_OF_FILE 0;
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>;
411 list { std::cout << $][1 << '\n'; }
415 item { $$.push_back ($][1); }
416 | list "," item { $$ = $][1; $$.push_back ($][3); }
417 | list error { $$ = $][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
429 # define BUILD(Type, Value) build (Value)
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++)
444 case 0:]AT_TOKEN_CTOR_IF([[
445 return parser::make_END_OF_FILE (]AT_LOCATION_IF([location ()])[);]],
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 ()])[);]],
453 *llocp = location ();])[
454 return ]AT_TOKEN([COMMA])[;]])[
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)[;]])[
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])[;]])[
480 AT_DATA_SOURCE([[modern.cc]],
481 [[#include <iostream>
484 #if defined __cplusplus && 201103L <= __cplusplus
485 std::cout << "Modern C++: " << __cplusplus << '\n';
488 std::cout << "Legac++\n";
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
507 if AT_AUTOMOVE_IF([$modern], [false]); then
508 AT_PARSER_CHECK([list], 0,
531 Destroy: (0, 1, 2, 4, 6)
534 AT_PARSER_CHECK([list], 0,
551 Destroy: (0, 1, 2, 4)
553 Destroy: (0, 1, 2, 4)
556 Destroy: (0, 1, 2, 4)
557 Destroy: (0, 1, 2, 4, 6)
562 AT_BISON_OPTION_POPDEFS
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]])
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,
592 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
594 AT_DATA_GRAMMAR([[input.y]],
595 [[%skeleton "lalr1.cc"
600 %define api.value.type variant
601 %define api.token.constructor
602 %define parse.error verbose
609 static yy::parser::symbol_type yylex();
616 %printer { yyo << $$; } <int>;
617 %destructor { std::cerr << "destroy: " << $$ << '\n'; } <int>
620 NUMBER { $$ = $1 * 10; }
621 | expr <int>{ $$ = 20; } NUMBER
623 std::cerr << "expr: " << $1 << ' ' << $2 << ' ' << $3 << '\n';
631 parser::symbol_type yylex()
637 return parser::make_NUMBER (1);
639 return parser::make_NUMBER (30);
641 return parser::make_EOI ();
645 void parser::error(const std::string& message)
647 std::cerr << message << '\n';
654 p.set_debug_level (1);
660 AT_FULL_COMPILE([[input]])
661 # This used to print "Discarding 'a'." again at the end.
662 AT_PARSER_CHECK([[input]], [[0]], [[]],
667 Next token is token NUMBER (1)
668 Shifting token NUMBER (1)
671 Reducing stack by rule 1 (line 34):
672 $1 = token NUMBER (1)
673 -> $$ = nterm expr (10)
678 Next token is token NUMBER (30)
679 Reducing stack by rule 2 (line 35):
680 -> $$ = nterm @1 (20)
683 Next token is token NUMBER (30)
684 Shifting token NUMBER (30)
687 Reducing stack by rule 3 (line 35):
690 $3 = token NUMBER (30)
692 -> $$ = nterm expr (40)
699 Next token is token EOI ()
700 Shifting token EOI ()
704 Cleanup: popping token EOI ()
705 Cleanup: popping nterm expr (40)
710 AT_BISON_OPTION_POPDEFS
714 ## ----------------------- ##
715 ## Doxygen Documentation. ##
716 ## ----------------------- ##
718 m4_define([AT_CHECK_DOXYGEN],
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])
738 AT_BISON_CHECK([-o input.cc input.yy])
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
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.
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
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
791 m4_popdef([AT_DOXYGEN_PRIVATE])
794 AT_CHECK_DOXYGEN([Public])
795 AT_CHECK_DOXYGEN([Private])
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]],
811 %define api.namespace {]$1[}
816 int yylex (]$1[::parser::semantic_type *lval, const ]$1[::parser::location_type*) {
829 ]$1[::parser::error (const ]$1[::parser::location_type &loc,
830 const std::string &msg)
832 std::cerr << "At " << loc << ": " << msg << '\n';
839 AT_BISON_CHECK([[-o input.cc input.yy]])
843 AT_COMPILE_CXX([[input]])
844 AT_PARSER_CHECK([[input]])])])
845 AT_BISON_OPTION_POPDEFS
848 AT_SETUP([[Relative namespace references]])
850 AT_TEST([[foo::bar]])
851 AT_TEST([[foo::bar::baz]])
854 AT_SETUP([[Absolute namespace references]])
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]])
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 ) */]], [[-]])
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"
893 int yylex (yy::parser::semantic_type *);
897 %define parse.error verbose
902 std::cerr << "Discarding 'a'.\n";
907 start: error-reduce consistent-error 'a' { USE ($3); };
910 'a' 'a' consistent-error 'a' { USE (($1, $2, $4)); }
911 | 'a' error { std::cerr << "Reducing 'a'.\n"; USE ($1); }
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";
933 yy::parser::error (const std::string &m)
935 std::cerr << m << '\n';
942 AT_FULL_COMPILE([[input]])
943 # This used to print "Discarding 'a'." again at the end.
944 AT_PARSER_CHECK([[input]], [[1]], [[]],
951 AT_BISON_OPTION_POPDEFS
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]],
972 int yylex (yy::parser::value_type *);
975 %define parse.error verbose
979 start: with-recovery | '!' without-recovery;
984 | with-recovery error { std::cerr << "caught error\n"; }
989 | without-recovery item
996 throw syntax_error ("invalid expression");
1002 yy::parser::error (const std::string &m)
1004 std::cerr << "error: " << m << '\n';
1009 # Another file to check syntax_error's linkage.
1010 AT_DATA_SOURCE([scan.cc],
1011 [[#include <cstdio> // getchar
1014 // 'a': valid item, 's': syntax error, 'l': lexical error.
1016 yylex (yy::parser::value_type *lval)
1018 switch (int res = getchar ())
1020 // Don't choke on echo's \n.
1022 return yylex (lval);
1024 throw yy::parser::syntax_error ("invalid character");
1031 AT_BISON_CHECK([[-o input.cc input.yy]])
1034 AT_GLR2_CC_IF([AT_REQUIRE_CXX_STD(11, [echo "$at_std not supported"; continue])])
1035 AT_LANG_COMPILE([[input]], [[input.cc scan.cc]])
1037 # Leave enough valid tokens to make sure we recovered from the
1038 # previous error, otherwise we might hide some error messages
1039 # (discarded during error recoevery).
1041 AT_PARSER_CHECK([[input < in]], [[0]], [[]],
1042 [[error: invalid expression
1044 error: invalid character
1049 AT_PARSER_CHECK([[input < in]], [1], [],
1050 [[error: invalid expression
1054 AT_PARSER_CHECK([[input < in]], [1], [],
1055 [[error: invalid character
1058 ]) # AT_FOR_EACH_CXX
1060 AT_BISON_OPTION_POPDEFS
1064 AT_TEST([%skeleton "lalr1.cc"])
1065 AT_TEST([%skeleton "glr.cc"])
1066 AT_TEST([%skeleton "glr2.cc"])
1068 m4_popdef([AT_TEST])
1072 ## ------------------ ##
1073 ## Exception safety. ##
1074 ## ------------------ ##
1076 # AT_TEST([BISON-DIRECTIVES = ''], [WITH-RECOVERY = "with"])
1077 # ----------------------------------------------------------
1078 # Check that no object is leaked when exceptions are thrown.
1079 # WITH-RECOVERY = "with" or "without".
1080 m4_pushdef([AT_TEST],
1081 [AT_SETUP([[Exception safety $2 error recovery $1]])
1083 AT_SKIP_IF_EXCEPTION_SUPPORT_IS_POOR
1085 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1])
1087 AT_DATA_GRAMMAR([[input.yy]],
1088 [[%skeleton "lalr1.cc"
1090 %define parse.error verbose
1095 #include <cstdlib> // size_t and getenv.
1102 /// A class that tracks its instances.
1110 log (this, "Object::Object");
1111 Object::instances.insert (this);
1114 Object (const Object& that)
1117 log (this, "Object::Object");
1118 Object::instances.insert (this);
1124 log (this, "Object::Object");
1125 Object::instances.insert (this);
1130 log (this, "Object::~Object");
1131 objects::iterator i = instances.find (this);
1132 // Make sure this object is alive.
1133 assert (i != instances.end ());
1134 Object::instances.erase (i);
1137 Object& operator= (const Object& that)
1143 Object& operator= (char v)
1150 typedef std::set<const Object*> objects;
1151 static objects instances;
1156 return instances.empty ();
1160 log (Object const *o, const std::string& msg)
1165 std::cerr << o << "->";
1166 std::cerr << msg << " {";
1167 const char* sep = " ";
1168 for (objects::const_iterator i = instances.begin(),
1169 i_end = instances.end();
1173 std::cerr << sep << *i;
1176 std::cerr << " }\n";
1185 #include <cstring> // strchr
1186 #include <stdexcept>
1187 int yylex (yy::parser::semantic_type *);
1188 Object::objects Object::instances;
1189 static char const *input;
1195 yyo << &$$ << " '" << $$.val << '\'';
1197 throw std::runtime_error ("printer");
1200 %token <Object> 'a' 'E' 'e' 'p' 'R' 's' 'T'
1201 %type <Object> list item
1207 %destructor { delete $$; } <obj>;
1210 yyo << $$ << " '" << $$->val << '\'';
1212 throw std::runtime_error ("printer");
1215 %token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T'
1216 %type <obj> list item
1221 if (strchr (input, 'i'))
1222 throw std::runtime_error ("initial-action");
1227 start: list {]AT_VARIANT_IF([], [ delete $][1]; )[};
1231 // Right recursion to load the stack.
1232 | item list { $$ = $][1; ]AT_VARIANT_IF([], [delete $][2]; )[}
1237 | 'e' { YY_USE ($$); YY_USE ($][1); error ("syntax error"); }
1238 // Not just 'E', otherwise we reduce when 'E' is the lookahead, and
1239 // then the stack is emptied, defeating the point of the test.
1240 | 'E' 'a' { YY_USE ($][1); $$ = $][2; }
1241 | 'R' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYERROR; }
1242 | 'p' { $$ = $][1; }
1243 | 's' { $$ = $][1; throw std::runtime_error ("reduction"); }
1244 | 'T' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYABORT; }
1245 ]m4_if([$2], [with],
1246 [[| error { $$ = ]AT_VARIANT_IF([], [new ])[Object ('R'); yyerrok; }]])[
1251 yylex (yy::parser::semantic_type *lvalp)
1254 // 'e': user action calls error.
1255 // 'E': syntax error, with yyerror that throws.
1256 // 'i': initial action throws.
1257 // 'l': yylex throws.
1258 // 'R': call YYERROR in the action
1259 // 's': reduction throws.
1260 // 'T': call YYABORT in the action
1261 switch (char res = *input++)
1264 throw std::runtime_error ("yylex");
1266 lvalp->]AT_VARIANT_IF([build<Object> (res)],
1267 [obj = new Object (res)])[;
1275 /* A C++ error reporting function. */
1277 yy::parser::error (const std::string& m)
1279 throw std::runtime_error (m);
1283 main (int argc, const char *argv[])
1291 assert (std::string(argv[1]) == "--debug");
1300 debug |= !!getenv ("YYDEBUG");
1301 parser.set_debug_level (debug);
1305 res = parser.parse ();
1307 catch (const std::exception& e)
1309 std::cerr << "exception caught: " << e.what () << '\n';
1313 std::cerr << "unknown exception caught\n";
1315 Object::log (YY_NULLPTR, "end");
1316 assert (Object::empty());
1320 AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
1323 AT_COMPILE_CXX([[input]])
1325 AT_PARSER_CHECK([[input aaaas]], [[2]], [[]],
1326 [[exception caught: reduction
1329 AT_PARSER_CHECK([[input aaaal]], [[2]], [[]],
1330 [[exception caught: yylex
1333 AT_PARSER_CHECK([[input i]], [[2]], [[]],
1334 [[exception caught: initial-action
1337 AT_PARSER_CHECK([[input aaaap]])
1339 AT_PARSER_CHECK([[input --debug aaaap]], [[2]], [[]], [[stderr]])
1340 AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
1342 AT_PARSER_CHECK([[input aaaae]], [[2]], [[]],
1343 [[exception caught: syntax error
1346 AT_PARSER_CHECK([[input aaaaE]], [[2]], [[]],
1347 [[exception caught: syntax error, unexpected end of file, expecting 'a'
1350 AT_PARSER_CHECK([[input aaaaT]], [[1]])
1352 AT_PARSER_CHECK([[input aaaaR]], [m4_if([$2], [with], [0], [1])])
1355 AT_BISON_OPTION_POPDEFS
1361 AT_TEST([], [without])
1362 AT_TEST([%define api.value.type variant], [with])
1363 AT_TEST([%define api.value.type variant], [without])
1365 m4_popdef([AT_TEST])
1367 ## ------------------------------------- ##
1368 ## C++ GLR parser identifier shadowing. ##
1369 ## ------------------------------------- ##
1371 AT_SETUP([[C++ GLR parser identifier shadowing]])
1373 AT_BISON_OPTION_PUSHDEFS([%skeleton "glr.cc"])
1374 AT_DATA_GRAMMAR([input.yy], [
1386 int yylex (yy::parser::semantic_type *lvalp);
1394 int yylex (yy::parser::semantic_type *lvalp)
1396 // Note: this argument is unused, but named on purpose. There used to be a
1397 // bug with a macro that erroneously expanded this identifier to
1400 return ]AT_TOKEN([ZERO])[;
1403 void yy::parser::error (std::string const&)
1410 AT_BISON_CHECK([[-o input.cc input.yy]])
1411 AT_FOR_EACH_CXX([AT_COMPILE_CXX([[input]])])
1413 AT_BISON_OPTION_POPDEFS
1418 ## ------------------ ##
1419 ## Shared locations. ##
1420 ## ------------------ ##
1422 AT_SETUP([Shared locations])
1424 # AT_TEST([PREFIX], [DIRECTIVES])
1425 # -------------------------------
1426 # Generate and compile to *.o. Make sure there is no (allowed) YY*
1427 # nor yy* identifiers in the header after applying api.prefix. Check
1428 # that headers can be compiled by a C++ compiler.
1429 m4_pushdef([AT_TEST],
1430 [AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %define api.namespace {$1} $2])
1431 AT_LOC_PUSHDEF([begin.line], [begin.column], [end.line], [end.column])
1432 AT_DATA_GRAMMAR([$1.yy],
1433 [[%skeleton "lalr1.cc"
1434 %define api.namespace {$1}
1437 ]AT_YYERROR_DECLARE[
1444 ]AT_YYLEX_DEFINE(["0"])[
1447 AT_BISON_CHECK([-fcaret -o $1.cc $1.yy])
1448 AT_LANG_COMPILE([$1.o], [], [-Iinclude])
1451 AT_BISON_OPTION_POPDEFS
1454 mkdir -p include/ast
1459 %define api.location.file "include/ast/loc.hh"
1460 %define api.location.include {<ast/loc.hh>}])
1462 # Check the CPP guard and Doxyen comments.
1463 AT_CHECK([sed -ne '/INCLUDED/p;/\\file/{p;n;p;}' include/ast/loc.hh], [],
1464 [[ ** \file ast/loc.hh
1465 ** Define the x1::location class.
1466 #ifndef YY_YY_AST_LOC_HH_INCLUDED
1467 # define YY_YY_AST_LOC_HH_INCLUDED
1468 #endif // !YY_YY_AST_LOC_HH_INCLUDED
1474 %code requires {#include <ast/loc.hh>}
1475 %define api.location.type {x1::location}])
1477 m4_popdef([AT_TEST])
1480 [AT_DATA_SOURCE_PROLOGUE
1487 int res = parser.parse(); \
1489 std::cerr << #S": " << res << '\n'; \
1501 AT_COMPILE_CXX([parser], [[x[12].o main.cc]], [-Iinclude])
1502 AT_PARSER_CHECK([parser], [0])
1508 ## ---------------- ##
1509 ## Default action. ##
1510 ## ---------------- ##
1512 # In C++ we generate explicitly the code for the default action
1513 # instead of simply copying blindly the semantic value buffer. This
1514 # is important when copying raw memory is not enough, as exemplified
1515 # by move-only types.
1517 AT_SETUP([Default action])
1518 AT_KEYWORDS([action])
1520 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"
1521 %define api.token.constructor
1522 %define api.value.type variant])
1524 AT_DATA_GRAMMAR([test.y],
1526 #include <memory> // unique_ptr
1529 ]AT_YYERROR_DECLARE[
1533 %define api.value.automove
1534 %token ONE TWO EOI 0
1535 %type <std::unique_ptr<int>> ONE TWO one two one.opt two.opt
1537 exp: one.opt two.opt { std::cout << *$][1 << ", " << *$][2 << '\n'; }
1538 one.opt: one | %empty {}
1539 two.opt: two | %empty {}
1544 ]AT_YYLEX_DEFINE(["12"],
1546 return yy::parser::make_ONE (std::make_unique<int> (10));
1547 else if (res == '2')
1548 return yy::parser::make_TWO (std::make_unique<int> (20));
1550 return yy::parser::make_EOI ();
1555 AT_LANG_FOR_EACH_STD([
1556 AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])
1557 AT_FULL_COMPILE([[test]], [], [], [], [-fcaret])
1558 AT_PARSER_CHECK([[test]], 0, [[10, 20
1562 AT_BISON_OPTION_POPDEFS