bison-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

{master} merge maint


From: Akim Demaille
Subject: {master} merge maint
Date: Fri, 27 Jul 2012 16:35:28 +0200

commit fd68b4cc8acb1360d43c41708216fe3825f2ff26
Merge: 9e41ba0 7b18c11
Author: Akim Demaille <address@hidden>
Date:   Fri Jul 27 16:22:45 2012 +0200

    Merge remote-tracking branch 'origin/maint'
    
    * origin/maint: (29 commits)
      regen
      synclines: remove spurious empty line
      also support $<foo>$ in the %initial-action
      skeletons: b4_dollar_pushdef and popdef to simpify complex definitions
      regen
      printer/destructor: translate only once
      factor the handling of m4 escaping
      news: schedule the removal of the ";" hack
      style changes in the scanners
      regen
      support $<tag>$ in printers and destructors
      scan-code: factor the handling of the type in $<TYPE>$
      muscles: fix another occurrence of unescaped type name
      glr.cc: fix the handling of yydebug
      gnulib: update
      formatting changes
      tests: fix an assertion
      tests: adjust to GCC 4.8, which displays caret errors
      be sure to properly escape type names
      obstack_quote: escape and quote for M4
      muscles: shuffle responsabilities
      muscles: make private functions static
      muscles: rename private functions/macros
      obstack_escape: escape M4 characters
      remove dead macro
      maint: style changes
      doc: avoid problems with case insensitive file systems
      configure: fix botched quoting
      news: fix typo.
    
    Conflicts:
        NEWS
        data/c.m4
        data/glr.cc
        data/lalr1.cc
        examples/rpcalc/local.mk
        src/muscle-tab.h
        src/output.c
        src/parse-gram.c
        src/parse-gram.h
        src/parse-gram.y
        src/scan-code.l
        src/symlist.c
        src/symlist.h
        src/symtab.h
        tests/calc.at

diff --git a/.gitignore b/.gitignore
index a81896f..bfb7caf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
 /INSTALL
 /Makefile
 /Makefile.in
+/README-release
 /_*
 /a.exe
 /a.out
@@ -31,4 +32,3 @@
 /patches
 /releases
 /stamp-h*
-/README-release
diff --git a/NEWS b/NEWS
index fe23b55..150068e 100644
--- a/NEWS
+++ b/NEWS
@@ -129,6 +129,41 @@ GNU Bison NEWS
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+ Bison no longer executes user-specified M4 code when processing a grammar.
+
+** Future Changes
+
+  In addition to the removal of the features announced in Bison 2.6, the
+  next major release will remove the "Temporary hack for adding a semicolon
+  to the user action", as announced in the release 2.5.  Instead of:
+
+    exp: exp "+" exp { $$ = $1 + $3 };
+
+  write:
+
+    exp: exp "+" exp { $$ = $1 + $3; };
+
+** Bug fixes
+
+*** Type names are now properly escaped.
+
+*** glr.cc: set_debug_level and debug_level work as expected.
+
+*** Stray @ or $ in actions
+
+  While Bison used to warn about stray $ or @ in action rules, it did not
+  for other actions such as printers, destructors, or initial actions.  It
+  now does.
+
+** Type names in actions
+
+  For consistency with rule actions, it is now possible to qualify $$ by a
+  type-name in destructors, printers, and initial actions.  For instance:
+
+    %printer { fprintf (yyo, "(%d, %f)", $<ival>$, $<fval>$); } <*> <>;
+
+  will display two values for each typed and untyped symbol (provided
+  that YYSTYPE has both "ival" and "fval" fields).
 
 * Noteworthy changes in release 2.6 (2012-07-19) [stable]
 
@@ -137,7 +172,7 @@ GNU Bison NEWS
   The next major release of Bison will drop support for the following
   deprecated features.  Please report disagreements to address@hidden
 
-*** K&C parsers
+*** K&R C parsers
 
   Support for generating parsers in K&R C will be removed.  Parsers
   generated for C support ISO C90, and are tested with ISO C99 and ISO C11
diff --git a/THANKS b/THANKS
index c0c274c..5e76b1d 100644
--- a/THANKS
+++ b/THANKS
@@ -13,6 +13,7 @@ Anthony Heading           address@hidden
 Arnold Robbins            address@hidden
 Art Haas                  address@hidden
 Baron Schwartz            address@hidden
+Ben Pfaff                 address@hidden
 Benoit Perrot             address@hidden
 Bernd Kiefer              address@hidden
 Bert Deknuydt             address@hidden
diff --git a/TODO b/TODO
index dfd6cfe..6457742 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,8 @@
 * Short term
+** scan-code.l
+Avoid variables for format strings, as then GCC cannot check them.
+show_sub_messages should call show_sub_message.
+
 ** Variable names.
 What should we name `variant' and `lex_symbol'?
 
diff --git a/configure.ac b/configure.ac
index cf42d2c..328d929 100644
--- a/configure.ac
+++ b/configure.ac
@@ -177,7 +177,7 @@ case $VALGRIND:$host_os in
     # VALGRIND+=' 
--suppressions=$(abs_top_srcdir)/build-aux/darwin11.4.0.valgrind'
     VALGRIND=;;
   *:*)
-    AC_SUBST([VALGRIND_PREBISON], [$VALGRIND -q]);;
+    AC_SUBST([VALGRIND_PREBISON], ["$VALGRIND -q"]);;
 esac
 
 AM_MISSING_PROG([AUTOM4TE], [autom4te])
diff --git a/data/bison.m4 b/data/bison.m4
index 1db75a8..f2126ef 100644
--- a/data/bison.m4
+++ b/data/bison.m4
@@ -376,19 +376,17 @@ m4_define([b4_symbol_action_location],
 # Same as in C, but using references instead of pointers.
 m4_define([b4_symbol_action],
 [b4_symbol_if([$1], [has_$2],
-[m4_pushdef([b4_dollar_dollar],
-    [b4_symbol_value([(*yyvaluep)],
-                     b4_symbol_if([$1], [has_type],
-                                  [b4_symbol([$1], [type])]))])dnl
-m4_pushdef([b4_at_dollar], [(*yylocationp)])dnl
+[b4_dollar_pushdef([(*yyvaluep)],
+                   b4_symbol_if([$1], [has_type],
+                                [m4_dquote(b4_symbol([$1], [type]))]),
+                   [(*yylocationp)])dnl
       b4_symbol_case_([$1])[]dnl
 b4_syncline([b4_symbol([$1], [$2_line])], ["b4_symbol([$1], [$2_file])"])
         b4_symbol([$1], [$2])
 b4_syncline(address@hidden@], address@hidden@])
         break;
 
-m4_popdef([b4_at_dollar])dnl
-m4_popdef([b4_dollar_dollar])dnl
+b4_dollar_popdef[]dnl
 ])])
 
 
diff --git a/data/c-like.m4 b/data/c-like.m4
new file mode 100644
index 0000000..732d18d
--- /dev/null
+++ b/data/c-like.m4
@@ -0,0 +1,43 @@
+                                                            -*- Autoconf -*-
+
+# Common code for C-like languages (C, C++, Java, etc.)
+
+# Copyright (C) 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/>.
+
+# b4_dollar_dollar_(VALUE, FIELD, DEFAULT-FIELD)
+# ----------------------------------------------
+# If FIELD (or DEFAULT-FIELD) is non-null, return "VALUE.FIELD",
+# otherwise just VALUE.  Be sure to pass "(VALUE)" is VALUE is a
+# pointer.
+m4_define([b4_dollar_dollar_],
+[b4_symbol_value([$1],
+                 m4_if([$2], [[]],
+                       [[$3]], [[$2]]))])
+
+# b4_dollar_pushdef(VALUE-POINTER, DEFAULT-FIELD, LOCATION)
+# b4_dollar_popdef
+# ---------------------------------------------------------
+# Define b4_dollar_dollar for VALUE and DEFAULT-FIELD,
+# and b4_at_dollar for LOCATION.
+m4_define([b4_dollar_pushdef],
+[m4_pushdef([b4_dollar_dollar],
+            [b4_dollar_dollar_([$1], m4_dquote($][1), [$2])])dnl
+m4_pushdef([b4_at_dollar], [$3])dnl
+])
+m4_define([b4_dollar_popdef],
+[m4_popdef([b4_at_dollar])dnl
+m4_popdef([b4_dollar_dollar])dnl
+])
diff --git a/data/c.m4 b/data/c.m4
index b8659eb..a07422d 100644
--- a/data/c.m4
+++ b/data/c.m4
@@ -17,6 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+m4_include(b4_pkgdatadir/[c-like.m4])
 
 # b4_tocpp(STRING)
 # ----------------
