| # Checking the C++ Features. -*- Autotest -*- |
| |
| # Copyright (C) 2004-2005, 2007, 2009-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 |
| # the Free Software Foundation, either version 3 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| AT_BANNER([[C++ Features.]]) |
| |
| |
| ## ----------------------- ## |
| ## Doxygen Documentation. ## |
| ## ----------------------- ## |
| |
| m4_define([AT_CHECK_DOXYGEN], |
| [m4_case([$1], |
| [Public], [m4_pushdef([AT_DOXYGEN_PRIVATE], [NO])], |
| [Private], [m4_pushdef([AT_DOXYGEN_PRIVATE], [YES])], |
| [m4_fatal([invalid argument: $1])]) |
| AT_SETUP([Doxygen $1 Documentation]) |
| |
| AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"]) |
| AT_DATA([input.yy], |
| [[%skeleton "lalr1.cc" |
| %locations |
| %debug |
| %defines |
| %% |
| exp:; |
| %% |
| ]AT_YYERROR_DEFINE[ |
| ]]) |
| |
| AT_BISON_CHECK([-o input.cc input.yy], 0) |
| |
| AT_DATA([Doxyfile], |
| [# The PROJECT_NAME tag is a single word (or a sequence of words |
| # surrounded by quotes) that should identify the project. |
| PROJECT_NAME = "Bison C++ Parser" |
| |
| # The QUIET tag can be used to turn on/off the messages that are |
| # generated by doxygen. Possible values are YES and NO. If left blank |
| # NO is used. |
| QUIET = YES |
| |
| # The WARNINGS tag can be used to turn on/off the warning messages |
| # that are generated by doxygen. Possible values are YES and NO. If |
| # left blank NO is used. |
| WARNINGS = YES |
| # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate |
| # warnings for undocumented members. If EXTRACT_ALL is set to YES then |
| # this flag will automatically be disabled. |
| WARN_IF_UNDOCUMENTED = YES |
| # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings |
| # for potential errors in the documentation, such as not documenting |
| # some parameters in a documented function, or documenting parameters |
| # that don't exist or using markup commands wrongly. |
| WARN_IF_DOC_ERROR = YES |
| # The WARN_FORMAT tag determines the format of the warning messages |
| # that doxygen can produce. The string should contain the $file, |
| # $line, and $text tags, which will be replaced by the file and line |
| # number from which the warning originated and the warning text. |
| WARN_FORMAT = "$file:$line: $text" |
| |
| # If the EXTRACT_ALL tag is set to YES doxygen will assume all |
| # entities in documentation are documented, even if no documentation |
| # was available. Private class members and static file members will |
| # be hidden unless the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set |
| # to YES |
| EXTRACT_ALL = YES |
| |
| # If the EXTRACT_PRIVATE tag is set to YES all private members of a |
| # class will be included in the documentation. |
| EXTRACT_PRIVATE = AT_DOXYGEN_PRIVATE |
| |
| # If the EXTRACT_STATIC tag is set to YES all static members of a file |
| # will be included in the documentation. |
| EXTRACT_STATIC = AT_DOXYGEN_PRIVATE |
| ]) |
| |
| AT_CHECK([doxygen --version || exit 77], 0, ignore) |
| AT_CHECK([doxygen], 0, [], [ignore]) |
| |
| AT_BISON_OPTION_POPDEFS |
| AT_CLEANUP |
| |
| m4_popdef([AT_DOXYGEN_PRIVATE]) |
| ])# AT_CHECK_DOXYGEN |
| |
| AT_CHECK_DOXYGEN([Public]) |
| AT_CHECK_DOXYGEN([Private]) |
| |
| ## ------------ ## |
| ## Namespaces. ## |
| ## ------------ ## |
| |
| # AT_CHECK_NAMESPACE(NAMESPACE-DECL, [COMPILE-ERROR]) |
| # --------------------------------------------------- |
| # See if Bison can handle %define namespace "NAMESPACE-DECL". If COMPILE-ERROR |
| # is specified, then Bison should accept the input, but compilation will fail, |
| # so don't check compilation. |
| m4_define([AT_CHECK_NAMESPACE], |
| [ |
| |
| AT_DATA_GRAMMAR([[input.y]], |
| [[%language "C++" |
| %defines |
| %define namespace "]$1[" |
| %union { int i; } |
| %define global_tokens_and_yystype |
| |
| %code { |
| // YYSTYPE contains a namespace reference. |
| int yylex (YYSTYPE *lval) { |
| lval->i = 3; |
| return 0; |
| } |
| } |
| |
| %% |
| |
| start: ; |
| |
| %% |
| |
| void |
| ]$1[::parser::error (const ]$1[::parser::location_type &loc, |
| const std::string &msg) |
| { |
| std::cerr << "At " << loc << ": " << msg << std::endl; |
| } |
| |
| int |
| main (void) |
| { |
| ]$1[::parser p; |
| return p.parse (); |
| } |
| ]]) |
| |
| AT_BISON_CHECK([[-o input.cc input.y]]) |
| |
| m4_if([$#], [1], |
| [AT_COMPILE_CXX([[input]], [[input.cc]]) |
| AT_PARSER_CHECK([[./input]])]) |
| |
| ]) |
| |
| AT_SETUP([[Relative namespace references]]) |
| AT_CHECK_NAMESPACE([[foo]]) |
| AT_CHECK_NAMESPACE([[foo::bar]]) |
| AT_CHECK_NAMESPACE([[foo::bar::baz]]) |
| AT_CLEANUP |
| |
| AT_SETUP([[Absolute namespace references]]) |
| AT_CHECK_NAMESPACE([[::foo]]) |
| AT_CHECK_NAMESPACE([[::foo::bar]]) |
| AT_CHECK_NAMESPACE([[::foo::bar::baz]]) |
| AT_CHECK_NAMESPACE([[ ::foo]]) |
| AT_CHECK_NAMESPACE([[ ::foo::bar]]) |
| AT_CHECK_NAMESPACE([[ ::foo::bar::baz]]) |
| AT_CLEANUP |
| |
| AT_SETUP([[Syntactically invalid namespace references]]) |
| AT_CHECK_NAMESPACE([[:foo:bar]], [[-]]) |
| AT_CHECK_NAMESPACE([[foo: :bar]], [[-]]) |
| # This one is interesting because `[3]' is encoded as `@<:@3@:>@', which |
| # contains single occurrences of `:'. |
| AT_CHECK_NAMESPACE([[foo[3]::bar::baz]], [[-]]) |
| AT_CHECK_NAMESPACE([[foo::bar,baz]], [[-]]) |
| AT_CHECK_NAMESPACE([[foo::bar::(baz]], [[-]]) |
| 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 (location_type(), "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 location_type& l, const std::string& m) |
| { |
| YYUSE (l); |
| throw std::runtime_error (m); |
| } |
| |
| int |
| main (int argc, const char *argv[]) |
| { |
| switch (argc) |
| { |
| case 2: |
| input = argv[1]; |
| break; |
| case 3: |
| assert (!strcmp (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 |