1# Checking the C++ Features. -*- Autotest -*- 2 3# Copyright (C) 2004-2005, 2007, 2009-2012 Free Software Foundation, 4# Inc. 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 <http://www.gnu.org/licenses/>. 18 19AT_BANNER([[C++ Features.]]) 20 21 22## ----------------------- ## 23## Doxygen Documentation. ## 24## ----------------------- ## 25 26m4_define([AT_CHECK_DOXYGEN], 27[m4_case([$1], 28 [Public], [m4_pushdef([AT_DOXYGEN_PRIVATE], [NO])], 29 [Private], [m4_pushdef([AT_DOXYGEN_PRIVATE], [YES])], 30 [m4_fatal([invalid argument: $1])]) 31AT_SETUP([Doxygen $1 Documentation]) 32 33AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"]) 34AT_DATA([input.yy], 35[[%skeleton "lalr1.cc" 36%locations 37%debug 38%defines 39%% 40exp:; 41%% 42]AT_YYERROR_DEFINE[ 43]]) 44 45AT_BISON_CHECK([-o input.cc input.yy], 0) 46 47AT_DATA([Doxyfile], 48[# The PROJECT_NAME tag is a single word (or a sequence of words 49# surrounded by quotes) that should identify the project. 50PROJECT_NAME = "Bison C++ Parser" 51 52# The QUIET tag can be used to turn on/off the messages that are 53# generated by doxygen. Possible values are YES and NO. If left blank 54# NO is used. 55QUIET = YES 56 57# The WARNINGS tag can be used to turn on/off the warning messages 58# that are generated by doxygen. Possible values are YES and NO. If 59# left blank NO is used. 60WARNINGS = YES 61# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate 62# warnings for undocumented members. If EXTRACT_ALL is set to YES then 63# this flag will automatically be disabled. 64WARN_IF_UNDOCUMENTED = YES 65# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings 66# for potential errors in the documentation, such as not documenting 67# some parameters in a documented function, or documenting parameters 68# that don't exist or using markup commands wrongly. 69WARN_IF_DOC_ERROR = YES 70# The WARN_FORMAT tag determines the format of the warning messages 71# that doxygen can produce. The string should contain the $file, 72# $line, and $text tags, which will be replaced by the file and line 73# number from which the warning originated and the warning text. 74WARN_FORMAT = "$file:$line: $text" 75 76# If the EXTRACT_ALL tag is set to YES doxygen will assume all 77# entities in documentation are documented, even if no documentation 78# was available. Private class members and static file members will 79# be hidden unless the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set 80# to YES 81EXTRACT_ALL = YES 82 83# If the EXTRACT_PRIVATE tag is set to YES all private members of a 84# class will be included in the documentation. 85EXTRACT_PRIVATE = AT_DOXYGEN_PRIVATE 86 87# If the EXTRACT_STATIC tag is set to YES all static members of a file 88# will be included in the documentation. 89EXTRACT_STATIC = AT_DOXYGEN_PRIVATE 90]) 91 92AT_CHECK([doxygen --version || exit 77], 0, ignore) 93AT_CHECK([doxygen], 0, [], [ignore]) 94 95AT_BISON_OPTION_POPDEFS 96AT_CLEANUP 97 98m4_popdef([AT_DOXYGEN_PRIVATE]) 99])# AT_CHECK_DOXYGEN 100 101AT_CHECK_DOXYGEN([Public]) 102AT_CHECK_DOXYGEN([Private]) 103 104## ------------ ## 105## Namespaces. ## 106## ------------ ## 107 108# AT_CHECK_NAMESPACE(NAMESPACE-DECL, [COMPILE-ERROR]) 109# --------------------------------------------------- 110# See if Bison can handle %define namespace "NAMESPACE-DECL". If COMPILE-ERROR 111# is specified, then Bison should accept the input, but compilation will fail, 112# so don't check compilation. 113m4_define([AT_CHECK_NAMESPACE], 114[ 115 116AT_DATA_GRAMMAR([[input.y]], 117[[%language "C++" 118%defines 119%define namespace "]$1[" 120%union { int i; } 121%define global_tokens_and_yystype 122 123%code { 124 // YYSTYPE contains a namespace reference. 125 int yylex (YYSTYPE *lval) { 126 lval->i = 3; 127 return 0; 128 } 129} 130 131%% 132 133start: ; 134 135%% 136 137void 138]$1[::parser::error (const ]$1[::parser::location_type &loc, 139 const std::string &msg) 140{ 141 std::cerr << "At " << loc << ": " << msg << std::endl; 142} 143 144int 145main (void) 146{ 147 ]$1[::parser p; 148 return p.parse (); 149} 150]]) 151 152AT_BISON_CHECK([[-o input.cc input.y]]) 153 154m4_if([$#], [1], 155[AT_COMPILE_CXX([[input]], [[input.cc]]) 156AT_PARSER_CHECK([[./input]])]) 157 158]) 159 160AT_SETUP([[Relative namespace references]]) 161AT_CHECK_NAMESPACE([[foo]]) 162AT_CHECK_NAMESPACE([[foo::bar]]) 163AT_CHECK_NAMESPACE([[foo::bar::baz]]) 164AT_CLEANUP 165 166AT_SETUP([[Absolute namespace references]]) 167AT_CHECK_NAMESPACE([[::foo]]) 168AT_CHECK_NAMESPACE([[::foo::bar]]) 169AT_CHECK_NAMESPACE([[::foo::bar::baz]]) 170AT_CHECK_NAMESPACE([[ ::foo]]) 171AT_CHECK_NAMESPACE([[ ::foo::bar]]) 172AT_CHECK_NAMESPACE([[ ::foo::bar::baz]]) 173AT_CLEANUP 174 175AT_SETUP([[Syntactically invalid namespace references]]) 176AT_CHECK_NAMESPACE([[:foo:bar]], [[-]]) 177AT_CHECK_NAMESPACE([[foo: :bar]], [[-]]) 178# This one is interesting because `[3]' is encoded as `@<:@3@:>@', which 179# contains single occurrences of `:'. 180AT_CHECK_NAMESPACE([[foo[3]::bar::baz]], [[-]]) 181AT_CHECK_NAMESPACE([[foo::bar,baz]], [[-]]) 182AT_CHECK_NAMESPACE([[foo::bar::(baz]], [[-]]) 183AT_CLEANUP 184 185 186## ------------------ ## 187## Exception safety. ## 188## ------------------ ## 189 190AT_SETUP([[Exception safety]]) 191 192AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"]) 193 194AT_DATA_GRAMMAR([[input.yy]], 195[[%skeleton "lalr1.cc" 196%defines // FIXME: Mandated in 2.6. 197%debug 198%error-verbose 199 200%code requires 201{ 202 #include <cassert> 203 #include <cstdlib> // size_t and getenv. 204 #include <iostream> 205 #include <list> 206 207 bool debug = false; 208 209 /// A class that counts its number of instances. 210 struct Object 211 { 212 typedef std::list<const Object*> objects; 213 static objects instances; 214 char val; 215 216 static bool 217 empty () 218 { 219 return instances.empty(); 220 } 221 222 static void 223 log (Object const *o, const std::string& msg) 224 { 225 if (debug) 226 { 227 if (o) 228 std::cerr << o << "->"; 229 std::cerr << msg << " {"; 230 const char* sep = " "; 231 for (objects::const_iterator i = instances.begin(), 232 i_end = instances.end(); 233 i != i_end; 234 ++i) 235 { 236 std::cerr << sep << *i; 237 sep = ", "; 238 } 239 std::cerr << " }" << std::endl; 240 } 241 } 242 243 Object (char v) 244 : val (v) 245 { 246 instances.push_back(this); 247 log (this, "Object::Object"); 248 } 249 250 ~Object () 251 { 252 instances.remove(this); 253 log (this, "Object::~Object"); 254 } 255 }; 256} 257 258%code 259{ 260 #include <cassert> 261 #include <cstring> // strchr 262 #include <stdexcept> 263 int yylex (yy::parser::semantic_type *); 264 Object::objects Object::instances; 265 static char const *input; 266} 267 268%union 269{ 270 Object *obj; 271} 272 273%initial-action 274{ 275 if (strchr (input, 'i')) 276 throw std::runtime_error ("initial-action"); 277} 278 279%destructor { delete $$; } <obj>; 280%printer 281{ 282 yyo << $$ << " '" << $$->val << '\''; 283 if ($$->val == 'p') 284 throw std::runtime_error ("printer"); 285} <obj>; 286 287%token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T' 288%type <obj> list item 289 290%% 291 292start: list { delete $1; }; 293 294list: 295 item { $$ = $1; } 296| item list { $$ = $1; delete $2; } // Right recursion to load the stack. 297; 298 299item: 300 'a' { $$ = $1; } 301| 'e' { YYUSE ($$); YYUSE($1); error (location_type(), "syntax error"); } 302// Not just 'E', otherwise we reduce when 'E' is the lookahead, and 303// then the stack is emptied, defeating the point of the test. 304| 'E' 'a' { YYUSE($1); $$ = $2; } 305| 'R' { $$ = YY_NULL; delete $1; YYERROR; } 306| 'p' { $$ = $1; } 307| 's' { $$ = $1; throw std::runtime_error ("reduction"); } 308| 'T' { $$ = YY_NULL; delete $1; YYABORT; } 309| error { $$ = YY_NULL; yyerrok; } 310; 311%% 312 313int 314yylex (yy::parser::semantic_type *lvalp) 315{ 316 // 'a': no error. 317 // 'e': user action calls error. 318 // 'E': syntax error, with yyerror that throws. 319 // 'i': initial action throws. 320 // 'l': yylex throws. 321 // 'R': call YYERROR in the action 322 // 's': reduction throws. 323 // 'T': call YYABORT in the action 324 switch (int res = *input++) 325 { 326 case 'l': 327 throw std::runtime_error ("yylex"); 328 default: 329 lvalp->obj = new Object (res); 330 // Fall through. 331 case 0: 332 return res; 333 } 334} 335 336/* A C++ error reporting function. */ 337void 338yy::parser::error (const location_type& l, const std::string& m) 339{ 340 YYUSE (l); 341 throw std::runtime_error (m); 342} 343 344int 345main (int argc, const char *argv[]) 346{ 347 switch (argc) 348 { 349 case 2: 350 input = argv[1]; 351 break; 352 case 3: 353 assert (!strcmp (argv[1], "--debug")); 354 debug = 1; 355 input = argv[2]; 356 break; 357 default: 358 abort (); 359 } 360 361 yy::parser parser; 362 debug |= !!getenv ("YYDEBUG"); 363 parser.set_debug_level (debug); 364 int res = 2; 365 try 366 { 367 res = parser.parse (); 368 } 369 catch (const std::exception& e) 370 { 371 std::cerr << "exception caught: " << e.what () << std::endl; 372 } 373 catch (...) 374 { 375 std::cerr << "unknown exception caught" << std::endl; 376 } 377 Object::log (YY_NULL, "end"); 378 assert (Object::empty()); 379 return res; 380} 381]]) 382AT_BISON_CHECK([[-o input.cc --report=all input.yy]]) 383AT_COMPILE_CXX([[input]]) 384 385AT_PARSER_CHECK([[./input aaaas]], [[2]], [[]], 386[[exception caught: reduction 387]]) 388 389AT_PARSER_CHECK([[./input aaaal]], [[2]], [[]], 390[[exception caught: yylex 391]]) 392 393AT_PARSER_CHECK([[./input i]], [[2]], [[]], 394[[exception caught: initial-action 395]]) 396 397AT_PARSER_CHECK([[./input aaaap]]) 398 399AT_PARSER_CHECK([[./input --debug aaaap]], [[2]], [[]], [[stderr]]) 400AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore]) 401 402AT_PARSER_CHECK([[./input aaaae]], [[2]], [[]], 403[[exception caught: syntax error 404]]) 405 406AT_PARSER_CHECK([[./input aaaaE]], [[2]], [[]], 407[[exception caught: syntax error, unexpected $end, expecting 'a' 408]]) 409 410AT_PARSER_CHECK([[./input aaaaT]], [[1]]) 411 412# There is error-recovery, so exit success. 413AT_PARSER_CHECK([[./input aaaaR]], [[0]]) 414 415AT_BISON_OPTION_POPDEFS 416 417AT_CLEANUP 418