diff --git a/data/glr.c b/data/glr.c
index d392622..935ee96 100644
--- a/data/glr.c
+++ b/data/glr.c
@@ -2294,12 +2294,10 @@ yyrecoverSyntaxError (yyGLRStack* 
yystackp]b4_user_formals[)
 #endif
 ])
 m4_ifdef([b4_initial_action], [
-m4_pushdef([b4_at_dollar],     [yylloc])dnl
-m4_pushdef([b4_dollar_dollar], [yylval])dnl
+b4_dollar_pushdef([yylval], [], [yylloc])dnl
   /* User initialization code.  */
   b4_user_initial_action
-m4_popdef([b4_dollar_dollar])dnl
-m4_popdef([b4_at_dollar])])dnl
+b4_dollar_popdef])[]dnl
 [
   if (! yyinitGLRStack (yystackp, YYINITDEPTH))
     goto yyexhaustedlab;
diff --git a/data/glr.cc b/data/glr.cc
index 81e8027..7b3ea3f 100644
--- a/data/glr.cc
+++ b/data/glr.cc
@@ -202,6 +202,7 @@ m4_pushdef([b4_parse_param], 
m4_defn([b4_parse_param_orig]))dnl
   void
   ]b4_parser_class_name[::set_debug_level (debug_level_type l)
   {
+    // Actually, it is yydebug which is really used.
     yydebug = l;
   }
 
diff --git a/data/java.m4 b/data/java.m4
index cd70852..988ac39 100644
--- a/data/java.m4
+++ b/data/java.m4
@@ -17,6 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+m4_include(b4_pkgdatadir/[c-like.m4])
 
 # b4_comment(TEXT)
 # ----------------
diff --git a/data/lalr1.cc b/data/lalr1.cc
index 96936bb..313dba2 100644
--- a/data/lalr1.cc
+++ b/data/lalr1.cc
@@ -98,19 +98,19 @@ m4_define([b4_rhs_location],
 # Same as in C, but using references instead of pointers.
 m4_define([b4_symbol_action],
 [b4_symbol_if([$1], [has_$2],
-[m4_pushdef([b4_dollar_dollar],
-    [b4_symbol_value_template([yysym.value],
-                              b4_symbol_if([$1], [has_type],
-                                           [b4_symbol([$1], [type])]))])dnl
-m4_pushdef([b4_at_dollar], [yysym.location])dnl
+[m4_pushdef([b4_symbol_value], m4_defn([b4_symbol_value_template]))[]dnl
+b4_dollar_pushdef([yysym.value],
+                   b4_symbol_if([$1], [has_type],
+                                [m4_dquote(b4_symbol([$1], [type]))]),
+                   [yysym.location])dnl
       b4_symbol_case_([$1])
 b4_syncline([b4_symbol([$1], [$2_line])], ["b4_symbol([$1], [$2_file])"])
         b4_symbol([$1], [$2])
 b4_syncline(address@hidden@], address@hidden@])
         break;
 
-m4_popdef([b4_at_dollar])dnl
-m4_popdef([b4_dollar_dollar])dnl
+m4_popdef([b4_symbol_value])[]dnl
+b4_dollar_popdef[]dnl
 ])])
 
 
@@ -670,12 +670,10 @@ m4_if(b4_prefix, [yy], [],
     YYCDEBUG << "Starting parse" << std::endl;
 
 ]m4_ifdef([b4_initial_action], [
-m4_pushdef([b4_at_dollar],     [yyla.location])dnl
-m4_pushdef([b4_dollar_dollar], [yyla.value])dnl
+b4_dollar_pushdef([yyla.value], [], [yyla.location])dnl
     /* User initialization code.  */
     b4_user_initial_action
-m4_popdef([b4_dollar_dollar])dnl
-m4_popdef([b4_at_dollar])])dnl
+b4_dollar_popdef])[]dnl
 
   [  /* Initialize the stack.  The initial state will be set in
        yynewstate, since the latter expects the semantical and the
diff --git a/data/lalr1.java b/data/lalr1.java
index d3729c6..1cfaef3 100644
--- a/data/lalr1.java
+++ b/data/lalr1.java
@@ -502,12 +502,10 @@ b4_lexer_if([[
     yyerrstatus_ = 0;
 
 ]m4_ifdef([b4_initial_action], [
-m4_pushdef([b4_at_dollar],     [yylloc])dnl
-m4_pushdef([b4_dollar_dollar], [yylval])dnl
+b4_dollar_pushdef([yylval], [], [yylloc])dnl
     /* User initialization code.  */
     b4_user_initial_action
-m4_popdef([b4_dollar_dollar])dnl
-m4_popdef([b4_at_dollar])])dnl
+b4_dollar_popdef])[]dnl
 
   [  /* Initialize the stack.  */
     yystack.push (yystate, yylval]b4_locations_if([, yylloc])[);
diff --git a/data/local.mk b/data/local.mk
index 6337f76..b8db2d6 100644
--- a/data/local.mk
+++ b/data/local.mk
@@ -18,6 +18,7 @@ dist_pkgdata_DATA =                             \
   data/bison.m4                                 \
   data/c++-skel.m4                              \
   data/c++.m4                                   \
+  data/c-like.m4                                \
   data/c-skel.m4                                \
   data/c.m4                                     \
   data/glr.c                                    \
diff --git a/data/yacc.c b/data/yacc.c
index b3864e9..7376f90 100644
--- a/data/yacc.c
+++ b/data/yacc.c
@@ -1484,17 +1484,16 @@ b4_c_function_def([[yyparse]], [[int]], b4_parse_param)[
   yylloc.first_column = yylloc.last_column = ]b4_location_initial_column[;
 #endif]])
 m4_ifdef([b4_initial_action],[
-m4_pushdef([b4_at_dollar],     [m4_define([b4_at_dollar_used])yylloc])dnl
-m4_pushdef([b4_dollar_dollar], [m4_define([b4_dollar_dollar_used])yylval])dnl
+b4_dollar_pushdef([m4_define([b4_dollar_dollar_used])yylval], [],
+                  [m4_define([b4_at_dollar_used])yylloc])dnl
 /* User initialization code.  */
 b4_user_initial_action
-m4_popdef([b4_dollar_dollar])dnl
-m4_popdef([b4_at_dollar])])dnl
+b4_dollar_popdef[]dnl
 m4_ifdef([b4_dollar_dollar_used],[[  yyvsp[0] = yylval;
 ]])dnl
 m4_ifdef([b4_at_dollar_used], [[  yylsp[0] = yylloc;
-]])[
-  goto yysetstate;
+]])])dnl
+[  goto yysetstate;
 
 /*------------------------------------------------------------.
 | yynewstate -- Push a new state, which is found in yystate.  |
diff --git a/doc/bison.texi b/doc/bison.texi
index 2d06fec..62ff045 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -110,7 +110,7 @@ Reference sections:
 * Glossary::            Basic concepts are explained.
 * Copying This Manual:: License for copying this manual.
 * Bibliography::        Publications cited in this manual.
-* Index::               Cross-references to the text.
+* Index of Terms::      Cross-references to the text.
 
 @detailmenu
  --- The Detailed Node Listing ---
@@ -4634,9 +4634,9 @@ code.
 @deffn {Directive} %initial-action @{ @var{code} @}
 @findex %initial-action
 Declare that the braced @var{code} must be invoked before parsing each time
address@hidden is called.  The @var{code} may use @code{$$} and
address@hidden@@$} --- initial value and location of the lookahead --- and the
address@hidden
address@hidden is called.  The @var{code} may use @code{$$} (or
address@hidden<@var{tag}>$}) and @code{@@$} --- initial value and location of 
the
+lookahead --- and the @code{%parse-param}.
 @end deffn
 
 For instance, if your locations use a file name, you may use
@@ -4674,11 +4674,11 @@ symbol is automatically discarded.
 @deffn {Directive} %destructor @{ @var{code} @} @var{symbols}
 @findex %destructor
 Invoke the braced @var{code} whenever the parser discards one of the
address@hidden
-Within @var{code}, @code{$$} designates the semantic value associated
-with the discarded symbol, and @code{@@$} designates its location.
-The additional parser parameters are also available (@pxref{Parser Function, ,
-The Parser Function @code{yyparse}}).
address@hidden  Within @var{code}, @code{$$} (or @code{$<@var{tag}>$})
+designates the semantic value associated with the discarded symbol, and
address@hidden@@$} designates its location.  The additional parser parameters 
are
+also available (@pxref{Parser Function, , The Parser Function
address@hidden).
 
 When a symbol is listed among @var{symbols}, its @code{%destructor} is called a
 per-symbol @code{%destructor}.
@@ -4816,10 +4816,11 @@ Decl, , Freeing Discarded Symbols}).
 @c This is the same text as for %destructor.
 Invoke the braced @var{code} whenever the parser displays one of the
 @var{symbols}.  Within @var{code}, @code{yyoutput} denotes the output stream
-(a @code{FILE*} in C, and an @code{std::ostream&} in C++),
address@hidden designates the semantic value associated with the symbol, and
address@hidden@@$} its location.  The additional parser parameters are also
-available (@pxref{Parser Function, , The Parser Function @code{yyparse}}).
+(a @code{FILE*} in C, and an @code{std::ostream&} in C++), @code{$$} (or
address@hidden<@var{tag}>$}) designates the semantic value associated with the
+symbol, and @code{@@$} its location.  The additional parser parameters are
+also available (@pxref{Parser Function, , The Parser Function
address@hidden).
 
 The @var{symbols} are defined as for @code{%destructor} (@pxref{Destructor
 Decl, , Freeing Discarded Symbols}.): they can be per-type (e.g.,
@@ -12375,8 +12376,8 @@ London, Department of Computer Science, TR-00-12 
(December 2000).
 
@uref{http://www.cs.rhul.ac.uk/research/languages/publications/tomita_style_1.ps}
 @end table
 
address@hidden Index
address@hidden Index
address@hidden Index of Terms
address@hidden Index of Terms
 
 @printindex cp
 
diff --git a/gnulib b/gnulib
index 2f67aa7..dbd9144 160000
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit 2f67aa79c6f13961eda685144130692dc9f7b95f
+Subproject commit dbd914496c99c52220e5f5ba4121d6cb55fb3beb
diff --git a/src/muscle-tab.c b/src/muscle-tab.c
index 47d9fb6..1168a9c 100644
--- a/src/muscle-tab.c
+++ b/src/muscle-tab.c
@@ -177,10 +177,10 @@ static void
 muscle_syncline_grow (char const *key, location loc)
 {
   char *extension = NULL;
-  obstack_fgrow1 (&muscle_obstack, "]b4_syncline(%d, [[", loc.start.line);
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack,
-                        quotearg_style (c_quoting_style, loc.start.file));
-  obstack_sgrow (&muscle_obstack, "]])[");
+  obstack_fgrow1 (&muscle_obstack, "]b4_syncline(%d, ", loc.start.line);
+  obstack_quote (&muscle_obstack,
+                 quotearg_style (c_quoting_style, loc.start.file));
+  obstack_sgrow (&muscle_obstack, ")[");
   obstack_1grow (&muscle_obstack, 0);
   extension = obstack_finish (&muscle_obstack);
   muscle_grow (key, extension, "");
@@ -205,11 +205,11 @@ void muscle_pair_list_grow (const char *muscle,
                             const char *a1, const char *a2)
 {
   char *pair;
-  obstack_sgrow (&muscle_obstack, "[[[");
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack, a1);
-  obstack_sgrow (&muscle_obstack, "]], [[");
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack, a2);
-  obstack_sgrow (&muscle_obstack, "]]]");
+  obstack_sgrow (&muscle_obstack, "[");
+  obstack_quote (&muscle_obstack, a1);
+  obstack_sgrow (&muscle_obstack, ", ");
+  obstack_quote (&muscle_obstack, a2);
+  obstack_sgrow (&muscle_obstack, "]");
   obstack_1grow (&muscle_obstack, 0);
   pair = obstack_finish (&muscle_obstack);
   muscle_grow (muscle, pair, ",\n");
@@ -259,53 +259,61 @@ muscle_find (char const *key)
 }
 
 
-void
+/* In the format `file_name:line.column', append BOUND to MUSCLE.  Use
+   digraphs for special characters in the file name.  */
+
+static void
 muscle_boundary_grow (char const *key, boundary bound)
 {
   char *extension;
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack, bound.file);
+  obstack_sgrow  (&muscle_obstack, "[[");
+  obstack_escape (&muscle_obstack, bound.file);
   obstack_1grow (&muscle_obstack, ':');
   obstack_fgrow1 (&muscle_obstack, "%d", bound.line);
   obstack_1grow (&muscle_obstack, '.');
   obstack_fgrow1 (&muscle_obstack, "%d", bound.column);
+  obstack_sgrow  (&muscle_obstack, "]]");
   obstack_1grow (&muscle_obstack, '\0');
   extension = obstack_finish (&muscle_obstack);
   muscle_grow (key, extension, "");
   obstack_free (&muscle_obstack, extension);
 }
 
