From df213193d8dc7c5b12420710e328345c6e2c973e Mon Sep 17 00:00:00 2001
From: Akim Demaille <akim@lrde.epita.fr>
Date: Tue, 26 Jun 2012 10:09:10 +0200
Subject: [PATCH] api.prefix.

* data/c.m4 (b4_api_prefix, b4_api_PREFIX): New.
(b4_prefix, b4_union_name, b4_token_enums, b4_declare_yylstype): Use them.
* data/glr.c, data/yacc.c, data/glr.cc, data/lalr1.cc: Use them to change
the prefix of exported preprocessor symbols.
* tests/headers.at (Several parsers): New.
* tests/local.at (AT_API_PREFIX): New.
AT_YYSTYPE, AT_YYLTYPE): Adjust.
* NEWS: Announce api.prefix.
* doc/bison.texi (%define Summary): Add api.prefix.
---
 NEWS             | 55 +++++++++++++++++++++++++++++++++++
 data/c.m4        | 65 ++++++++++++++++++++++++-----------------
 data/glr.c       |  8 ++++++
 data/glr.cc      | 14 ++++-----
 data/lalr1.cc    | 12 ++++----
 data/yacc.c      | 10 ++++++-
 doc/bison.texi   | 17 +++++++++++
 tests/headers.at | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/local.at   |  7 +++--
 9 files changed, 234 insertions(+), 42 deletions(-)

diff --git a/NEWS b/NEWS
index 33fac14..6100cac 100644
--- a/NEWS
+++ b/NEWS
@@ -70,6 +70,61 @@ GNU Bison NEWS
   For the same reasons, the undocumented and unused macro YYLSP_NEEDED is no
   longer defined.
 
+** New %define variable: api.prefix
+
+  Now that the generated headers are more complete and properly protected
+  against multiple inclusions, constant names, such as YYSTYPE are a
+  problem.  While yyparse and other are properly renamed by %name-prefix,
+  YYSTYPE, YYDEBUG and other have never been affected by it.  Because it
+  would introduce backward compatibility issues where it is not expected
+  that YYSTYPE would be renamed, instead of changing the behavior of
+  %name-prefix, it was decided to deprecate it in favor of a new %define
+  variable: api.prefix.
+
+  The following examples compares both:
+
+    %name-prefix "bar_"               | %define api.prefix "bar_"
+    %token <ival> FOO                   %token <ival> FOO
+    %union { int ival; }                %union { int ival; }
+    %%                                  %%
+    exp: 'a';                           exp: 'a';
+
+  bison generates
+
+    #ifndef BAR_FOO_H                   #ifndef BAR_FOO_H
+    # define BAR_FOO_H                  # define BAR_FOO_H
+
+    /* Enabling traces.  */             /* Enabling traces.  */
+    # ifndef YYDEBUG                    # ifndef YYDEBUG
+    #  define YYDEBUG 0                 #  define YYDEBUG 0
+    # endif                             # endif
+    # if YYDEBUG                        # if YYDEBUG
+    extern int bar_debug;               extern int bar_debug;
+    # endif                             # endif
+
+    /* Tokens.  */                      /* Tokens.  */
+    # ifndef YYTOKENTYPE              | # ifndef BAR_TOKENTYPE
+    #  define YYTOKENTYPE             | #  define BAR_TOKENTYPE
+       enum yytokentype {             |    enum bar_tokentype {
+         FOO = 258                           FOO = 258
+       };                                  };
+    # endif                             # endif
+
+    #if ! defined YYSTYPE \           | #if ! defined BAR_STYPE \
+     && ! defined YYSTYPE_IS_DECLARED |  && ! defined BAR_STYPE_IS_DECLARED
+    typedef union YYSTYPE             | typedef union BAR_STYPE
+    {                                   {
+     int ival;                           int ival;
+    } YYSTYPE;                        | } BAR_STYPE;
+    # define YYSTYPE_IS_DECLARED 1    | # define BAR_STYPE_IS_DECLARED 1
+    #endif                              #endif
+
+    extern YYSTYPE bar_lval;          | extern BAR_STYPE bar_lval;
+
+    int bar_parse (void);               int bar_parse (void);
+
+    #endif /* !BAR_FOO_H  */            #endif /* !BAR_FOO_H  */
+
 * Noteworthy changes in release 2.5.1 (2012-06-05) [stable]
 
 ** Future changes:
