# Checking the C++ Features. -*- Autotest -*-
-# Copyright (C) 2004-2005, 2007, 2009-2012 Free Software Foundation,
-# Inc.
+# Copyright (C) 2004-2005, 2007-2012 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
AT_BANNER([[C++ Features.]])
+## ---------- ##
+## Variants. ##
+## ---------- ##
+
+# AT_TEST([DIRECTIVES])
+# ---------------------
+# Check the support of variants in C++, with the additional DIRECTIVES.
+m4_pushdef([AT_TEST],
+[AT_SETUP([Variants $1])
+
+AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1])
+# Store strings and integers in a list of strings.
+AT_DATA_GRAMMAR([list.yy],
+[[%debug
+%skeleton "lalr1.cc"
+%defines
+%define api.value.type variant
+]m4_bpatsubst([$1], [\\n], [
+])[
+
+%code requires // code for the .hh file
+{
+#include <list>
+#include <string>
+typedef std::list<std::string> strings_type;
+}
+
+%code // code for the .cc file
+{
+#include <cstdlib> // abort, getenv
+#include <iostream>
+#include <sstream>
+
+ namespace yy
+ {
+ static]AT_TOKEN_CTOR_IF([[
+ parser::symbol_type yylex ()]], [[
+ parser::token_type yylex (parser::semantic_type* yylval]AT_LOCATION_IF([,
+ parser::location_type* yylloc])[)]])[;
+ }
+
+ // Printing a list of strings (for %printer).
+ // Koening look up will look into std, since that's an std::list.
+ namespace std
+ {
+ std::ostream&
+ operator<<(std::ostream& o, const strings_type& s)
+ {
+ o << '(';
+ for (strings_type::const_iterator i = s.begin (); i != s.end (); ++i)
+ {
+ if (i != s.begin ())
+ o << ", ";
+ o << *i;
+ }
+ return o << ')';
+ }
+ }
+
+ // Conversion to string.
+ template <typename T>
+ inline
+ std::string
+ string_cast (const T& t)
+ {
+ std::ostringstream o;
+ o << t;
+ return o.str ();
+ }
+}
+
+%token <::std::string> TEXT;
+%token <int> NUMBER;
+%token END_OF_FILE 0;
+
+%type <::std::string> item;
+// Using the template type to exercize its parsing.
+// Starting with :: to ensure we don't output "<::" which starts by the
+// digraph for the left square bracket.
+%type <::std::list<std::string>> list result;
+
+%printer { yyo << $][$; }
+ <int> <::std::string> <::std::list<std::string>>;
+%%
+
+result:
+ list { std::cout << $][1 << std::endl; }
+;
+
+list:
+ /* nothing */ { /* Generates an empty string list */ }
+| list item { std::swap ($][$,$][1); $$.push_back ($][2); }
+| list error { std::swap ($][$,$][1); }
+;
+
+item:
+ TEXT { std::swap ($][$,$][1); }
+| NUMBER { if ($][1 == 3) YYERROR; else $][$ = string_cast ($][1); }
+;
+%%
+
+#ifdef TWO_STAGE_BUILD
+# define BUILD(Type, Value) build<Type> () = Value
+#else
+# define BUILD(Type, Value) build (Value)
+#endif
+
+#define STAGE_MAX 5
+namespace yy
+{
+ static]AT_TOKEN_CTOR_IF([[
+ parser::symbol_type yylex ()]], [[
+ parser::token_type yylex (parser::semantic_type* yylval]AT_LOCATION_IF([,
+ parser::location_type* yylloc])[)]])[
+ {]AT_LOCATION_IF([
+ typedef parser::location_type location;])[
+ static int stage = -1;
+ ++stage;
+ if (stage == STAGE_MAX)
+ {]AT_TOKEN_CTOR_IF([[
+ return parser::make_END_OF_FILE (]AT_LOCATION_IF([location ()])[);]],
+[AT_LOCATION_IF([
+ *yylloc = location ();])[
+ return parser::token::END_OF_FILE;]])[
+ }
+ else if (stage % 2)
+ {]AT_TOKEN_CTOR_IF([[
+ return parser::make_NUMBER (stage]AT_LOCATION_IF([, location ()])[);]],
+[[
+ yylval->BUILD (int, stage);]AT_LOCATION_IF([
+ *yylloc = location ();])[
+ return parser::token::NUMBER;]])[
+ }
+ else
+ {]AT_TOKEN_CTOR_IF([[
+ return parser::make_TEXT (string_cast (stage)]AT_LOCATION_IF([, location ()])[);]], [[
+ yylval->BUILD (std::string, string_cast (stage));]AT_LOCATION_IF([
+ *yylloc = location ();])[
+ return parser::token::TEXT;]])[
+ }
+ abort ();
+ }
+}
+
+]AT_YYERROR_DEFINE[
+
+int
+main ()
+{
+ yy::parser p;
+ p.set_debug_level (!!getenv ("YYDEBUG"));
+ return p.parse ();
+}
+]])
+
+AT_BISON_CHECK([-o list.cc list.yy])
+AT_COMPILE_CXX([list], [$NO_STRICT_ALIAS_CXXFLAGS list.cc])
+AT_PARSER_CHECK([./list], 0,
+[(0, 1, 2, 4)
+])
+
+AT_BISON_OPTION_POPDEFS
+AT_CLEANUP
+])
+
+AT_TEST([])
+AT_TEST([%define parse.assert])
+AT_TEST([%locations %define parse.assert])
+AT_TEST([[%define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]])
+AT_TEST([[%define parse.assert %define api.token.constructor]])
+AT_TEST([[%define parse.assert %define api.token.constructor %define api.token.prefix "TOK_"]])
+AT_TEST([[%locations %define parse.assert %define api.token.constructor %define api.token.prefix "TOK_"]])
+
+m4_popdef([AT_TEST])
+
+
## ----------------------- ##
## Doxygen Documentation. ##
## ----------------------- ##
[m4_case([$1],
[Public], [m4_pushdef([AT_DOXYGEN_PRIVATE], [NO])],
[Private], [m4_pushdef([AT_DOXYGEN_PRIVATE], [YES])],
- [m4_fatal([invalid argument: $1])])
+ [m4_fatal([invalid argument: $1])])
AT_SETUP([Doxygen $1 Documentation])
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
AT_CHECK_DOXYGEN([Public])
AT_CHECK_DOXYGEN([Private])
+
## ------------ ##
## Namespaces. ##
## ------------ ##
AT_DATA_GRAMMAR([[input.y]],
[[%language "C++"
%defines
-%define namespace "]$1["
+%define api.namespace "]$1["
%union { int i; }
%define global_tokens_and_yystype
+%locations
%code {
// YYSTYPE contains a namespace reference.
- int yylex (YYSTYPE *lval) {
+ int yylex (YYSTYPE *lval, const ]$1[::parser::location_type*) {
lval->i = 3;
return 0;
}
}
int
-main (void)
+main ()
{
]$1[::parser p;
return p.parse ();
# contains single occurrences of `:'.
AT_CHECK_NAMESPACE([[foo[3]::bar::baz]], [[-]])
AT_CHECK_NAMESPACE([[foo::bar,baz]], [[-]])
-AT_CHECK_NAMESPACE([[foo::bar::(baz]], [[-]])
+AT_CHECK_NAMESPACE([[foo::bar::(baz /* Pacify Emacs ) */]], [[-]])
+AT_CLEANUP
+
+
+## -------------------------------------- ##
+## Syntax error discarding no lookahead. ##
+## -------------------------------------- ##
+
+# After a syntax error, lalr1.cc used to not check whether there
+# actually is a lookahead before discarding the lookahead. As a result,
+# it mistakenly invoked the destructor for the previous lookahead.
+
+AT_SETUP([[Syntax error discarding no lookahead]])
+
+AT_DATA_GRAMMAR([[input.yy]],
+[[%skeleton "lalr1.cc"
+
+%code {
+ #include <string>
+ int yylex (yy::parser::semantic_type *);
+ #define USE(Args)
+}
+
+%defines
+%define parse.error verbose
+
+%nonassoc 'a' ;
+
+%destructor {
+ std::cerr << "Discarding 'a'." << std::endl;
+} 'a'
+
+%%
+
+start: error-reduce consistent-error 'a' { USE ($3); };
+
+error-reduce:
+ 'a' 'a' consistent-error 'a' { USE (($1, $2, $4)); }
+| 'a' error { std::cerr << "Reducing 'a'." << std::endl; USE ($1); }
+;
+
+consistent-error:
+ 'a'
+| /*empty*/ %prec 'a'
+;
+
+// Provide another context in which all rules are useful so that this
+// test case looks a little more realistic.
+start: 'b' consistent-error ;
+
+%%
+
+int
+yylex (yy::parser::semantic_type *)
+{
+ static char const *input = "aa";
+ return *input++;
+}
+
+void
+yy::parser::error (const std::string &m)
+{
+ std::cerr << m << std::endl;
+}
+
+int
+main ()
+{
+ yy::parser parser;
+ return parser.parse ();
+}
+]])
+AT_BISON_CHECK([[-o input.cc input.yy]])
+AT_COMPILE_CXX([[input]])
+# This used to print "Discarding 'a'." again at the end.
+AT_PARSER_CHECK([[./input]], [[1]], [[]],
+[[syntax error
+Discarding 'a'.
+Reducing 'a'.
+]])
+
+AT_CLEANUP
+
+
+## --------------------------- ##
+## Syntax error as exception. ##
+## --------------------------- ##
+
+AT_SETUP([[Syntax error as exception]])
+
+AT_DATA_GRAMMAR([[input.yy]],
+[[%skeleton "lalr1.cc"
+
+%code
+{
+ #include <cstdlib>
+ int yylex (yy::parser::semantic_type *);
+}
+
+%defines
+%define api.value.type variant
+%define parse.error verbose
+%define parse.trace
+%%
+
+start:
+ thing
+| start thing
+;
+
+thing:
+ error { std::cerr << "caught error" << std::endl; }
+| item
+;
+
+item:
+ 'a'
+| 's'
+ {
+ throw yy::parser::syntax_error ("invalid expression");
+ }
+
+%%
+
+int
+yylex (yy::parser::semantic_type *)
+{
+ // 's': syntax error, 'l': lexical error.
+ static char const *input = "asal";
+ switch (int res = *input++)
+ {
+ case 'l':
+ throw yy::parser::syntax_error ("invalid character");
+ default:
+ return res;
+ }
+}
+
+void
+yy::parser::error (const std::string &m)
+{
+ std::cerr << "error: " << m << std::endl;
+}
+
+int
+main ()
+{
+ yy::parser parser;
+ parser.set_debug_level (!!getenv ("YYDEBUG"));
+ return parser.parse ();
+}
+]])
+AT_BISON_CHECK([[-o input.cc input.yy]])
+AT_COMPILE_CXX([[input]])
+
+AT_PARSER_CHECK([[./input]], [[0]], [[]],
+[[error: invalid expression
+caught error
+error: invalid character
+caught error
+]])
+
+AT_CLEANUP
+
+
+## ------------------ ##
+## Exception safety. ##
+## ------------------ ##
+
+AT_SETUP([[Exception safety]])
+
+AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
+
+AT_DATA_GRAMMAR([[input.yy]],
+[[%skeleton "lalr1.cc"
+%defines // FIXME: Mandated in 2.6.
+%debug
+%error-verbose
+
+%code requires
+{
+ #include <cassert>
+ #include <cstdlib> // size_t and getenv.
+ #include <iostream>
+ #include <list>
+
+ bool debug = false;
+
+ /// A class that counts its number of instances.
+ struct Object
+ {
+ typedef std::list<const Object*> objects;
+ static objects instances;
+ char val;
+
+ static bool
+ empty ()
+ {
+ return instances.empty();
+ }
+
+ static void
+ log (Object const *o, const std::string& msg)
+ {
+ if (debug)
+ {
+ if (o)
+ std::cerr << o << "->";
+ std::cerr << msg << " {";
+ const char* sep = " ";
+ for (objects::const_iterator i = instances.begin(),
+ i_end = instances.end();
+ i != i_end;
+ ++i)
+ {
+ std::cerr << sep << *i;
+ sep = ", ";
+ }
+ std::cerr << " }" << std::endl;
+ }
+ }
+
+ Object (char v)
+ : val (v)
+ {
+ instances.push_back(this);
+ log (this, "Object::Object");
+ }
+
+ ~Object ()
+ {
+ instances.remove(this);
+ log (this, "Object::~Object");
+ }
+ };
+}
+
+%code
+{
+ #include <cassert>
+ #include <cstring> // strchr
+ #include <stdexcept>
+ int yylex (yy::parser::semantic_type *);
+ Object::objects Object::instances;
+ static char const *input;
+}
+
+%union
+{
+ Object *obj;
+}
+
+%initial-action
+{
+ if (strchr (input, 'i'))
+ throw std::runtime_error ("initial-action");
+}
+
+%destructor { delete $$; } <obj>;
+%printer
+{
+ yyo << $$ << " '" << $$->val << '\'';
+ if ($$->val == 'p')
+ throw std::runtime_error ("printer");
+} <obj>;
+
+%token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T'
+%type <obj> list item
+
+%%
+
+start: list { delete $1; };
+
+list:
+ item { $$ = $1; }
+| item list { $$ = $1; delete $2; } // Right recursion to load the stack.
+;
+
+item:
+ 'a' { $$ = $1; }
+| 'e' { YYUSE ($$); YYUSE($1); error ("syntax error"); }
+// Not just 'E', otherwise we reduce when 'E' is the lookahead, and
+// then the stack is emptied, defeating the point of the test.
+| 'E' 'a' { YYUSE($1); $$ = $2; }
+| 'R' { $$ = YY_NULL; delete $1; YYERROR; }
+| 'p' { $$ = $1; }
+| 's' { $$ = $1; throw std::runtime_error ("reduction"); }
+| 'T' { $$ = YY_NULL; delete $1; YYABORT; }
+| error { $$ = YY_NULL; yyerrok; }
+;
+%%
+
+int
+yylex (yy::parser::semantic_type *lvalp)
+{
+ // 'a': no error.
+ // 'e': user action calls error.
+ // 'E': syntax error, with yyerror that throws.
+ // 'i': initial action throws.
+ // 'l': yylex throws.
+ // 'R': call YYERROR in the action
+ // 's': reduction throws.
+ // 'T': call YYABORT in the action
+ switch (int res = *input++)
+ {
+ case 'l':
+ throw std::runtime_error ("yylex");
+ default:
+ lvalp->obj = new Object (res);
+ // Fall through.
+ case 0:
+ return res;
+ }
+}
+
+/* A C++ error reporting function. */
+void
+yy::parser::error (const std::string& m)
+{
+ throw std::runtime_error (m);
+}
+
+int
+main (int argc, const char *argv[])
+{
+ switch (argc)
+ {
+ case 2:
+ input = argv[1];
+ break;
+ case 3:
+ assert (std::string(argv[1]) == "--debug");
+ debug = 1;
+ input = argv[2];
+ break;
+ default:
+ abort ();
+ }
+
+ yy::parser parser;
+ debug |= !!getenv ("YYDEBUG");
+ parser.set_debug_level (debug);
+ int res = 2;
+ try
+ {
+ res = parser.parse ();
+ }
+ catch (const std::exception& e)
+ {
+ std::cerr << "exception caught: " << e.what () << std::endl;
+ }
+ catch (...)
+ {
+ std::cerr << "unknown exception caught" << std::endl;
+ }
+ Object::log (YY_NULL, "end");
+ assert (Object::empty());
+ return res;
+}
+]])
+AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
+AT_COMPILE_CXX([[input]])
+
+AT_PARSER_CHECK([[./input aaaas]], [[2]], [[]],
+[[exception caught: reduction
+]])
+
+AT_PARSER_CHECK([[./input aaaal]], [[2]], [[]],
+[[exception caught: yylex
+]])
+
+AT_PARSER_CHECK([[./input i]], [[2]], [[]],
+[[exception caught: initial-action
+]])
+
+AT_PARSER_CHECK([[./input aaaap]])
+
+AT_PARSER_CHECK([[./input --debug aaaap]], [[2]], [[]], [[stderr]])
+AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
+
+AT_PARSER_CHECK([[./input aaaae]], [[2]], [[]],
+[[exception caught: syntax error
+]])
+
+AT_PARSER_CHECK([[./input aaaaE]], [[2]], [[]],
+[[exception caught: syntax error, unexpected $end, expecting 'a'
+]])
+
+AT_PARSER_CHECK([[./input aaaaT]], [[1]])
+
+# There is error-recovery, so exit success.
+AT_PARSER_CHECK([[./input aaaaR]], [[0]])
+
+AT_BISON_OPTION_POPDEFS
+
+AT_CLEANUP
+
+## ------------------------------------ ##
+## C++ GLR parser identifier shadowing ##
+## ------------------------------------ ##
+
+AT_SETUP([[C++ GLR parser identifier shadowing]])
+
+AT_DATA_GRAMMAR([input.yy], [
+%skeleton "glr.cc"
+
+%union
+{
+ int ival;
+}
+
+%token <ival> ZERO;
+
+%code
+{
+ int yylex (yy::parser::semantic_type *yylval);
+}
+
+%%
+exp: ZERO
+
+%%
+
+int yylex (yy::parser::semantic_type *yylval)
+{
+ return yy::parser::token::ZERO;
+}
+
+void yy::parser::error (std::string const& msg)
+{}
+
+int main()
+{}
+])
+
+AT_BISON_CHECK([[-o input.cc input.yy]])
+AT_COMPILE_CXX([[input]])
+
AT_CLEANUP