-void
+
+/* In the format `[[file_name:line.column]], [[file_name:line.column]]',
+   append LOC to MUSCLE.  Use digraphs for special characters in each
+   file name.  */
+
+static void
 muscle_location_grow (char const *key, location loc)
 {
-  muscle_grow (key, "[[", "");
   muscle_boundary_grow (key, loc.start);
-  muscle_grow (key, "]], [[", "");
+  muscle_grow (key, "", ", ");
   muscle_boundary_grow (key, loc.end);
-  muscle_grow (key, "]]", "");
 }
 
-#define MUSCLE_COMMON_DECODE(Value)                                           \
-  case '$':                                                                   \
-    aver (*++(Value) == ']');                                                 \
-    aver (*++(Value) == '[');                                                 \
-    obstack_sgrow (&muscle_obstack, "$");                                     \
-    break;                                                                    \
-  case '@':                                                                   \
-    switch (*++(Value))                                                       \
-      {                                                                       \
-        case '@': obstack_sgrow (&muscle_obstack, "@" ); break;               \
-        case '{': obstack_sgrow (&muscle_obstack, "[" ); break;               \
-        case '}': obstack_sgrow (&muscle_obstack, "]" ); break;               \
-        default: aver (false); break;                                         \
-      }                                                                       \
-    break;                                                                    \
-  default:                                                                    \
-    obstack_1grow (&muscle_obstack, *(Value));                                \
+#define COMMON_DECODE(Value)                                    \
+  case '$':                                                     \
+    aver (*++(Value) == ']');                                   \
+    aver (*++(Value) == '[');                                   \
+    obstack_sgrow (&muscle_obstack, "$");                       \
+    break;                                                      \
+  case '@':                                                     \
+    switch (*++(Value))                                         \
+      {                                                         \
+        case '@': obstack_sgrow (&muscle_obstack, "@" ); break; \
+        case '{': obstack_sgrow (&muscle_obstack, "[" ); break; \
+        case '}': obstack_sgrow (&muscle_obstack, "]" ); break; \
+        default: aver (false); break;                           \
+      }                                                         \
+    break;                                                      \
+  default:                                                      \
+    obstack_1grow (&muscle_obstack, *(Value));                  \
     break;
 
-/* Reverse of MUSCLE_OBSTACK_SGROW.  */
+/* Reverse of obstack_escape.  */
 static char *