diff --git a/data/c.m4 b/data/c.m4
index 1f266a4..0b2699a 100644
--- a/data/c.m4
+++ b/data/c.m4
@@ -84,11 +84,24 @@ m4_define([b4_identification],
 ## Default values.  ##
 ## ---------------- ##
 
+# b4_api_prefix, b4_api_PREFIX
+# ----------------------------
+# Corresponds to %define api.prefix
+b4_percent_define_default([[api.prefix]], [[yy]])
+m4_define([b4_api_prefix],
+[b4_percent_define_get([[api.prefix]])])
+m4_define([b4_api_PREFIX],
+[m4_toupper(b4_api_prefix)])
+
+
+# b4_prefix
+# ---------
+# If the %name-prefix is not given, it is api.prefix.
+m4_define_default([b4_prefix], [b4_api_prefix])
+
 # If the %union is not named, its name is YYSTYPE.
-m4_define_default([b4_union_name], [YYSTYPE])
+m4_define_default([b4_union_name], [b4_api_PREFIX[]STYPE])
 
-# If the %name-prefix is not given, it is yy.
-m4_define_default([b4_prefix], [yy])
 
 ## ------------------------ ##
 ## Pure/impure interfaces.  ##
@@ -232,18 +245,18 @@ m4_define([b4_token_enum],
 # Output the definition of the tokens (if there are) as enums.
 m4_define([b4_token_enums],
 [m4_if([$#$1], [1], [],
-[/* Tokens.  */
-#ifndef YYTOKENTYPE
-# define YYTOKENTYPE
+[[/* Tokens.  */
+#ifndef ]b4_api_PREFIX[TOKENTYPE
+# define ]b4_api_PREFIX[TOKENTYPE
    /* Put the tokens into the symbol table, so that GDB and other debuggers
       know about them.  */
-   enum yytokentype {
-m4_map_sep([     b4_token_enum], [,
+   enum ]b4_api_prefix[tokentype {
+]m4_map_sep([     b4_token_enum], [,
 ],
-	   [$@])
+	   [$@])[
    };
 #endif
-])])
+]])])
 
 
 # b4_token_enums_defines(LIST-OF-PAIRS-TOKEN-NAME-TOKEN-NUMBER)
@@ -533,39 +546,39 @@ b4_locations_if([, yylocationp])[]b4_user_args[);
 ## -------------- ##
 
 # b4_declare_yylstype
-# ------------------
+# -------------------
 # Declarations that might either go into the header (if --defines) or
 # in the parser body.  Declare YYSTYPE/YYLTYPE, and yylval/yylloc.
 m4_define([b4_declare_yylstype],
-[[#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+[[#if ! defined ]b4_api_PREFIX[STYPE && ! defined ]b4_api_PREFIX[STYPE_IS_DECLARED
 ]m4_ifdef([b4_stype],
 [[typedef union ]b4_union_name[
 {
 ]b4_user_stype[
-} YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1]],
+} ]b4_api_PREFIX[STYPE;
+# define ]b4_api_PREFIX[STYPE_IS_TRIVIAL 1]],
 [m4_if(b4_tag_seen_flag, 0,
-[[typedef int YYSTYPE;
-# define YYSTYPE_IS_TRIVIAL 1]])])[
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
+[[typedef int ]b4_api_PREFIX[STYPE;
+# define ]b4_api_PREFIX[STYPE_IS_TRIVIAL 1]])])[
+# define ]b4_api_prefix[stype ]b4_api_PREFIX[STYPE /* obsolescent; will be withdrawn */
+# define ]b4_api_PREFIX[STYPE_IS_DECLARED 1
 #endif]b4_locations_if([[
 
-#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
-typedef struct YYLTYPE
+#if ! defined ]b4_api_PREFIX[LTYPE && ! defined ]b4_api_PREFIX[LTYPE_IS_DECLARED
+typedef struct ]b4_api_PREFIX[LTYPE
 {
   int first_line;
   int first_column;
   int last_line;
   int last_column;
-} YYLTYPE;
-# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
-# define YYLTYPE_IS_DECLARED 1
-# define YYLTYPE_IS_TRIVIAL 1
+} ]b4_api_PREFIX[LTYPE;
+# define ]b4_api_prefix[ltype ]b4_api_PREFIX[LTYPE /* obsolescent; will be withdrawn */
+# define ]b4_api_PREFIX[LTYPE_IS_DECLARED 1
+# define ]b4_api_PREFIX[LTYPE_IS_TRIVIAL 1
 #endif]])
 
-b4_pure_if([], [[extern YYSTYPE ]b4_prefix[lval;
-]b4_locations_if([[extern YYLTYPE ]b4_prefix[lloc;]])])[]dnl
+b4_pure_if([], [[extern ]b4_api_PREFIX[STYPE ]b4_prefix[lval;
+]b4_locations_if([[extern ]b4_api_PREFIX[LTYPE ]b4_prefix[lloc;]])])[]dnl
 ])
 
 # b4_declare_yydebug
diff --git a/data/glr.c b/data/glr.c
index 699d730..ce61160 100644
--- a/data/glr.c
+++ b/data/glr.c
@@ -188,6 +188,14 @@ b4_copyright([Skeleton implementation for Bison GLR parsers in C],
 ]b4_identification
 
 b4_percent_code_get([[top]])[
+]m4_if(b4_api_prefix, [yy], [],
+[[/* Substitute the type names.  */
+#define YYSTYPE              ]b4_api_PREFIX[STYPE
+#define YYSTYPE_IS_TRIVIAL   ]b4_api_PREFIX[STYPE_IS_TRIVIAL
+#define YYSTYPE_IS_DECLARED  ]b4_api_PREFIX[STYPE_IS_DECLARED]b4_locations_if([[
+#define YYLTYPE              ]b4_api_PREFIX[LTYPE
+#define YYLTYPE_IS_TRIVIAL   ]b4_api_PREFIX[LTYPE_IS_TRIVIAL
+#define YYLTYPE_IS_DECLARED  ]b4_api_PREFIX[LTYPE_IS_DECLARED]])])[
 ]m4_if(b4_prefix, [yy], [],
 [[/* Substitute the variable and function names.  */
 #define yyparse ]b4_prefix[parse
diff --git a/data/glr.cc b/data/glr.cc
index 8e46be3..d1eb6bd 100644
--- a/data/glr.cc
+++ b/data/glr.cc
@@ -269,7 +269,7 @@ b4_copyright([Skeleton interface for Bison GLR parsers in C++],
   {
   public:
     /// Symbol semantic values.
-#ifndef YYSTYPE
+#ifndef ]b4_api_PREFIX[STYPE
 ]m4_ifdef([b4_stype],
 [    union semantic_type
     {
@@ -277,9 +277,9 @@ b4_user_stype
     };],
 [m4_if(b4_tag_seen_flag, 0,
 [[    typedef int semantic_type;]],
-[[    typedef YYSTYPE semantic_type;]])])[
+[[    typedef ]b4_api_PREFIX[STYPE semantic_type;]])])[
 #else
-    typedef YYSTYPE semantic_type;
+    typedef ]b4_api_PREFIX[STYPE semantic_type;
 #endif
     /// Symbol locations.
     typedef ]b4_percent_define_get([[location_type]],
@@ -350,11 +350,11 @@ b4_user_stype
 b4_percent_define_flag_if([[global_tokens_and_yystype]],
 [b4_token_defines(b4_tokens)])
 [
-#ifndef YYSTYPE
-# define YYSTYPE ]b4_namespace_ref[::]b4_parser_class_name[::semantic_type
+#ifndef ]b4_api_PREFIX[STYPE
+# define ]b4_api_PREFIX[STYPE ]b4_namespace_ref[::]b4_parser_class_name[::semantic_type
 #endif
-#ifndef YYLTYPE
-# define YYLTYPE ]b4_namespace_ref[::]b4_parser_class_name[::location_type
+#ifndef ]b4_api_PREFIX[LTYPE
+# define ]b4_api_PREFIX[LTYPE ]b4_namespace_ref[::]b4_parser_class_name[::location_type
 #endif
 
 ]b4_namespace_close[
diff --git a/data/lalr1.cc b/data/lalr1.cc
index e82e654..337af10 100644
--- a/data/lalr1.cc
+++ b/data/lalr1.cc
@@ -69,7 +69,7 @@ b4_copyright([Skeleton interface for Bison LALR(1) parsers in C++],
   {
   public:
     /// Symbol semantic values.
-#ifndef YYSTYPE
+#ifndef ]b4_api_PREFIX[STYPE
 ]m4_ifdef([b4_stype],
 [    union semantic_type
     {
@@ -77,9 +77,9 @@ b4_user_stype
     };],
 [m4_if(b4_tag_seen_flag, 0,
 [[    typedef int semantic_type;]],
-[[    typedef YYSTYPE semantic_type;]])])[
+[[    typedef ]b4_api_PREFIX[STYPE semantic_type;]])])[
 #else
-    typedef YYSTYPE semantic_type;
+    typedef ]b4_api_PREFIX[STYPE semantic_type;
 #endif
     /// Symbol locations.
     typedef ]b4_percent_define_get([[location_type]],
@@ -262,9 +262,9 @@ b4_user_stype
 ]b4_percent_define_flag_if([[global_tokens_and_yystype]],
 [b4_token_defines(b4_tokens)
 
-#ifndef YYSTYPE
+#ifndef ]b4_api_PREFIX[STYPE
  /* Redirection for backward compatibility.  */
-# define YYSTYPE b4_namespace_ref::b4_parser_class_name::semantic_type
+# define ]b4_api_PREFIX[STYPE b4_namespace_ref::b4_parser_class_name::semantic_type
 #endif
 ])[
 ]b4_percent_code_get([[provides]])[
@@ -597,7 +597,7 @@ m4_popdef([b4_at_dollar])])dnl
       {
 	YYCDEBUG << "Reading a token: ";
 	yychar = ]b4_c_function_call([yylex], [int],
-				     [[YYSTYPE*], [&yylval]][]dnl
+				     [b4_api_PREFIX[STYPE*], [&yylval]][]dnl
 b4_locations_if([, [[location*], [&yylloc]]])dnl
 m4_ifdef([b4_lex_param], [, ]b4_lex_param))[;
       }
diff --git a/data/yacc.c b/data/yacc.c
index 7cf088b..8c595f9 100644
--- a/data/yacc.c
+++ b/data/yacc.c
@@ -307,7 +307,15 @@ b4_copyright([Bison implementation for Yacc-like parsers in C],
 
 ]b4_identification
 b4_percent_code_get([[top]])[]dnl
-m4_if(b4_prefix, [yy], [],
+m4_if(b4_api_prefix, [yy], [],
+[[/* Substitute the type names.  */
+#define YYSTYPE              ]b4_api_PREFIX[STYPE
+#define YYSTYPE_IS_TRIVIAL   ]b4_api_PREFIX[STYPE_IS_TRIVIAL
+#define YYSTYPE_IS_DECLARED  ]b4_api_PREFIX[STYPE_IS_DECLARED]b4_locations_if([[
+#define YYLTYPE              ]b4_api_PREFIX[LTYPE
+#define YYLTYPE_IS_TRIVIAL   ]b4_api_PREFIX[LTYPE_IS_TRIVIAL
+#define YYLTYPE_IS_DECLARED  ]b4_api_PREFIX[LTYPE_IS_DECLARED]])])[
+]m4_if(b4_prefix, [yy], [],
 [[/* Substitute the variable and function names.  */]b4_pull_if([[
 #define yyparse         ]b4_prefix[parse]])b4_push_if([[
 #define yypush_parse    ]b4_prefix[push_parse]b4_pull_if([[
diff --git a/doc/bison.texi b/doc/bison.texi
index b163166..5e7206f 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -5319,6 +5319,21 @@ Some of the accepted @var{variable}s are:
 @itemize @bullet
 @c ================================================== api.pure
 @item api.pure
+@findex %define api.prefix
+
+@itemize @bullet
+@item Language(s): All
+
+@item Purpose: Rename exported symbols
+@xref{Multiple Parsers, ,Multiple Parsers in the Same Program}.
+
+@item Accepted Values: String
+
+@item Default Value: @code{yy}
+@end itemize
+
+@c ================================================== api.pure
+@item api.pure
 @findex %define api.pure
 
 @itemize @bullet
@@ -5332,6 +5347,8 @@ Some of the accepted @var{variable}s are:
 @item Default Value: @code{false}
 @end itemize
 
+@c ================================================== api.push-pull
+
 @item api.push-pull
 @findex %define api.push-pull
 
diff --git a/tests/headers.at b/tests/headers.at
index 549d62d..baf918d 100644
--- a/tests/headers.at
+++ b/tests/headers.at
@@ -117,3 +117,91 @@ AT_COMPILE([caller], [caller.o input.o])
 AT_PARSER_CHECK([./caller])
 
 AT_CLEANUP
+
+## ----------------- ##
+## Several parsers.  ##
+## ----------------- ##
+
+AT_SETUP([Several parsers])
+
+# AT_DATA_GRAMMAR_SEVERAL([PREFIX], [DIRECTIVES])
+# -----------------------------------------------
+# Generate and compile to *.o.  Make sure there is no YY* nor yy* in
+# the header (but YYDEBUG and YYPARSE_PARAM).
+m4_define([AT_DATA_GRAMMAR_SEVERAL],
+[AT_BISON_OPTION_PUSHDEFS([%define api.prefix "$1_" $2])
+AT_DATA_GRAMMAR([AT_SKEL_CC_IF([$1.yy], [$1.y])],
+[[%define api.prefix "$1_"
+$2
+%union
+{
+  int integer;
+}
+%{
+#include <stdio.h>
+  ]AT_YYERROR_DECLARE[
+  ]AT_YYLEX_DECLARE[
+%}
+%%
+exp:
+  'x' '1' { printf ("x1\n"); }
+| 'x' '2' { printf ("x2\n"); }
+| 'x' '3' { printf ("x3\n"); }
+| 'x' '4' { printf ("x4\n"); }
+| 'x' '5' { printf ("x5\n"); }
+| 'x' '6' { printf ("x6\n"); }
+;
+
+%%
+]AT_YYERROR_DEFINE[
+]AT_YYLEX_DEFINE([$1])[
+]])
+
+AT_BISON_CHECK([-d -o AT_SKEL_CC_IF([$1.cc $1.yy], [$1.c $1.y])])
+#AT_CHECK([$EGREP -i yy $1.h | $EGREP -v 'YY(DEBUG|PARSE_PARAM)'], [1])
+AT_LANG_COMPILE([$1.o])
+AT_BISON_OPTION_POPDEFS
+])
+
+AT_DATA([main.cc],
+[[extern "C"
+{
+  #include "x1.h"
+  #include "x2.h"
+  #include "x3.h"
+  #include "x4.h"
+}
+#include "x5.hh"
+//#include "x6.hh"
+int
+main (void)
+{
+  int errs = 0;
+  errs += x1_parse();
+  errs += x2_parse();
+  errs += x3_parse();
+  errs += x4_parse();
+  x5_::parser p5;
+  errs += p5.parse();
+//  errs += x6_parse();
+  return !!errs;
+}
+]])
+
+AT_DATA_GRAMMAR_SEVERAL([x1], [])
+AT_DATA_GRAMMAR_SEVERAL([x2], [%locations %debug])
+AT_DATA_GRAMMAR_SEVERAL([x3], [%glr-parser])
+AT_DATA_GRAMMAR_SEVERAL([x4], [%locations %debug %glr-parser])
+AT_DATA_GRAMMAR_SEVERAL([x5], [%locations %debug %language "c++"])
+#AT_DATA_GRAMMAR_SEVERAL([x6], [%locations %language "c++"])
+
+AT_COMPILE_CXX([parser], [x1.o x2.o x3.o x4.o x5.o main.cc])
+AT_CHECK([./parser], [0],
+[[x1
+x2
+x3
+x4
+x5
+]])
+
+AT_CLEANUP
diff --git a/tests/local.at b/tests/local.at
index 5a5bbdd..7c199de 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -145,6 +145,8 @@ m4_pushdef([AT_API_prefix],
 [m4_bmatch([$3], [%define api\.prefix ".*"],
            [m4_bregexp([$3], [%define api\.prefix "\([^""]*\)"], [\1])],
            [yy])])
+m4_pushdef([AT_API_PREFIX],
+[m4_toupper(AT_API_prefix)])
 # yyerror receives the location if %location & %pure & (%glr or %parse-param).
 m4_pushdef([AT_YYERROR_ARG_LOC_IF],
 [AT_GLR_OR_PARAM_IF([AT_PURE_AND_LOC_IF([$1], [$2])],
@@ -165,10 +167,10 @@ m4_pushdef([AT_PURE_LEX_IF],
 
 m4_pushdef([AT_YYSTYPE],
 [AT_SKEL_CC_IF([AT_NAME_PREFIX[::parser::semantic_type]],
-               [[YYSTYPE]])])
+               [AT_API_PREFIX[STYPE]])])
 m4_pushdef([AT_YYLTYPE],
 [AT_SKEL_CC_IF([AT_NAME_PREFIX[::parser::location_type]],
-               [[YYLTYPE]])])
+               [AT_API_PREFIX[LTYPE]])])
 
 
 AT_PURE_LEX_IF(
@@ -221,6 +223,7 @@ m4_popdef([AT_LOC])
 m4_popdef([AT_PURE_LEX_IF])
 m4_popdef([AT_YYERROR_SEES_LOC_IF])
 m4_popdef([AT_YYERROR_ARG_LOC_IF])
+m4_popdef([AT_API_PREFIX])
 m4_popdef([AT_API_prefix])
 m4_popdef([AT_NAME_PREFIX])
 m4_popdef([AT_GLR_OR_PARAM_IF])
-- 
1.7.11.1

