Home | History | Annotate | Download | only in tests
      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 
     19 AT_BANNER([[C++ Features.]])
     20 
     21 
     22 ## ----------------------- ##
     23 ## Doxygen Documentation.  ##
     24 ## ----------------------- ##
     25 
     26 m4_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])])
     31 AT_SETUP([Doxygen $1 Documentation])
     32 
     33 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
     34 AT_DATA([input.yy],
     35 [[%skeleton "lalr1.cc"
     36 %locations
     37 %debug
     38 %defines
     39 %%
     40 exp:;
     41 %%
     42 ]AT_YYERROR_DEFINE[
     43 ]])
     44 
     45 AT_BISON_CHECK([-o input.cc input.yy], 0)
     46 
     47 AT_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.
     50 PROJECT_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.
     55 QUIET = 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.
     60 WARNINGS     = 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.
     64 WARN_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.
     69 WARN_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.
     74 WARN_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
     81 EXTRACT_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.
     85 EXTRACT_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.
     89 EXTRACT_STATIC         = AT_DOXYGEN_PRIVATE
     90 ])
     91 
     92 AT_CHECK([doxygen --version || exit 77], 0, ignore)
     93 AT_CHECK([doxygen], 0, [], [ignore])
     94 
     95 AT_BISON_OPTION_POPDEFS
     96 AT_CLEANUP
     97 
     98 m4_popdef([AT_DOXYGEN_PRIVATE])
     99 ])# AT_CHECK_DOXYGEN
    100 
    101 AT_CHECK_DOXYGEN([Public])
    102 AT_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.
    113 m4_define([AT_CHECK_NAMESPACE],
    114 [
    115 
    116 AT_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 
    133 start: ;
    134 
    135 %%
    136 
    137 void
    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 
    144 int
    145 main (void)
    146 {
    147   ]$1[::parser p;
    148   return p.parse ();
    149 }
    150 ]])
    151 
    152 AT_BISON_CHECK([[-o input.cc input.y]])
    153 
    154 m4_if([$#], [1],
    155 [AT_COMPILE_CXX([[input]], [[input.cc]])
    156 AT_PARSER_CHECK([[./input]])])
    157 
    158 ])
    159 
    160 AT_SETUP([[Relative namespace references]])
    161 AT_CHECK_NAMESPACE([[foo]])
    162 AT_CHECK_NAMESPACE([[foo::bar]])
    163 AT_CHECK_NAMESPACE([[foo::bar::baz]])
    164 AT_CLEANUP
    165 
    166 AT_SETUP([[Absolute namespace references]])
    167 AT_CHECK_NAMESPACE([[::foo]])
    168 AT_CHECK_NAMESPACE([[::foo::bar]])
    169 AT_CHECK_NAMESPACE([[::foo::bar::baz]])
    170 AT_CHECK_NAMESPACE([[	::foo]])
    171 AT_CHECK_NAMESPACE([[  	 ::foo::bar]])
    172 AT_CHECK_NAMESPACE([[  ::foo::bar::baz]])
    173 AT_CLEANUP
    174 
    175 AT_SETUP([[Syntactically invalid namespace references]])
    176 AT_CHECK_NAMESPACE([[:foo:bar]], [[-]])
    177 AT_CHECK_NAMESPACE([[foo: :bar]], [[-]])
    178 # This one is interesting because `[3]' is encoded as `@<:@3@:>@', which
    179 # contains single occurrences of `:'.
    180 AT_CHECK_NAMESPACE([[foo[3]::bar::baz]], [[-]])
    181 AT_CHECK_NAMESPACE([[foo::bar,baz]], [[-]])
    182 AT_CHECK_NAMESPACE([[foo::bar::(baz]], [[-]])
    183 AT_CLEANUP
    184 
    185 
    186 ## ------------------ ##
    187 ## Exception safety.  ##
    188 ## ------------------ ##
    189 
    190 AT_SETUP([[Exception safety]])
    191 
    192 AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
    193 
    194 AT_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 
    292 start: list { delete $1; };
    293 
    294 list:
    295   item       { $$ = $1; }
    296 | item list  { $$ = $1; delete $2; } // Right recursion to load the stack.
    297 ;
    298 
    299 item:
    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 
    313 int
    314 yylex (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.  */
    337 void
    338 yy::parser::error (const location_type& l, const std::string& m)
    339 {
    340   YYUSE (l);
    341   throw std::runtime_error (m);
    342 }
    343 
    344 int
    345 main (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 ]])
    382 AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
    383 AT_COMPILE_CXX([[input]])
    384 
    385 AT_PARSER_CHECK([[./input aaaas]], [[2]], [[]],
    386 [[exception caught: reduction
    387 ]])
    388 
    389 AT_PARSER_CHECK([[./input aaaal]], [[2]], [[]],
    390 [[exception caught: yylex
    391 ]])
    392 
    393 AT_PARSER_CHECK([[./input i]], [[2]], [[]],
    394 [[exception caught: initial-action
    395 ]])
    396 
    397 AT_PARSER_CHECK([[./input aaaap]])
    398 
    399 AT_PARSER_CHECK([[./input --debug aaaap]], [[2]], [[]], [[stderr]])
    400 AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
    401 
    402 AT_PARSER_CHECK([[./input aaaae]], [[2]], [[]],
    403 [[exception caught: syntax error
    404 ]])
    405 
    406 AT_PARSER_CHECK([[./input aaaaE]], [[2]], [[]],
    407 [[exception caught: syntax error, unexpected $end, expecting 'a'
    408 ]])
    409 
    410 AT_PARSER_CHECK([[./input aaaaT]], [[1]])
    411 
    412 # There is error-recovery, so exit success.
    413 AT_PARSER_CHECK([[./input aaaaR]], [[0]])
    414 
    415 AT_BISON_OPTION_POPDEFS
    416 
    417 AT_CLEANUP
    418