-muscle_string_decode (char const *key)
+string_decode (char const *key)
 {
   char const *value;
   char *value_decoded;
@@ -317,7 +325,7 @@ muscle_string_decode (char const *key)
   do {
     switch (*value)
       {
-        MUSCLE_COMMON_DECODE (value)
+        COMMON_DECODE (value)
         case '[':
         case ']':
           aver (false);
@@ -332,7 +340,7 @@ muscle_string_decode (char const *key)
 
 /* Reverse of muscle_location_grow.  */
 static location
-muscle_location_decode (char const *key)
+location_decode (char const *key)
 {
   location loc;
   char const *value = muscle_find_const (key);
@@ -342,7 +350,7 @@ muscle_location_decode (char const *key)
   while (*++value)
     switch (*value)
       {
-        MUSCLE_COMMON_DECODE (value)
+        COMMON_DECODE (value)
         case '[':
           aver (false);
           break;
@@ -491,7 +499,7 @@ muscle_percent_define_get (char const *variable)
                                variable, ")");
 
   muscle_insert (usage_name, "");
-  value = muscle_string_decode (name);
+  value = string_decode (name);
   if (!value)
     value = xstrdup ("");
   return value;
@@ -505,7 +513,7 @@ muscle_percent_define_get_loc (char const *variable)
   if (!muscle_find_const (loc_name))
     complain (fatal, _("%s: undefined %%define variable %s"),
           "muscle_percent_define_get_loc", quote (variable));
-  return muscle_location_decode (loc_name);
+  return location_decode (loc_name);
 }
 
 char const *
@@ -610,7 +618,7 @@ muscle_percent_define_check_values (char const * const 
*values)
 
       name = UNIQSTR_CONCAT ("percent_define(", *variablep, ")");
 
-      value = muscle_string_decode (name);
+      value = string_decode (name);
       if (value)
         {
           for (++values; *values; ++values)
diff --git a/src/muscle-tab.h b/src/muscle-tab.h
index 5f701ae..1f33f8c 100644
--- a/src/muscle-tab.h
+++ b/src/muscle-tab.h
@@ -35,63 +35,48 @@ void muscle_free (void);
 extern struct obstack muscle_obstack;
 
 #define MUSCLE_INSERT_BOOL(Key, Value)                          \
-do {                                                            \
-  int v__ = Value;                                              \
-  MUSCLE_INSERT_INT (Key, v__);                                 \
-} while(0)
+  do {                                                          \
+    int v__ = Value;                                            \
+    MUSCLE_INSERT_INT (Key, v__);                               \
+  } while (0)
 
 #define MUSCLE_INSERT_INT(Key, Value)                           \
-do {                                                            \
-  obstack_fgrow1 (&muscle_obstack, "%d", Value);                \
-  obstack_1grow (&muscle_obstack, 0);                           \
-  muscle_insert (Key, obstack_finish (&muscle_obstack));        \
-} while(0)
+  do {                                                          \
+    obstack_fgrow1 (&muscle_obstack, "%d", Value);              \
+    obstack_1grow (&muscle_obstack, 0);                         \
+    muscle_insert (Key, obstack_finish (&muscle_obstack));      \
+  } while (0)
 
 #define MUSCLE_INSERT_LONG_INT(Key, Value)                      \
-do {                                                            \
-  obstack_fgrow1 (&muscle_obstack, "%ld", Value);               \
-  obstack_1grow (&muscle_obstack, 0);                           \
-  muscle_insert (Key, obstack_finish (&muscle_obstack));        \
-} while(0)
+  do {                                                          \
+    obstack_fgrow1 (&muscle_obstack, "%ld", Value);             \
+    obstack_1grow (&muscle_obstack, 0);                         \
+    muscle_insert (Key, obstack_finish (&muscle_obstack));      \
+  } while (0)
 
 /* Key -> Value, but don't apply escaping to Value. */
 #define MUSCLE_INSERT_STRING_RAW(Key, Value)                    \
-do {                                                            \
-  obstack_sgrow (&muscle_obstack, Value);                       \
-  obstack_1grow (&muscle_obstack, 0);                           \
-  muscle_insert (Key, obstack_finish (&muscle_obstack));        \
-} while(0)
+  do {                                                          \
+    obstack_sgrow (&muscle_obstack, Value);                     \
+    obstack_1grow (&muscle_obstack, 0);                         \
+    muscle_insert (Key, obstack_finish (&muscle_obstack));      \
+  } while (0)
 
 /* Key -> Value, applying M4 escaping to Value. */
 #define MUSCLE_INSERT_STRING(Key, Value)                        \
-do {                                                            \
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack, Value);                \
-  obstack_1grow (&muscle_obstack, 0);                           \
-  muscle_insert (Key, obstack_finish (&muscle_obstack));        \
-} while(0)
-
-#define MUSCLE_OBSTACK_SGROW(Obstack, Value)            \
-do {                                                    \
-  char const *p__;                                      \
-  for (p__ = Value; *p__; p__++)                        \
-    switch (*p__)                                       \
-      {                                                 \
-      case '$': obstack_sgrow (Obstack, "$]["); break;  \
-      case '@': obstack_sgrow (Obstack, "@@" ); break;  \
-      case '[': obstack_sgrow (Obstack, "@{" ); break;  \
-      case ']': obstack_sgrow (Obstack, "@}" ); break;  \
-      default: obstack_1grow (Obstack, *p__); break;    \
-      }                                                 \
-} while(0)
+  do {                                                          \
+    obstack_escape (&muscle_obstack, Value);                    \
+    obstack_1grow (&muscle_obstack, 0);                         \
+    muscle_insert (Key, obstack_finish (&muscle_obstack));      \
+  } while (0)
 
 #define MUSCLE_INSERT_C_STRING(Key, Value)                      \
-do {                                                            \
-  MUSCLE_OBSTACK_SGROW (&muscle_obstack,                        \
-                        quotearg_style (c_quoting_style,        \
-                                        Value));                \
-  obstack_1grow (&muscle_obstack, 0);                           \
-  muscle_insert (Key, obstack_finish (&muscle_obstack));        \
-} while(0)
+  do {                                                          \
+    obstack_escape (&muscle_obstack,                            \
+                    quotearg_style (c_quoting_style, Value));   \
+    obstack_1grow (&muscle_obstack, 0);                         \
+    muscle_insert (Key, obstack_finish (&muscle_obstack));      \
+  } while (0)
 
 /* Append VALUE to the current value of KEY.  If KEY did not already
    exist, create it.  Use MUSCLE_OBSTACK.  De-allocate the previously
@@ -113,14 +98,6 @@ void muscle_code_grow (const char *key, const char *value, 
location loc);
 void muscle_pair_list_grow (const char *muscle,
                             const char *a1, const char *a2);
 
-/* In the format `[[file_name:line.column]], [[file_name:line.column]]', append
-   LOC to MUSCLE.  Use digraphs for special characters in each file name.  */
-void muscle_location_grow (char const *key, location loc);
-
-/* In the format `file_name:line.column', append BOUND to MUSCLE.  Use digraphs
-   for special characters in the file name.  */
-void muscle_boundary_grow (char const *key, boundary bound);
-
 /* Grow KEY for the occurrence of the name USER_NAME at LOC appropriately for
    use with b4_check_user_names in ../data/bison.m4.  USER_NAME is not escaped
    with digraphs, so it must not contain `[' or `]'.  */
diff --git a/src/output.c b/src/output.c
index 26a6e7f..c227be2 100644
--- a/src/output.c
+++ b/src/output.c
@@ -108,29 +108,39 @@ 
GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_symbol_number_table, symbol_number)
 GENERATE_MUSCLE_INSERT_TABLE(muscle_insert_state_number_table, state_number)
 
 
-/*--------------------------------------------------------------------.
-| Print to OUT a representation of STRING escaped both for C and M4.  |
-`--------------------------------------------------------------------*/
+/*----------------------------------------------------------------.
+| Print to OUT a representation of CP quoted and escaped for M4.  |
+`----------------------------------------------------------------*/
 
 static void
-escaped_output (FILE *out, char const *string)
+quoted_output (FILE *out, char const *cp)
 {
-  char const *p;
   fprintf (out, "[[");
 
-  for (p = quotearg_style (c_quoting_style, string); *p; p++)
-    switch (*p)
+  for (; *cp; cp++)
+    switch (*cp)
       {
       case '$': fputs ("$][", out); break;
       case '@': fputs ("@@",  out); break;
       case '[': fputs ("@{",  out); break;
       case ']': fputs ("@}",  out); break;
-      default: fputc (*p, out); break;
+      default:  fputc (*cp,   out); break;
       }
 
   fprintf (out, "]]");
 }
 
+/*----------------------------------------------------------------.
+| Print to OUT a representation of STRING quoted and escaped both |
+| for C and M4.                                                   |
+`----------------------------------------------------------------*/
+
+static void
+string_output (FILE *out, char const *string)
+{
+  quoted_output (out, quotearg_style (c_quoting_style, string));
+}
+
 
 /*------------------------------------------------------------------.
 | Prepare the muscles related to the symbols: translate, tname, and |
@@ -174,7 +184,7 @@ prepare_symbols (void)
 
         if (i)
           obstack_1grow (&format_obstack, ' ');
-        MUSCLE_OBSTACK_SGROW (&format_obstack, cp);
+        obstack_escape (&format_obstack, cp);
         free (cp);
         obstack_1grow (&format_obstack, ',');
         j += width;
@@ -355,7 +365,7 @@ user_actions_output (FILE *out)
         fprintf (out, "b4_%scase(%d, [b4_syncline(%d, ",
                  rules[r].is_predicate ? "predicate_" : "",
                  r + 1, rules[r].action_location.start.line);
-        escaped_output (out, rules[r].action_location.start.file);
+        string_output (out, rules[r].action_location.start.file);
         fprintf (out, ")\n[    %s]])\n\n", rules[r].action);
       }
   fputs ("])\n\n", out);
diff --git a/src/parse-gram.y b/src/parse-gram.y
index 9d9d1a4..5189699 100644
--- a/src/parse-gram.y
+++ b/src/parse-gram.y
@@ -389,10 +389,15 @@ grammar_declaration:
     }
 | code_props_type "{...}" generic_symlist
     {
-      symbol_list *list;
-      for (list = $3; list; list = list->next)
-        symbol_list_code_props_set (list, $1, @2, $2);
-      symbol_list_free ($3);
+      code_props code;
+      code_props_symbol_action_init (&code, $2, @2);
+      code_props_translate_code (&code);
+      {
+        symbol_list *list;
+        for (list = $3; list; list = list->next)
+          symbol_list_code_props_set (list, $1, &code);
+        symbol_list_free ($3);
+      }
     }
 | "%default-prec"
     {
diff --git a/src/scan-code.h b/src/scan-code.h
index 2dfb170..348d9e3 100644
--- a/src/scan-code.h
+++ b/src/scan-code.h
@@ -45,7 +45,10 @@ typedef struct code_props {
     CODE_PROPS_SYMBOL_ACTION, CODE_PROPS_RULE_ACTION
   } kind;
 
-  /** \c NULL iff \c code_props::kind is \c CODE_PROPS_NONE.  */
+  /**
+   * \c NULL iff \c code_props::kind is \c CODE_PROPS_NONE.
+   * Memory is allocated in an obstack freed elsewhere.
+   */
   char const *code;
   /** Undefined iff \c code_props::code is \c NULL.  */
   location location;
diff --git a/src/scan-code.l b/src/scan-code.l
index f6efeff..2fe880e 100644
--- a/src/scan-code.l
+++ b/src/scan-code.l
@@ -47,6 +47,9 @@ YY_DECL;
 
 #define YY_USER_ACTION  location_compute (loc, &loc->end, yytext, yyleng);
 
+static char *fetch_type_name (char *cp, char const **type_name,
+                              location dollar_loc);
+
 static void handle_action_dollar (symbol_list *rule, char *cp,
                                   location dollar_loc);
 static void handle_action_at (symbol_list *rule, char *cp, location at_loc);
@@ -96,11 +99,15 @@ ref      -?[0-9]+|{id}|"["{id}"]"|"$"
   int braces_level = 0;
 
   /* Whether a semicolon is probably needed.
-     The heuristic is that a semicolon is not needed after '{', '}', ';',
-     or a C preprocessor directive, and that whitespaces and comments
-     do not affect this flag.
-     Note that '{' does not need a semicolon because of '{}'.
-     A semicolon may be needed before a cpp direcive, but don't bother.  */
+
+     The heuristic is that a semicolon is not needed after '{', '}',
+     ';', or a C preprocessor directive, and that whitespaces and
+     comments do not affect this flag.  Note that '{' does not need a
+     semicolon because of '{}'.  A semicolon may be needed before a
+     cpp directive, but don't bother.
+
+     While it is maintained in several start-conditions (factoring
+     opportunities), it is meaningful only for SC_RULE_ACTION. */
   bool need_semicolon = false;
 
   /* Whether in a C preprocessor directive.  Don't use a start condition
@@ -158,7 +165,8 @@ ref      -?[0-9]+|{id}|"["{id}"]"|"$"
 }
 
 
-<SC_RULE_ACTION,SC_SYMBOL_ACTION>{
+<SC_RULE_ACTION,SC_SYMBOL_ACTION>
+{
   "'" {
     STRING_GROW;
     BEGIN SC_CHARACTER;
@@ -177,42 +185,31 @@ ref      -?[0-9]+|{id}|"["{id}"]"|"$"
     STRING_GROW;
     BEGIN SC_LINE_COMMENT;
   }
+  address@hidden  {
+    complain_at (*loc, Wother, _("stray '%s'"), yytext);
+    obstack_escape (&obstack_for_string, yytext);
+    need_semicolon = true;
+  }
+  [\[\]]  {
+    obstack_escape (&obstack_for_string, yytext);
+    need_semicolon = true;
+  }
 }
 
 <SC_RULE_ACTION>
 {
   "$"("<"{tag}">")?{ref}  {
-    ref_tail_fields = 0;
+    ref_tail_fields = NULL;
     handle_action_dollar (self->rule, yytext, *loc);
-    if (ref_tail_fields) {
+    if (ref_tail_fields)
       obstack_sgrow (&obstack_for_string, ref_tail_fields);
-    }
     need_semicolon = true;
   }
   "@"{ref} {
-    ref_tail_fields = 0;
+    ref_tail_fields = NULL;
     handle_action_at (self->rule, yytext, *loc);
-    if (ref_tail_fields) {
+    if (ref_tail_fields)
       obstack_sgrow (&obstack_for_string, ref_tail_fields);
-    }
-    need_semicolon = true;
-  }
-  "$"  {
-    complain_at (*loc, Wother, _("stray '$'"));
-    obstack_sgrow (&obstack_for_string, "$][");
-    need_semicolon = true;
-  }
-  "@"  {
-    complain_at (*loc, Wother, _("stray '@'"));
-    obstack_sgrow (&obstack_for_string, "@@");
-    need_semicolon = true;
-  }
-  "["  {
-    obstack_sgrow (&obstack_for_string, "@{");
-    need_semicolon = true;
-  }
-  "]"  {
-    obstack_sgrow (&obstack_for_string, "@}");
     need_semicolon = true;
   }
 
@@ -253,8 +250,12 @@ ref      -?[0-9]+|{id}|"["{id}"]"|"$"
 
 <SC_SYMBOL_ACTION>
 {
-  "$$" {
-    obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar[");
+  "$"("<"{tag}">")?"$" {
+    const char *type_name = NULL;
+    fetch_type_name (yytext + 1, &type_name, *loc)[-1] = 0;
+    obstack_sgrow (&obstack_for_string, "]b4_dollar_dollar(");
+    obstack_quote (&obstack_for_string, type_name);
+    obstack_sgrow (&obstack_for_string, ")[");
     self->is_value_used = true;
   }
   "@$" {
@@ -264,29 +265,17 @@ ref      -?[0-9]+|{id}|"["{id}"]"|"$"
 }
 
 
-  /*-----------------------------------------.
-  | Escape M4 quoting characters in C code.  |
-  `-----------------------------------------*/
-
 <*>
 {
-  \$    obstack_sgrow (&obstack_for_string, "$][");
-  \@    obstack_sgrow (&obstack_for_string, "@@");
-  \[    obstack_sgrow (&obstack_for_string, "@{");
-  \]    obstack_sgrow (&obstack_for_string, "@}");
-}
-
-  /*-----------------------------------------------------.
-  | By default, grow the string obstack with the input.  |
-  `-----------------------------------------------------*/
+  /* Escape M4 quoting characters in C code.  */
+  address@hidden    obstack_escape (&obstack_for_string, yytext);
 
-<*>.|\n STRING_GROW;
+  /* By default, grow the string obstack with the input.  */
+  .|\n        STRING_GROW;
 
  /* End of processing. */
-<*><<EOF>>       {
-                   STRING_FINISH;
-                   return last_string;
-                 }
+  <<EOF>>     STRING_FINISH; return last_string;
+}
 
 %%
 
@@ -335,7 +324,7 @@ typedef struct
    not visible from current midrule. */
 #define VARIANT_NOT_VISIBLE_FROM_MIDRULE (1 << 2)
 
-static variant *variant_table = 0;
+static variant *variant_table = NULL;
 static unsigned variant_table_size = 0;
 static unsigned variant_count = 0;
 
@@ -357,7 +346,7 @@ static void
 variant_table_free (void)
 {
   free (variant_table);
-  variant_table = 0;
+  variant_table = NULL;
   variant_table_size = variant_count = 0;
 }
 
@@ -681,6 +670,32 @@ parse_ref (char *cp, symbol_list *rule, int rule_length,
 int max_left_semantic_context = 0;
 
 
+/* If CP points to a typename (i.e., <.*?>), set TYPE_NAME to its
+   beginning (i.e., after the opening "<", and return the pointer
+   immediately after it.  */
+
+static
+char *
+fetch_type_name (char *cp, char const **type_name,
+                 location dollar_loc)
+{
+  if (*cp == '<')
+    {
+      *type_name = ++cp;
+      while (*cp != '>')
+        ++cp;
+
+      /* The '>' symbol will be later replaced by '\0'. Original
+         'text' is needed for error messages. */
+      ++cp;
+      if (untyped_var_seen)
+        complain_at (dollar_loc, complaint,
+                     _("explicit type given in untyped grammar"));
+      tag_seen = true;
+    }
+  return cp;
+}
+
 /*------------------------------------------------------------------.
 | TEXT is pointing to a wannabee semantic value (i.e., a '$').      |
 |                                                                   |
@@ -694,7 +709,6 @@ handle_action_dollar (symbol_list *rule, char *text, 
location dollar_loc)
 {
   char const *type_name = NULL;
   char *cp = text + 1;
-  char *gt_ptr = 0;
   symbol_list *effective_rule;
   int effective_rule_length;
   int n;
@@ -711,27 +725,14 @@ handle_action_dollar (symbol_list *rule, char *text, 
location dollar_loc)
     }
 
   /* Get the type name if explicit. */
-  if (*cp == '<')
-    {
-      type_name = ++cp;
-      while (*cp != '>')
-        ++cp;
-
-      /* The '>' symbol will be later replaced by '\0'. Original
-         'text' is needed for error messages. */
-      gt_ptr = cp;
-      ++cp;
-      if (untyped_var_seen)
-        complain_at (dollar_loc, complaint,
-                  _("explicit type given in untyped grammar"));
-      tag_seen = true;
-    }
+  cp = fetch_type_name (cp, &type_name, dollar_loc);
 
   n = parse_ref (cp, effective_rule, effective_rule_length,
                  rule->midrule_parent_rhs_index, text, dollar_loc, '$');
 
-  if (gt_ptr)
-    *gt_ptr = '\0';
+  /* End type_name. */
+  if (type_name)
+    cp[-1] = '\0';
 
   switch (n)
     {
@@ -759,11 +760,11 @@ handle_action_dollar (symbol_list *rule, char *text, 
location dollar_loc)
             }
           else
             untyped_var_seen = true;
-          type_name = "";
         }
 
-      obstack_fgrow1 (&obstack_for_string,
-                      "]b4_lhs_value([%s])[", type_name);
+      obstack_sgrow (&obstack_for_string, "]b4_lhs_value(");
+      obstack_quote (&obstack_for_string, type_name);
+      obstack_sgrow (&obstack_for_string, ")[");
       rule->action_props.is_value_used = true;
       break;
 
@@ -781,12 +782,12 @@ handle_action_dollar (symbol_list *rule, char *text, 
location dollar_loc)
                          quote (effective_rule->content.sym->tag));
           else
             untyped_var_seen = true;
