| # Checking the output filenames. -*- Autotest -*- |
| |
| # 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 |
| # 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.]]) |
| |
| |
| ## ---------- ## |
| ## Variants. ## |
| ## ---------- ## |
| |
| # AT_CHECK_VARIANTS([DIRECTIVES]) |
| # ------------------------------- |
| # Check the support of variants in C++, with the additional DIRECTIVES. |
| m4_define([AT_CHECK_VARIANTS], |
| [AT_SETUP([Variants $1]) |
| |
| # Store strings and integers in a list of strings. |
| AT_DATA_GRAMMAR([list.yy], |
| [[%debug |
| %skeleton "lalr1.cc" |
| %defines |
| %define variant |
| %locations |
| ]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 <iostream> |
| #include <sstream> |
| |
| static |
| #if defined USE_LEX_SYMBOL |
| yy::parser::symbol_type yylex (); |
| #else |
| yy::parser::token_type yylex (yy::parser::semantic_type* yylval, |
| yy::parser::location_type* yylloc); |
| #endif |
| |
| // 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 { debug_stream() << $][$; } |
| <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); } |
| ; |
| %% |
| |
| #define STAGE_MAX 5 |
| static |
| #if defined USE_LEX_SYMBOL |
| yy::parser::symbol_type yylex() |
| #else |
| yy::parser::token_type yylex(yy::parser::semantic_type* yylval, |
| yy::parser::location_type* yylloc) |
| #endif |
| { |
| #ifndef USE_LEX_SYMBOL |
| typedef yy::parser::token token; |
| #endif |
| typedef yy::parser::location_type location_type; |
| static int stage = -1; |
| ++stage; |
| if (stage == STAGE_MAX) |
| { |
| #if defined USE_LEX_SYMBOL |
| return yy::parser::make_END_OF_FILE (location_type ()); |
| #else |
| *yylloc = location_type (); |
| return token::END_OF_FILE; |
| #endif |
| } |
| else if (stage % 2) |
| { |
| #if defined USE_LEX_SYMBOL |
| return yy::parser::make_NUMBER (stage, location_type ()); |
| #else |
| # if defined ONE_STAGE_BUILD |
| yylval->build(stage); |
| # else |
| yylval->build<int>() = stage; |
| # endif |
| *yylloc = location_type (); |
| return token::NUMBER; |
| #endif |
| } |
| else |
| { |
| #if defined USE_LEX_SYMBOL |
| return yy::parser::make_TEXT (string_cast (stage), location_type ()); |
| #else |
| # if defined ONE_STAGE_BUILD |
| yylval->build (string_cast (stage)); |
| # else |
| yylval->build<std::string>() = string_cast (stage); |
| # endif |
| *yylloc = location_type (); |
| return token::TEXT; |
| #endif |
| } |
| abort(); |
| } |
| |
| void |
| yy::parser::error(const yy::parser::location_type&, |
| const std::string& message) |
| { |
| std::cerr << message << std::endl; |
| } |
| |
| 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]) |
| AT_CHECK([./list], 0, |
| [(0, 1, 2, 4) |
| ]) |
| |
| AT_CLEANUP |
| ]) |
| |
| AT_CHECK_VARIANTS([]) |
| AT_CHECK_VARIANTS([%define parse.assert]) |
| AT_CHECK_VARIANTS([[%define parse.assert %code {\n#define ONE_STAGE_BUILD\n}]]) |
| AT_CHECK_VARIANTS([[%define parse.assert %define lex_symbol %code {\n#define USE_LEX_SYMBOL\n}]]) |
| AT_CHECK_VARIANTS([[%define parse.assert %define lex_symbol %code {\n#define USE_LEX_SYMBOL\n} %define api.tokens.prefix "TOK_"]]) |
| |
| |
| ## ----------------------- ## |
| ## 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_DATA([input.yy], |
| [[%skeleton "lalr1.cc" |
| %locations |
| %debug |
| %defines |
| %% |
| exp:; |
| %% |
| yy::parser::error (const location& l, const std::string& m) |
| { |
| std::cerr << l << s << std::endl; |
| } |
| ]]) |
| |
| 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_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 api.namespace "]$1[" |
| %union { int i; } |
| %define global_tokens_and_yystype |
| %locations |
| |
| %code { |
| // YYSTYPE contains a namespace reference. |
| int yylex (YYSTYPE *lval, const ]$1[::parser::location_type*) { |
| 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 () |
| { |
| ]$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 /* 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 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 |