<http://lists.gnu.org/archive/html/help-bison/2007-09/msg00016.html>.
* 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 <jdenny@ces.clemson.edu>
+
+ Improve C++ namespace support. Discussed starting at
+ <http://lists.gnu.org/archive/html/help-bison/2007-09/msg00016.html>.
+ * 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 <jdenny@ces.clemson.edu>
Add syncline support and location accessor to internal %define
[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.
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]))
[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)[
{
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]])])
]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]])[
{
}
-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.
#endif
]m4_popdef([b4_parse_param])dnl
-[} // namespace ]b4_namespace[
+b4_namespace_close[
]])
# 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])
/* Using locations. */
#define YYLSP_NEEDED ]b4_locations_flag[
-namespace ]b4_namespace[
-{
+]b4_namespace_open[
class position;
class location;
-}
+]b4_namespace_close[
#include "location.hh"
while (/*CONSTCOND*/ 0)
#endif
-namespace ]b4_namespace[
-{
+]b4_namespace_open[
/// A Bison parser.
class ]b4_parser_class_name[
{
[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
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([],
#include <iostream>
#include "stack.hh"
-namespace ]b4_namespace[
-{
+]b4_namespace_open[
class position;
class location;
-}
+]b4_namespace_close[
#include "location.hh"
} while (false)
#endif
-namespace ]b4_namespace[
-{
+]b4_namespace_open[
/// A Bison parser.
class ]b4_parser_class_name[
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
#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
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
#include <deque>
-namespace ]b4_namespace[
-{
+]b4_namespace_open[
template <class T, class S = std::deque<T> >
class stack
{
const S& stack_;
unsigned int range_;
};
-}
+]b4_namespace_close[
#endif // not BISON_STACK_HH]
m4_divert_pop(0)
/**
** \file position.hh
- ** Define the ]b4_percent_define_get([[namespace]])[::position class.
+ ** Define the ]b4_namespace_ref[::position class.
*/
#ifndef BISON_POSITION_HH
# include <string>
# include <algorithm>
-namespace ]b4_percent_define_get([[namespace]])[
-{
+]b4_namespace_open[
/// Abstract a position.
class position
{
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++],
/**
** \file location.hh
- ** Define the ]b4_percent_define_get([[namespace]])[::location class.
+ ** Define the ]b4_namespace_ref[::location class.
*/
#ifndef BISON_LOCATION_HH
# include <string>
# include "position.hh"
-namespace ]b4_percent_define_get([[namespace]])[
-{
+]b4_namespace_open[
/// Abstract a location.
class location
return ostr;
}
-}
+]b4_namespace_close[
#endif // not BISON_LOCATION_HH]
m4_divert_pop(0)
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++
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
@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
@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
@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}.
# 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
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
]])
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