-          type_name = "";
         }
 
-      obstack_fgrow3 (&obstack_for_string,
-                      "]b4_rhs_value(%d, %d, [%s])[",
-                      effective_rule_length, n, type_name);
+      obstack_fgrow2 (&obstack_for_string,
+                      "]b4_rhs_value(%d, %d, ", effective_rule_length, n);
+      obstack_quote (&obstack_for_string, type_name);
+      obstack_sgrow (&obstack_for_string, ")[");
       if (n > 0)
         symbol_list_n_get (effective_rule, n)->action_props.is_value_used =
           true;
diff --git a/src/scan-skel.l b/src/scan-skel.l
index 17382a7..b79e583 100644
--- a/src/scan-skel.l
+++ b/src/scan-skel.l
@@ -72,8 +72,8 @@ static void fail_for_invalid_at (char const *at);
 "@@" fputc ('@', yyout);
 "@{" fputc ('[', yyout);
 "@}" fputc (']', yyout);
-"@`" /* Empty.  Used by b4_cat in ../data/bison.m4.  */
address@hidden  /* Likewise.  */
+"@`" continue;  /* Used by b4_cat in ../data/bison.m4.  */
address@hidden  continue;
 
 "@oline@"  fprintf (yyout, "%d", out_lineno + 1);
 "@ofile@"  QPUTS (outname);
