From 793fbca50acec434a8e60412394ef392fd03f315 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Mon, 8 Oct 2007 10:09:07 +0000 Subject: [PATCH] Improve C++ namespace support. Discussed starting at . * data/c++.m4: (b4_namespace_ref, b4_namespace_open, b4_namespace_close): New macros that interpret the %define variable "namespace" so its value can contain "::" to indicate nested namespaces. * data/glr.cc (b4_namespace): Don't define, and replace all uses with the above macros. * data/lalr1.cc (b4_namespace): Likewise. * data/location.cc (b4_namespace): Likewise. * doc/bison.texinfo (Decl Summary): Move `%define push_pull' entry inside a new table in the general %define entry. Document `%define namespace' there as well. Point the %name-prefix entry to it since it explains it more completely in the case of C++. (C++ Bison Interface): Mention `%define namespace' instead of %name-prefix. (Table of Symbols): Remove the `%define push_pull' entry. The %define entry suffices. * tests/c++.at (Relative namespace references): New test case. (Absolute namespace references): New test case. (Syntactically invalid namespace references): New test case. * tests/input.at (C++ namespace reference errors): New test case. --- ChangeLog | 25 ++++++++++++ data/c++.m4 | 45 +++++++++++++++++++++ data/glr.cc | 33 +++++++--------- data/lalr1.cc | 24 +++++------- data/location.cc | 14 +++---- doc/bison.texinfo | 99 +++++++++++++++++++++++++++++++++++++---------- tests/c++.at | 83 ++++++++++++++++++++++++++++++++++++++- tests/input.at | 49 +++++++++++++++++++++++ 8 files changed, 309 insertions(+), 63 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9072e409..3afcb7d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2007-10-08 Joel E. Denny + + Improve C++ namespace support. Discussed starting at + . + * data/c++.m4: (b4_namespace_ref, b4_namespace_open, + b4_namespace_close): New macros that interpret the %define variable + "namespace" so its value can contain "::" to indicate nested + namespaces. + * data/glr.cc (b4_namespace): Don't define, and replace all uses with + the above macros. + * data/lalr1.cc (b4_namespace): Likewise. + * data/location.cc (b4_namespace): Likewise. + * doc/bison.texinfo (Decl Summary): Move `%define push_pull' entry + inside a new table in the general %define entry. Document `%define + namespace' there as well. Point the %name-prefix entry to it since it + explains it more completely in the case of C++. + (C++ Bison Interface): Mention `%define namespace' instead of + %name-prefix. + (Table of Symbols): Remove the `%define push_pull' entry. The %define + entry suffices. + * tests/c++.at (Relative namespace references): New test case. + (Absolute namespace references): New test case. + (Syntactically invalid namespace references): New test case. + * tests/input.at (C++ namespace reference errors): New test case. + 2007-10-08 Joel E. Denny Add syncline support and location accessor to internal %define diff --git a/data/c++.m4 b/data/c++.m4 index 81ad14f5..91a9cc34 100644 --- a/data/c++.m4 +++ b/data/c++.m4 @@ -35,6 +35,51 @@ b4_percent_define_default([[define_location_comparison]], [std::string], [[true]], [[false]])]) +## ----------- ## +## Namespace. ## +## ----------- ## + +m4_define([b4_namespace_ref], [b4_percent_define_get([[namespace]])]) + +# Don't permit an empty b4_namespace_ref. Any `::parser::foo' appended to it +# would compile as an absolute reference with `parser' in the global namespace. +# b4_namespace_open would open an anonymous namespace and thus establish +# internal linkage. This would compile. However, it's cryptic, and internal +# linkage for the parser would be specified in all translation units that +# include the header, which is always generated. If we ever need to permit +# internal linkage somehow, surely we can find a cleaner approach. +m4_if(m4_bregexp(b4_namespace_ref, [^[ ]*$]), [-1], [], +[b4_complain_at(b4_percent_define_get_loc([[namespace]]), + [[namespace reference is empty]])]) + +# Instead of assuming the C++ compiler will do it, Bison should reject any +# invalid b4_namepsace_ref that would be converted to a valid +# b4_namespace_open. The problem is that Bison doesn't always output +# b4_namespace_ref to uncommented code but should reserve the ability to do so +# in future releases without risking breaking any existing user grammars. +# Specifically, don't allow empty names as b4_namespace_open would just convert +# those into anonymous namespaces, and that might tempt some users. +m4_if(m4_bregexp(b4_namespace_ref, [::[ ]*::]), [-1], [], +[b4_complain_at(b4_percent_define_get_loc([[namespace]]), + [[namespace reference has consecutive "::"]])]) +m4_if(m4_bregexp(b4_namespace_ref, [::[ ]*$]), [-1], [], +[b4_complain_at(b4_percent_define_get_loc([[namespace]]), + [[namespace reference has a trailing "::"]])]) + +m4_define([b4_namespace_open], +[b4_user_code([b4_percent_define_get_syncline([[namespace]]) +[namespace ]m4_bpatsubst(m4_dquote(m4_bpatsubst(m4_dquote(b4_namespace_ref), + [^\(.\)[ ]*::], [\1])), + [::], [ { namespace ])[ {]])]) + +m4_define([b4_namespace_close], +[b4_user_code([b4_percent_define_get_syncline([[namespace]]) +m4_bpatsubst(m4_dquote(m4_bpatsubst(m4_dquote(b4_namespace_ref), + [^\(.\)[ ]*\(::\)?\([^][:]\|:[^][:]\)*], + [\1])), + [::\([^][:]\|:[^][:]\)*], [} ])[} // ]b4_namespace_ref])]) + + # b4_token_enums(LIST-OF-PAIRS-TOKEN-NAME-TOKEN-NUMBER) # ----------------------------------------------------- # Output the definition of the tokens as enums. diff --git a/data/glr.cc b/data/glr.cc index 12f629a4..ea04b28d 100644 --- a/data/glr.cc +++ b/data/glr.cc @@ -58,8 +58,6 @@ m4_include(b4_pkgdatadir/[location.cc]) m4_define([b4_parser_class_name], [b4_percent_define_get([[parser_class_name]])]) -m4_define([b4_namespace], - [b4_percent_define_get([[namespace]])]) # Save the parse parameters. m4_define([b4_parse_param_orig], m4_defn([b4_parse_param])) @@ -79,9 +77,9 @@ m4_define([b4_yy_symbol_print_generate], [static void], [[FILE *], []], [[int yytype], [yytype]], - [[const b4_namespace::b4_parser_class_name::semantic_type *yyvaluep], + [[const b4_namespace_ref::b4_parser_class_name::semantic_type *yyvaluep], [yyvaluep]], - [[const b4_namespace::b4_parser_class_name::location_type *yylocationp], + [[const b4_namespace_ref::b4_parser_class_name::location_type *yylocationp], [yylocationp]], b4_parse_param)[ { @@ -97,7 +95,7 @@ m4_append([b4_post_prologue], b4_c_ansi_function_decl([yyerror], [static void], - [[b4_namespace::b4_parser_class_name::location_type *yylocationp], [yylocationp]], + [[b4_namespace_ref::b4_parser_class_name::location_type *yylocationp], [yylocationp]], b4_parse_param, [[const char* msg], [msg]])]) @@ -111,7 +109,7 @@ m4_append([b4_epilogue], ]b4_c_ansi_function_def([yyerror], [static void], - [[b4_namespace::b4_parser_class_name::location_type *yylocationp], [yylocationp]], + [[b4_namespace_ref::b4_parser_class_name::location_type *yylocationp], [yylocationp]], b4_parse_param, [[const char* msg], [msg]])[ { @@ -120,8 +118,7 @@ m4_append([b4_epilogue], } -namespace ]b4_namespace[ -{ +]b4_namespace_open[ ]dnl In this section, the parse param are the original parse_params. m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_orig]))dnl [ /// Build a parser object. @@ -203,7 +200,7 @@ m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_orig]))dnl #endif ]m4_popdef([b4_parse_param])dnl -[} // namespace ]b4_namespace[ +b4_namespace_close[ ]]) @@ -211,10 +208,10 @@ m4_pushdef([b4_parse_param], m4_defn([b4_parse_param_orig]))dnl # Let glr.c believe that the user arguments include the parser itself. m4_ifset([b4_parse_param], [m4_pushdef([b4_parse_param], - m4_dquote([[[b4_namespace::b4_parser_class_name& yyparser], [[yyparser]]],] + m4_dquote([[[b4_namespace_ref::b4_parser_class_name& yyparser], [[yyparser]]],] m4_defn([b4_parse_param])))], [m4_pushdef([b4_parse_param], - [[[[b4_namespace::b4_parser_class_name& yyparser], [[yyparser]]]]]) + [[[[b4_namespace_ref::b4_parser_class_name& yyparser], [[yyparser]]]]]) ]) m4_include(b4_pkgdatadir/[glr.c]) m4_popdef([b4_parse_param]) @@ -237,11 +234,10 @@ b4_copyright([Skeleton interface for Bison GLR parsers in C++], /* Using locations. */ #define YYLSP_NEEDED ]b4_locations_flag[ -namespace ]b4_namespace[ -{ +]b4_namespace_open[ class position; class location; -} +]b4_namespace_close[ #include "location.hh" @@ -269,8 +265,7 @@ namespace ]b4_namespace[ while (/*CONSTCOND*/ 0) #endif -namespace ]b4_namespace[ -{ +]b4_namespace_open[ /// A Bison parser. class ]b4_parser_class_name[ { @@ -368,13 +363,13 @@ b4_percent_define_flag_if([[global_tokens_and_yystype]], [b4_token_defines(b4_tokens)]) [ #ifndef YYSTYPE -# define YYSTYPE ]b4_namespace[::]b4_parser_class_name[::semantic_type +# define YYSTYPE ]b4_namespace_ref[::]b4_parser_class_name[::semantic_type #endif #ifndef YYLTYPE -# define YYLTYPE ]b4_namespace[::]b4_parser_class_name[::location_type +# define YYLTYPE ]b4_namespace_ref[::]b4_parser_class_name[::location_type #endif -} +]b4_namespace_close[ ]b4_percent_code_get([[provides]])[]dnl diff --git a/data/lalr1.cc b/data/lalr1.cc index fff83822..7ecc525b 100644 --- a/data/lalr1.cc +++ b/data/lalr1.cc @@ -20,8 +20,6 @@ m4_include(b4_pkgdatadir/[c++.m4]) m4_define([b4_parser_class_name], [b4_percent_define_get([[parser_class_name]])]) -m4_define([b4_namespace], - [b4_percent_define_get([[namespace]])]) # The header is mandatory. b4_defines_if([], @@ -51,11 +49,10 @@ dnl FIXME: This is wrong, we want computed header guards. #include #include "stack.hh" -namespace ]b4_namespace[ -{ +]b4_namespace_open[ class position; class location; -} +]b4_namespace_close[ #include "location.hh" @@ -96,8 +93,7 @@ do { \ } while (false) #endif -namespace ]b4_namespace[ -{ +]b4_namespace_open[ /// A Bison parser. class ]b4_parser_class_name[ @@ -286,14 +282,14 @@ b4_error_verbose_if([, int tok])[); static const token_number_type yyundef_token_; ]b4_parse_param_vars[ }; -} +]b4_namespace_close[ ]b4_percent_define_flag_if([[global_tokens_and_yystype]], [b4_token_defines(b4_tokens) #ifndef YYSTYPE /* Redirection for backward compatibility. */ -# define YYSTYPE b4_namespace::b4_parser_class_name::semantic_type +# define YYSTYPE b4_namespace_ref::b4_parser_class_name::semantic_type #endif ]) b4_percent_code_get([[provides]])[]dnl @@ -375,8 +371,7 @@ do { \ #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab -namespace ]b4_namespace[ -{ +]b4_namespace_open[ #if YYERROR_VERBOSE /* Return YYSTR after stripping away unnecessary quotes and @@ -1049,7 +1044,7 @@ b4_error_verbose_if([, int tok])[) const unsigned int ]b4_parser_class_name[::yyuser_token_number_max_ = ]b4_user_token_number_max[; const ]b4_parser_class_name[::token_number_type ]b4_parser_class_name[::yyundef_token_ = ]b4_undef_token_number[; -} // namespace ]b4_namespace[ +]b4_namespace_close[ ]b4_epilogue dnl @@ -1062,8 +1057,7 @@ b4_copyright([Stack handling for Bison parsers in C++], #include -namespace ]b4_namespace[ -{ +]b4_namespace_open[ template > class stack { @@ -1149,7 +1143,7 @@ namespace ]b4_namespace[ const S& stack_; unsigned int range_; }; -} +]b4_namespace_close[ #endif // not BISON_STACK_HH] m4_divert_pop(0) diff --git a/data/location.cc b/data/location.cc index a6e5e57f..4b79069f 100644 --- a/data/location.cc +++ b/data/location.cc @@ -25,7 +25,7 @@ b4_copyright([Positions for Bison parsers in C++], /** ** \file position.hh - ** Define the ]b4_percent_define_get([[namespace]])[::position class. + ** Define the ]b4_namespace_ref[::position class. */ #ifndef BISON_POSITION_HH @@ -35,8 +35,7 @@ b4_copyright([Positions for Bison parsers in C++], # include # include -namespace ]b4_percent_define_get([[namespace]])[ -{ +]b4_namespace_open[ /// Abstract a position. class position { @@ -142,7 +141,7 @@ namespace ]b4_percent_define_get([[namespace]])[ return ostr << pos.line << '.' << pos.column; } -} +]b4_namespace_close[ #endif // not BISON_POSITION_HH] @output(b4_dir_prefix[]location.hh@) b4_copyright([Locations for Bison parsers in C++], @@ -150,7 +149,7 @@ b4_copyright([Locations for Bison parsers in C++], /** ** \file location.hh - ** Define the ]b4_percent_define_get([[namespace]])[::location class. + ** Define the ]b4_namespace_ref[::location class. */ #ifndef BISON_LOCATION_HH @@ -160,8 +159,7 @@ b4_copyright([Locations for Bison parsers in C++], # include # include "position.hh" -namespace ]b4_percent_define_get([[namespace]])[ -{ +]b4_namespace_open[ /// Abstract a location. class location @@ -270,7 +268,7 @@ namespace ]b4_percent_define_get([[namespace]])[ return ostr; } -} +]b4_namespace_close[ #endif // not BISON_LOCATION_HH] m4_divert_pop(0) diff --git a/doc/bison.texinfo b/doc/bison.texinfo index ccec0d45..dae36315 100644 --- a/doc/bison.texinfo +++ b/doc/bison.texinfo @@ -4726,8 +4726,8 @@ where Bison should generate it. Not all values of @var{qualifier} are available for all target languages: @itemize @bullet -@findex %code requires @item requires +@findex %code requires @itemize @bullet @item Language(s): C, C++ @@ -4833,14 +4833,77 @@ This is equivalent to @code{"true"}. In this case, Bison selects a default value, which may depend on the selected target language and/or parser skeleton. @end enumerate -@end deffn -@deffn {Directive} %define push_pull "@var{value}" -Bison declaration to request a @code{"pull"} parser, a @code{"push"} parser, or -@code{"both"}. -The default @code{"@var{value}"} is @code{"pull"}. -This directive is currently only available for LALR(1) parsers in C. +Some of the accepted @var{variable}s are: + +@itemize @bullet +@item push_pull +@findex %define push_pull + +@itemize @bullet +@item Language(s): C (LALR(1) only) + +@item Purpose: Requests a pull parser, a push parser, or both. @xref{Push Decl, ,A Push Parser}. + +@item Accepted Values: @code{"pull"}, @code{"push"}, @code{"both"} + +@item Default Value: @code{"pull"} +@end itemize + +@item namespace +@findex %define namespace + +@itemize +@item Languages(s): C++ + +@item Purpose: Specifies the namespace for the parser class. +For example, if you specify: + +@smallexample +%define namespace "foo::bar" +@end smallexample + +Bison uses @code{foo::bar} verbatim in references such as: + +@smallexample +foo::bar::parser::semantic_type +@end smallexample + +However, to open a namespace, Bison removes any leading @code{::} and then +splits on any remaining occurrences: + +@smallexample +namespace foo @{ namespace bar @{ + class position; + class location; +@} @} +@end smallexample + +@item Accepted Values: Any absolute or relative C++ namespace reference without +a trailing @code{"::"}. +For example, @code{"foo"} or @code{"::foo::bar"}. + +@item Default Value: The value specified by @code{%name-prefix}, which defaults +to @code{yy}. +This usage of @code{%name-prefix} is for backward compatibility and can be +confusing since @code{%name-prefix} also specifies the textual prefix for the +lexical analyzer function. +Thus, if you specify @code{%name-prefix}, it is best to also specify +@code{%define namespace} so that @code{%name-prefix} @emph{only} affects the +lexical analyzer function. +For example, if you specify: + +@smallexample +%define namespace "foo" +%name-prefix "bar::" +@end smallexample + +The parser namespace is @code{foo} and @code{yylex} is referenced as +@code{bar::lex}. +@end itemize +@end itemize + @end deffn @deffn {Directive} %defines @@ -4921,9 +4984,9 @@ is @code{yyparse}, @code{yylex}, @code{yyerror}, @code{yynerrs}, @code{yypush_parse}, @code{yypull_parse}, @code{yypstate}, @code{yypstate_new} and @code{yypstate_delete} will also be renamed. For example, if you use @samp{%name-prefix "c_"}, the -names become @code{c_parse}, @code{c_lex}, and so on. In C++ parsers, -it is only the surrounding namespace which is named @var{prefix} instead -of @samp{yy}. +names become @code{c_parse}, @code{c_lex}, and so on. +For C++ parsers, see the @code{%define namespace} documentation in this +section. @xref{Multiple Parsers, ,Multiple Parsers in the Same Program}. @end deffn @@ -7812,10 +7875,12 @@ The C++ @acronym{LALR}(1) parser is selected using the language directive, @option{--language=c++}. @xref{Decl Summary}. -When run, @command{bison} will create several -entities in the @samp{yy} namespace. Use the @samp{%name-prefix} -directive to change the namespace name, see @ref{Decl Summary}. The -various classes are generated in the following files: +When run, @command{bison} will create several entities in the @samp{yy} +namespace. +@findex %define namespace +Use the @samp{%define namespace} directive to change the namespace name, see +@ref{Decl Summary}. +The various classes are generated in the following files: @table @file @item position.hh @@ -9329,12 +9394,6 @@ Define a variable to adjust Bison's behavior. @xref{Decl Summary,,%define}. @end deffn -@deffn {Directive} %define push_pull "@var{value}" -Bison declaration to request a @code{"pull"} parser, a @code{"push"} parser, or -@code{"both"}. -@xref{Decl Summary,,%define push_pull}. -@end deffn - @deffn {Directive} %defines Bison declaration to create a header file meant for the scanner. @xref{Decl Summary}. diff --git a/tests/c++.at b/tests/c++.at index 39beeb08..631e063e 100644 --- a/tests/c++.at +++ b/tests/c++.at @@ -1,5 +1,5 @@ # Checking the output filenames. -*- Autotest -*- -# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# Copyright (C) 2004, 2005, 2007 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 @@ -99,3 +99,84 @@ m4_popdef([AT_DOXYGEN_PRIVATE]) 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_CHECK([[bison -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 diff --git a/tests/input.at b/tests/input.at index 9778078a..2e12ac6f 100644 --- a/tests/input.at +++ b/tests/input.at @@ -858,3 +858,52 @@ AT_CHECK([[bison input.y]], [1], [], ]]) AT_CLEANUP + +## -------------------------------- ## +## C++ namespace reference errors. ## +## -------------------------------- ## + +AT_SETUP([[C++ namespace reference errors]]) + +# AT_CHECK_NAMESPACE_ERROR(NAMESPACE-DECL, ERROR, [ERROR], ...) +# ------------------------------------------------------------- +# Make sure Bison reports all ERROR's for %define namespace "NAMESPACE-DECL". +m4_define([AT_CHECK_NAMESPACE_ERROR], +[ +AT_DATA([[input.y]], +[[%language "C++" +%defines +%define namespace "]$1[" +%% +start: ; +]]) + +AT_CHECK([[bison input.y]], [1], [], +[m4_foreach([b4_arg], m4_dquote(m4_shift($@)), +[[input.y:3.9-17: ]b4_arg[ +]])]) +]) + +AT_CHECK_NAMESPACE_ERROR([[]], + [[namespace reference is empty]]) +AT_CHECK_NAMESPACE_ERROR([[ ]], + [[namespace reference is empty]]) +AT_CHECK_NAMESPACE_ERROR([[foo::::bar]], + [[namespace reference has consecutive "::"]]) +AT_CHECK_NAMESPACE_ERROR([[foo:: ::bar]], + [[namespace reference has consecutive "::"]]) +AT_CHECK_NAMESPACE_ERROR([[::::bar]], + [[namespace reference has consecutive "::"]]) +AT_CHECK_NAMESPACE_ERROR([[:: ::bar]], + [[namespace reference has consecutive "::"]]) +AT_CHECK_NAMESPACE_ERROR([[foo::bar:: ::]], + [[namespace reference has consecutive "::"]], + [[namespace reference has a trailing "::"]]) +AT_CHECK_NAMESPACE_ERROR([[foo::bar::]], + [[namespace reference has a trailing "::"]]) +AT_CHECK_NAMESPACE_ERROR([[foo::bar:: ]], + [[namespace reference has a trailing "::"]]) +AT_CHECK_NAMESPACE_ERROR([[::]], + [[namespace reference has a trailing "::"]]) + +AT_CLEANUP -- 2.47.2