@@ -91,7 +91,7 @@ static void fail_for_invalid_at (char const *at);
 \n         out_lineno++; ECHO;
 address@hidden    ECHO;
 
-<INITIAL><<EOF>> {
+<<EOF>> {
   if (outname)
     {
       free (outname);
@@ -100,15 +100,15 @@ static void fail_for_invalid_at (char const *at);
   return EOF;
 }
 
-<SC_AT_DIRECTIVE_ARGS>{
-  address@hidden { STRING_GROW; }
+<SC_AT_DIRECTIVE_ARGS>
+{
+  address@hidden  STRING_GROW;
 
-  "@@" { obstack_1grow (&obstack_for_string, '@'); }
-  "@{" { obstack_1grow (&obstack_for_string, '['); }
-  "@}" { obstack_1grow (&obstack_for_string, ']'); }
-  "@`" /* Empty.  Useful for starting an argument
-          that begins with whitespace. */
-  @\n  /* Empty.  */
+  "@@"   obstack_1grow (&obstack_for_string, '@');
+  "@{"   obstack_1grow (&obstack_for_string, '[');
+  "@}"   obstack_1grow (&obstack_for_string, ']');
+  "@`"   continue; /* For starting an argument that begins with whitespace. */
+  @\n    continue;
 
   @[,)] {
     if (at_directive_argc >= AT_DIRECTIVE_ARGC_MAX)
@@ -131,15 +131,17 @@ static void fail_for_invalid_at (char const *at);
       }
   }
 
-  @.? { fail_for_invalid_at (yytext); }
+  @.?  fail_for_invalid_at (yytext);
 }
 
-<SC_AT_DIRECTIVE_SKIP_WS>{
-  [ \t\r\n]
+<SC_AT_DIRECTIVE_SKIP_WS>
+{
+  [ \t\r\n]    continue;
   . { yyless (0); BEGIN SC_AT_DIRECTIVE_ARGS; }
 }
 
-<SC_AT_DIRECTIVE_ARGS,SC_AT_DIRECTIVE_SKIP_WS>{
+<SC_AT_DIRECTIVE_ARGS,SC_AT_DIRECTIVE_SKIP_WS>
+{
   <<EOF>> {
     complain (fatal, _("unclosed %s directive in skeleton"),
               at_directive_argv[0]);
diff --git a/src/symlist.c b/src/symlist.c
index bd9f3c2..2bbedb5 100644
--- a/src/symlist.c
+++ b/src/symlist.c
@@ -191,15 +191,12 @@ symbol_list_null (symbol_list *node)
 
 void
 symbol_list_code_props_set (symbol_list *node, code_props_type kind,
-                            location loc, char const *code)
+                            code_props const *cprops)
 {
-  code_props cprops;
-  code_props_symbol_action_init (&cprops, code, loc);
-  code_props_translate_code (&cprops);
   switch (node->content_type)
     {
       case SYMLIST_SYMBOL:
-        symbol_code_props_set (node->content.sym, kind, &cprops);
+        symbol_code_props_set (node->content.sym, kind, cprops);
         if (node->content.sym->status == undeclared)
           node->content.sym->status = used;
         break;
@@ -207,7 +204,7 @@ symbol_list_code_props_set (symbol_list *node, 
code_props_type kind,
         semantic_type_code_props_set
           (semantic_type_get (node->content.sem_type->tag,
                               &node->content.sem_type->location),
-           kind, &cprops);
+           kind, cprops);
         if (node->content.sem_type->status == undeclared)
           node->content.sem_type->status = used;
         break;
diff --git a/src/symlist.h b/src/symlist.h
index dd63408..03ffebe 100644
--- a/src/symlist.h
+++ b/src/symlist.h
@@ -113,9 +113,8 @@ uniqstr symbol_list_n_type_name_get (symbol_list *l, 
location loc, int n);
 /* Check whether the node is a border element of a rule. */
 bool symbol_list_null (symbol_list *node);
 
-/** Set the \c \%destructor or \c \%printer for \c node as \c code at
-    \c loc.  */
+/** Set the \c \%destructor or \c \%printer for \c node as \c cprops.  */
 void symbol_list_code_props_set (symbol_list *node, code_props_type kind,
-                                 location loc, char const *code);
+                                 code_props const *cprops);
 
 #endif /* !SYMLIST_H_ */
diff --git a/src/symtab.h b/src/symtab.h
index 9377800..49eebd4 100644
--- a/src/symtab.h
+++ b/src/symtab.h
@@ -92,8 +92,14 @@ struct symbol
   /** The location of its first occurrence.  */
   location location;
 
-  /** Its \c \%type.  */
+  /** Its \c \%type.
+
+      Beware that this is the type_name as was entered by the user,
+      including silly things such as "]" if she entered "%token <]> t".
+      Therefore, when outputting type_name to M4, be sure to escape it
+      into "@}".  See quoted_output for instance.  */
   uniqstr type_name;
+
   /** Its \c \%type's location.  */
   location type_location;
 
@@ -101,9 +107,9 @@ struct symbol
       symbol.
 
       Access this field only through <tt>symbol</tt>'s interface functions. For
-      Example, if <tt>symbol::destructor = NULL</tt> (resp. <tt>symbol::printer
+      example, if <tt>symbol::destructor = NULL</tt> (resp. <tt>symbol::printer
       = NULL</tt>), a default \c \%destructor (resp. \%printer) or a per-type
-      \c symbol_destructor_printer_get will compute the corect one. */
+      \c symbol_destructor_printer_get will compute the correct one. */
   code_props props[CODE_PROPS_SIZE];
 
   symbol_number number;
diff --git a/src/system.h b/src/system.h
index 5613a92..1d0af8f 100644
--- a/src/system.h
+++ b/src/system.h
@@ -177,32 +177,75 @@ typedef size_t uintptr_t;
   obstack_grow (Obs, Str, strlen (Str))
 
 #define obstack_fgrow1(Obs, Format, Arg1)       \
-do {                                            \
-  char buf[4096];                               \
-  sprintf (buf, Format, Arg1);                  \
-  obstack_grow (Obs, buf, strlen (buf));        \
-} while (0)
+  do {                                          \
+    char buf[4096];                             \
+    sprintf (buf, Format, Arg1);                \
+    obstack_grow (Obs, buf, strlen (buf));      \
+  } while (0)
 
 #define obstack_fgrow2(Obs, Format, Arg1, Arg2) \
-do {                                            \
-  char buf[4096];                               \
-  sprintf (buf, Format, Arg1, Arg2);            \
-  obstack_grow (Obs, buf, strlen (buf));        \
-} while (0)
+  do {                                          \
+    char buf[4096];                             \
+    sprintf (buf, Format, Arg1, Arg2);          \
+    obstack_grow (Obs, buf, strlen (buf));      \
+  } while (0)
 
 #define obstack_fgrow3(Obs, Format, Arg1, Arg2, Arg3)   \
-do {                                                    \
-  char buf[4096];                                       \
-  sprintf (buf, Format, Arg1, Arg2, Arg3);              \
-  obstack_grow (Obs, buf, strlen (buf));                \
-} while (0)
+  do {                                                  \
+    char buf[4096];                                     \
+    sprintf (buf, Format, Arg1, Arg2, Arg3);            \
+    obstack_grow (Obs, buf, strlen (buf));              \
+  } while (0)
 
 #define obstack_fgrow4(Obs, Format, Arg1, Arg2, Arg3, Arg4)     \
-do {                                                            \
-  char buf[4096];                                               \
-  sprintf (buf, Format, Arg1, Arg2, Arg3, Arg4);                \
-  obstack_grow (Obs, buf, strlen (buf));                        \
-} while (0)
+  do {                                                          \
+    char buf[4096];                                             \
+    sprintf (buf, Format, Arg1, Arg2, Arg3, Arg4);              \
+    obstack_grow (Obs, buf, strlen (buf));                      \
+  } while (0)
+
+
+/* Output Str escaped for our postprocessing (i.e., escape M4 special
+   characters).
+
+   For instance "[foo]" -> "@address@hidden", "$$" -> "$][$][". */
+
+# define obstack_escape(Obs, Str)                       \
+  do {                                                  \
+    char const *p__;                                    \
+    for (p__ = Str; *p__; p__++)                        \
+      switch (*p__)                                     \
+        {                                               \
+        case '$': obstack_sgrow (Obs, "$]["); break;    \
+        case '@': obstack_sgrow (Obs, "@@" ); break;    \
+        case '[': obstack_sgrow (Obs, "@{" ); break;    \
+        case ']': obstack_sgrow (Obs, "@}" ); break;    \
+        default:  obstack_1grow (Obs, *p__ ); break;    \
+        }                                               \
+  } while (0)
+
+
+/* Output Str both quoted for M4 (i.e., embed in [[...]]), and escaped
+   for our postprocessing (i.e., escape M4 special characters).  If
+   Str is empty (or NULL), output "[]" instead of "[[]]" as it make M4
+   programming easier (m4_ifval can be used).
+
+   For instance "[foo]" -> "address@hidden@}]]", "$$" -> "[[$][$][]]". */
+
+# define obstack_quote(Obs, Str)                \
+  do {                                          \
+    char const* obstack_quote_p = Str;          \
+    if (obstack_quote_p && obstack_quote_p[0])  \
+      {                                         \
+        obstack_sgrow (Obs, "[[");              \
+        obstack_escape (Obs, obstack_quote_p);  \
+        obstack_sgrow (Obs, "]]");              \
+      }                                         \
+    else                                        \
+      obstack_sgrow (Obs, "[]");                \
+  } while (0)
+
+
 
 
 
@@ -218,10 +261,6 @@ do {                                                       
     \
 # define TAB_EXT ".tab"
 #endif
 
-#ifndef DEFAULT_TMPDIR
-# define DEFAULT_TMPDIR "/tmp"
-#endif
-
 
 
 /*---------------------.
diff --git a/tests/actions.at b/tests/actions.at
index 120c401..bde0961 100644
--- a/tests/actions.at
+++ b/tests/actions.at
@@ -1255,6 +1255,143 @@ AT_CHECK_ACTION_LOCATIONS([[%destructor]])
 AT_CHECK_ACTION_LOCATIONS([[%printer]])
 
 
+## ------------------------- ##
+## Qualified $$ in actions.  ##
+## ------------------------- ##
+
+# Check that we can used qualified $$ (v.g., $<type>$) not only in
+# rule actions, but also where $$ is valid: %printer and %destructor.
+#
+# FIXME: Not actually checking %desctructor, but it's the same code as
+# %printer...
+#
+# To do that, use a semantic value that has two fields (sem_type),
+# declare symbols to have only one of these types (INT, float), and
+# use $<type>$ to get the other one.  Including for symbols that are
+# not typed (UNTYPED).
+
+m4_pushdef([AT_TEST],
+[AT_SETUP([[Qualified $$ in actions: $1]])
+
+AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "$1"])
+
+AT_DATA_GRAMMAR([[input.y]],
+[[%skeleton "$1"
+%defines   // FIXME: Mandated by lalr1.cc in Bison 2.6.
+%locations // FIXME: Mandated by lalr1.cc in Bison 2.6.
+%debug
+%code requires
+{
+# include <stdio.h>
+
+  typedef struct sem_type
+  {
+    int ival;
+    float fval;
+  } sem_type;
+
+# define YYSTYPE sem_type
+
+#ifdef __cplusplus
+# include <iostream>
+  static void
+  report (std::ostream& yyo, int ival, float fval)
+  {
+    yyo << "ival: " << ival << ", fval: " <<  fval;
+  }
+#else
+  static void
+  report (FILE* yyo, int ival, float fval)
+  {
+    fprintf (yyo, "ival: %d, fval: %1.1f", ival, fval);
+  }
+#endif
+}
+
+%code
+{
+  ]AT_YYERROR_DECLARE[
+  ]AT_YYLEX_DECLARE[
+}
+
+%token UNTYPED
+%token <ival> INT
+%type <fval> float
+%printer { report (yyo, $$,       $<fval>$); } <ival>;
+%printer { report (yyo, $<ival>$, $$      ); } <fval>;
+%printer { report (yyo, $<ival>$, $<fval>$); } <>;
+
+]AT_SKEL_CC_IF([[
+/* The lalr1.cc skeleton, for backward compatibility, defines
+   a constructor for position that initializes the filename.  The
+   glr.cc skeleton does not (and in fact cannot: location/position
+   are stored in a union, from which objects with constructors are
+   excluded in C++). */
+%initial-action {
+  @$.initialize ();
+}
+]])[
+
+%initial-action
+{
+  $<ival>$ = 42;
+  $<fval>$ = 4.2;
+}
+
+%%
+float: UNTYPED INT
+{
+  $$       = $<fval>1 + $<fval>2;
+  $<ival>$ = $<ival>1 + $][2;
+};
+%%
+]AT_YYERROR_DEFINE[
+]AT_YYLEX_DEFINE(AT_SKEL_CC_IF([[{yy::parser::token::UNTYPED,
+                                 yy::parser::token::INT,
+                                  EOF}]],
+                               [[{UNTYPED, INT, EOF}]]),
+                 [AT_VAL.ival = toknum * 10; AT_VAL.fval = toknum / 10.0;])[
+int
+main (void)
+{]AT_SKEL_CC_IF([[
+  yy::parser p;
+  p.set_debug_level(1);
+  return p.parse ();]], [[
+  yydebug = 1;
+  return yyparse ();]])[
+}
+]])
+
+AT_FULL_COMPILE([[input]])
+AT_PARSER_CHECK([./input], 0, [], [stderr])
+# Don't be too picky on the traces, GLR is not exactly the same.  Keep
+# only the lines from the printer.
+#
+# Don't care about locations.  FIXME: remove their removal when Bison
+# supports C++ without locations.
+AT_CHECK([[sed -ne 's/([-0-9.]*: /(/;/ival:/p' stderr]], 0,
+[[Reading a token: Next token is token UNTYPED (ival: 10, fval: 0.1)
+Shifting token UNTYPED (ival: 10, fval: 0.1)
+Reading a token: Next token is token INT (ival: 20, fval: 0.2)
+Shifting token INT (ival: 20, fval: 0.2)
+   $][1 = token UNTYPED (ival: 10, fval: 0.1)
+   $][2 = token INT (ival: 20, fval: 0.2)
+-> $$ = nterm float (ival: 30, fval: 0.3)
+Cleanup: popping nterm float (ival: 30, fval: 0.3)
+]])
+
+AT_BISON_OPTION_POPDEFS
+
+AT_CLEANUP
+])
+
+AT_TEST([yacc.c])
+AT_TEST([glr.c])
+AT_TEST([lalr1.cc])
+AT_TEST([glr.cc])
+
+m4_popdef([AT_TEST])
+
 ## ----------------------------------------------- ##
 ## Fix user actions without a trailing semicolon.  ##
 ## ----------------------------------------------- ##
diff --git a/tests/input.at b/tests/input.at
index 71505cf..d6f2084 100644
--- a/tests/input.at
+++ b/tests/input.at
@@ -1491,3 +1491,102 @@ AT_TEST([%define api.prefix foo], [-p bar], 
[input.y:1.9-18])
 m4_popdef([AT_TEST])
 
 AT_CLEANUP
+
+
+## -------------- ##
+## Stray $ or @.  ##
+## -------------- ##
+
+AT_SETUP([[Stray $ or @]])
+
+# Give %printer and %destructor "<*> exp TOK" instead of "<*>" to
+# check that the warnings are reported once, not three times.
+
+AT_DATA_GRAMMAR([[input.y]],
+[[%type <TYPE> exp
+%token <TYPE> TOK TOK2
+%destructor     { $%; @%; } <*> exp TOK;
+%initial-action { $%; @%; };
+%printer        { $%; @%; } <*> exp TOK;
+%%
+exp: TOK        { $%; @%; $$ = $1; };
+]])
+
+AT_BISON_CHECK([[input.y]], 0, [],
+[[input.y:11.19: warning: stray '$' [-Wother]
+input.y:11.23: warning: stray '@' [-Wother]
+input.y:12.19: warning: stray '$' [-Wother]
+input.y:12.23: warning: stray '@' [-Wother]
+input.y:13.19: warning: stray '$' [-Wother]
+input.y:13.23: warning: stray '@' [-Wother]
+input.y:15.19: warning: stray '$' [-Wother]
+input.y:15.23: warning: stray '@' [-Wother]
+]])
+
+AT_CLEANUP
+
+
+
+## ---------------- ##
+## Code injection.  ##
+## ---------------- ##
+
+
+AT_SETUP([[Code injection]])
+
+m4_pattern_allow([^m4_errprintn$])
+
+# AT_TEST([MACRO])
+# ----------------
+# Try to have MACRO be run by bison.
+m4_pushdef([AT_TEST],
+[AT_DATA([[input.y]],
+[[%type <$1(DEAD %type)> exp
+%token <$1(DEAD %token)> a
+%token b
+%initial-action
+{
+  $$;
+  $<$1(DEAD %initial-action)>$
+};
+%printer
+{
+  $$
+  $<$1(DEAD %printer)>$
+} <> <*>;
+%lex-param
+{
+  $1(DEAD %lex-param)
+};
+%parse-param
+{
+  $1(DEAD %parse-param)
+};
+%%
+exp:
+  a a[name] b
+  {
+    $$;
+    $][1;
+    $<$1(DEAD action 1)>$
+    $<$1(DEAD action 2)>1
+    $<$1(DEAD action 3)>name
+    $<$1(DEAD action 4)>0
+    ;
+  };
+]])
+
+# FIXME: Provide a means to iterate over all the skeletons.
+AT_BISON_CHECK([[-d               input.y]])
+AT_BISON_CHECK([[-d -S glr.c      input.y]])
+AT_BISON_CHECK([[-d -S lalr1.cc   input.y]])
+AT_BISON_CHECK([[-d -S glr.cc     input.y]])
+AT_BISON_CHECK([[   -S lalr1.java input.y]])
+])
+
+AT_TEST([m4_errprintn])
+AT_TEST([@:>@m4_errprintn])
+
+m4_popdef([AT_TEST])
+
+AT_CLEANUP
diff --git a/tests/local.at b/tests/local.at
index c79fc08..4499e13 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -338,7 +338,7 @@ static
   static size_t toknum = 0;
   int res;
   ]AT_USE_LEX_ARGS[;
-  assert (toknum < sizeof input);
+  assert (toknum < sizeof input / sizeof input[0]);
   res = input[toknum++];
   ]$2[;]AT_LOCATION_IF([[
   ]AT_LOC_FIRST_LINE[ = ]AT_LOC_LAST_LINE[ = 1;
diff --git a/tests/synclines.at b/tests/synclines.at
index 8d742d5..7e7122f 100644
--- a/tests/synclines.at
+++ b/tests/synclines.at
@@ -27,6 +27,11 @@ AT_BANNER([[User Actions.]])
 # errors.
 m4_define([AT_SYNCLINES_COMPILE],
 [AT_CHECK([$CC $CFLAGS $CPPFLAGS -c $1], [ignore], [], [stderr])
+
+# Transform stderr into something like this:
+#
+#   input.y:4: #error "4"
+#
 # In case GCC displays column information, strip it down.
 #
 #   input.y:4:2: #error "4"    or
@@ -42,6 +47,11 @@ m4_define([AT_SYNCLINES_COMPILE],
 # =>
 #   input.y:4: #error "8"
 #
+# The message may include a caret-error:
+#
+#   input.y:1:2: error: #error "1"
+#    #error "1"
+#     ^
 #
 # And possibly distcc adds its bits.
 #
@@ -54,12 +64,15 @@ m4_define([AT_SYNCLINES_COMPILE],
 #   distcc[35882] (dcc_connect_by_name) ERROR: failed to look up host 
"chrisimac": Unknown host
 #   distcc[35882] Warning: failed to distribute input.c to chrisimac/4, 
running locally instead
 
-AT_CHECK([[sed -e '/^distcc\[[0-9]*\] /d'                            \
-               -e 's/^\([^:]*:[^:.]*\)[.:][^:]*:\(.*\)$/\1:\2/'      \
-               -e 's/^\([^:]*:[^:]*:\)address@hidden:@]*\( @%:@error\)/\1\2/'  
\
-               -e "/^[^:]*: In function '[^\']*':$/d"                \
-            stderr]],
-         0, [stdout])
+AT_CHECK([[perl -p -0777 -f - stderr <<\EOF
+ s/^distcc\[\d+\] .*\n//gm;
+ s/^([^:]*:\d+)[.:][^:]*:(.*)$/$][1:$][2/gm;
+ s/^([^:]*:\d+:)[^#]*( #error)/$][1$][2/gm;
+ s/^[^:]*: In function '[^']*':\n//gm;
+ s/^\ +#error.*\n\ *\^\n//gm;
+EOF
+]],
+ 0, [stdout])
 ])
 
 # AT_TEST_SYNCLINE(TITLE, INPUT, ERROR-MSG)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]