]> git.saurik.com Git - bison.git/commitdiff
Merge remote-tracking branch 'origin/maint'
authorAkim Demaille <akim@lrde.epita.fr>
Fri, 27 Jul 2012 14:22:45 +0000 (16:22 +0200)
committerAkim Demaille <akim@lrde.epita.fr>
Fri, 27 Jul 2012 14:35:49 +0000 (16:35 +0200)
* 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

29 files changed:
1  2 
NEWS
THANKS
TODO
configure.ac
data/bison.m4
data/c-like.m4
data/c.m4
data/glr.c
data/glr.cc
data/java.m4
data/lalr1.cc
data/lalr1.java
data/local.mk
data/yacc.c
doc/bison.texi
src/muscle-tab.c
src/muscle-tab.h
src/output.c
src/parse-gram.y
src/scan-code.h
src/scan-code.l
src/scan-skel.l
src/symlist.c
src/symlist.h
src/symtab.h
src/system.h
tests/actions.at
tests/input.at
tests/local.at

diff --cc NEWS
index fe23b5562002c0a727c27dc50c2795b2606e6fc3,b99290c92662b1e099ebf2e3a1955850136b9b6d..150068eefb4fa38751c625da35f7975b4afad436
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
  GNU Bison NEWS
  
 +* Noteworthy changes in release ?.? (????-??-??) [?]
 +
 +** Incompatible changes
 +
 +*** Obsolete features
 +
 +  Support for YYFAIL is removed, as announced since Bison 2.4.2.
 +  Support for yystype and yyltype (instead of YYSTYPE and YYLTYPE)
 +  is removed, as announced in Bison 1.875.
 +
 +** Warnings
 +
 +*** Warning categories are now displayed in warnings
 +
 +  For instance:
 +
 +  foo.y:4.6: warning: type clash on default action: <foo> != <bar> [-Wother]
 +
 +*** Useless semantic types
 +
 +  Bison now warns about useless (uninhabited) semantic types.  Since
 +  semantic types are not declared to Bison (they are defined in the opaque
 +  %union structure), it is %printer/%destructor directives about useless
 +  types that trigger the warning:
 +
 +    %token <type1> term
 +    %type  <type2> nterm
 +    %printer    {} <type1> <type3>
 +    %destructor {} <type2> <type4>
 +    %%
 +    nterm: term { $$ = $1; };
 +
 +    3.28-34: warning: type <type3> is used, but is not associated to any symbol
 +    4.28-34: warning: type <type4> is used, but is not associated to any symbol
 +
 +*** Undeclared symbols
 +
 +  Bison used to raise an error for %printer and %destructor directives for
 +  undefined symbols.
 +
 +    %printer    {} symbol1
 +    %destructor {} symbol2
 +    %%
 +    exp: "a";
 +
 +  This is now only a warning.
 +
 +*** Useless destructors or printers
 +
 +  Bison now warns about useless destructors or printers.  In the following
 +  example, the printer for <type1>, and the destructor for <type2> are
 +  useless: all symbols of <type1> (token1) already have a printer, and all
 +  symbols of type <type2> (token2) already have a destructor.
 +
 +    %token <type1> token1
 +           <type2> token2
 +           <type3> token3
 +           <type4> token4
 +    %printer    {} token1 <type1> <type3>
 +    %destructor {} token2 <type2> <type4>
 +
 +** Additional yylex/yyparse arguments
 +
 +  The new directive %param declares additional arguments to both yylex and
 +  yyparse.  The %lex-param, %parse-param, and %param directives support one
 +  or more arguments.  Instead of
 +
 +    %lex-param   {arg1_type *arg1}
 +    %lex-param   {arg2_type *arg2}
 +    %parse-param {arg1_type *arg1}
 +    %parse-param {arg2_type *arg2}
 +
 +  one may now declare
 +
 +    %param {arg1_type *arg1} {arg2_type *arg2}
 +
 +** Java skeleton improvements
 +
 +  The constants for token names were moved to the Lexer interface.
 +  Also, it is possible to add code to the parser's constructors using
 +  "%code init" and "%define init_throws".
 +
 +** C++ skeleton improvements
 +
 +  The C++ parser features a syntax_error exception, which can be
 +  thrown from the scanner or from user rules to raise syntax errors.
 +  This facilitates reporting errors caught in sub-functions (e.g.,
 +  rejecting too large integral literals from a conversion function
 +  used by the scanner, or rejecting invalid combinations from a
 +  factory invoked by the user actions).
 +
 +** Variable api.tokens.prefix
 +
 +  The variable api.tokens.prefix changes the way tokens are identified in
 +  the generated files.  This is especially useful to avoid collisions
 +  with identifiers in the target language.  For instance
 +
 +    %token FILE for ERROR
 +    %define api.tokens.prefix "TOK_"
 +    %%
 +    start: FILE for ERROR;
 +
 +  will generate the definition of the symbols TOK_FILE, TOK_for, and
 +  TOK_ERROR in the generated sources.  In particular, the scanner must
 +  use these prefixed token names, although the grammar itself still
 +  uses the short names (as in the sample rule given above).
 +
 +** Variable api.namespace
 +
 +  The "namespace" variable is renamed "api.namespace".  Backward
 +  compatibility is ensured, but upgrading is recommended.
 +
 +** Variable parse.error
 +
 +  The variable error controls the verbosity of error messages.  The
 +  use of the %error-verbose directive is deprecated in favor of
 +  %define parse.error "verbose".
 +
 +** Semantic predicates
 +
 +  The new, experimental, semantic-predicate feature allows actions of
 +  the form %?{ BOOLEAN-EXPRESSION }, which cause syntax errors (as for
 +  YYERROR) if the expression evaluates to 0, and are evaluated immediately
 +  in GLR parsers, rather than being deferred.  The result is that they
 +  allow the programmer to prune possible parses based on the values of
 +  run-time expressions.
 +
  * 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]
  
diff --cc THANKS
Simple merge
diff --cc TODO
index dfd6cfe3aaed6b173ff29f119b3f18ff3b6c21aa,4f628a20aa4f3577ffc1a004030587c4395e17a8..6457742526d82946cfa5954ea2a2bdf6e890f64f
--- 1/TODO
--- 2/TODO
+++ b/TODO
@@@ -1,4 -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 --cc configure.ac
Simple merge
diff --cc data/bison.m4
index 1db75a8e653bb0dd47eefd9935dee36a84681b7f,fac86f2a0132f8e0d67507dc0e77671023f3a7f2..f2126ef40b0fb081e0ea44df332757c2d953246c
@@@ -321,161 -239,8 +321,159 @@@ b4_define_flag_if([nondeterministic]
  b4_define_flag_if([token_table])        # Whether yytoken_table is demanded.
  b4_define_flag_if([yacc])               # Whether POSIX Yacc is emulated.
  
 -# yytoken_table is needed to support verbose errors.
 -b4_error_verbose_if([m4_define([b4_token_table_flag], [1])])
 +
 +## --------- ##
 +## Symbols.  ##
 +## --------- ##
 +
 +# In order to unify the handling of the various aspects of symbols
 +# (tag, type_name, whether terminal, etc.), bison.exe defines one
 +# macro per (token, field), where field can has_id, id, etc.: see
 +# src/output.c:prepare_symbols_definitions().
 +#
 +# The following macros provide access to these values.
 +
 +# b4_symbol_(NUM, FIELD)
 +# ----------------------
 +# Recover a FIELD about symbol #NUM.  Thanks to m4_indir, fails if
 +# undefined.
 +m4_define([b4_symbol_],
 +[m4_indir([b4_symbol($1, $2)])])
 +
 +
 +# b4_symbol(NUM, FIELD)
 +# ---------------------
 +# Recover a FIELD about symbol #NUM.  Thanks to m4_indir, fails if
 +# undefined.  If FIELD = id, prepend the prefix.
 +m4_define([b4_symbol],
 +[m4_case([$2],
 +         [id],    [m4_do([b4_percent_define_get([api.tokens.prefix])],
 +                         [b4_symbol_([$1], [id])])],
 +         [b4_symbol_($@)])])
 +
 +
 +# b4_symbol_if(NUM, FIELD, IF-TRUE, IF-FALSE)
 +# -------------------------------------------
 +# If FIELD about symbol #NUM is 1 expand IF-TRUE, if is 0, expand IF-FALSE.
 +# Otherwise an error.
 +m4_define([b4_symbol_if],
 +[m4_case(b4_symbol([$1], [$2]),
 +         [1], [$3],
 +         [0], [$4],
 +         [m4_fatal([$0: field $2 of $1 is not a Boolean:] b4_symbol([$1], [$2]))])])
 +
 +
 +# b4_symbol_action_location(SYMBOL-NUM, KIND)
 +# -------------------------------------------
 +# Report the location of the KIND action as FILE:LINE.
 +m4_define([b4_symbol_action_location],
 +[b4_symbol([$1], [$2_file]):b4_syncline([b4_symbol([$1], [$2_line])])])
 +
 +
 +# b4_symbol_action(SYMBOL-NUM, KIND)
 +# ----------------------------------
 +# Run the action KIND (destructor or printer) for SYMBOL-NUM.
 +# 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([@oline@], [@ofile@])
 +        break;
 +
- m4_popdef([b4_at_dollar])dnl
- m4_popdef([b4_dollar_dollar])dnl
++b4_dollar_popdef[]dnl
 +])])
 +
 +
 +# b4_symbol_destructor(SYMBOL-NUM)
 +# b4_symbol_printer(SYMBOL-NUM)
 +# --------------------------------
 +m4_define([b4_symbol_destructor], [b4_symbol_action([$1], [destructor])])
 +m4_define([b4_symbol_printer],    [b4_symbol_action([$1], [printer])])
 +
 +
 +# b4_symbol_case_(SYMBOL-NUM)
 +# ---------------------------
 +# Issue a "case NUM" for SYMBOL-NUM.
 +m4_define([b4_symbol_case_],
 +[      case b4_symbol([$1], [number]): // b4_symbol([$1], [tag])
 +])
 +
 +
 +# b4_symbol_foreach(MACRO)
 +# ------------------------
 +# Invoke MACRO(SYMBOL-NUM) for each SYMBOL-NUM.
 +m4_define([b4_symbol_foreach],
 +          [m4_map([$1], m4_defn([b4_symbol_numbers]))])
 +
 +# b4_symbol_map(MACRO)
 +# --------------------
 +# Return a list (possibly empty elements) of MACRO invoked for each
 +# SYMBOL-NUM.
 +m4_define([b4_symbol_map],
 +[m4_map_args_sep([$1(], [)], [,], b4_symbol_numbers)])
 +
 +
 +# b4_token_visible_if(NUM, IF-TRUE, IF-FALSE)
 +# -------------------------------------------
 +# Whether NUM denotes a token that has an exported definition (i.e.,
 +# shows in enum yytokentype).
 +m4_define([b4_token_visible_if],
 +[b4_symbol_if([$1], [is_token],
 +              [b4_symbol_if([$1], [has_id], [$2], [$3])],
 +              [$3])])
 +
 +# b4_token_has_definition(NUM)
 +# ----------------------------
 +# 1 if NUM is visible, nothing otherwise.
 +m4_define([b4_token_has_definition],
 +[b4_token_visible_if([$1], [1])])
 +
 +# b4_any_token_visible_if([IF-TRUE], [IF-FALSE])
 +# ----------------------------------------------
 +# Whether there is a token that needs to be defined.
 +m4_define([b4_any_token_visible_if],
 +[m4_ifval(b4_symbol_foreach([b4_token_has_definition]),
 +          [$1], [$2])])
 +
 +
 +# b4_token_format(FORMAT, NUM)
 +# ----------------------------
 +m4_define([b4_token_format],
 +[b4_token_visible_if([$2],
 +[m4_quote(m4_format([$1],
 +                     [b4_symbol([$2], [id])],
 +                     [b4_symbol([$2], [user_number])]))])])
 +
 +
 +## ------- ##
 +## Types.  ##
 +## ------- ##
 +
 +# b4_type_action_(NUMS)
 +# ---------------------
 +# Run actions for the symbol NUMS that all have the same type-name.
 +# Skip NUMS that have no type-name.
 +m4_define([b4_type_action_],
 +[b4_symbol_if([$1], [has_type],
 +[m4_map([b4_symbol_case_], [$@])[]dnl
 +        b4_dollar_dollar([b4_symbol([$1], [number])],
 +                         [b4_symbol([$1], [tag])],
 +                         [b4_symbol([$1], [type])]);
 +        break;
 +
 +])])
 +
 +# b4_type_foreach(MACRO)
 +# ----------------------
 +# Invoke MACRO(SYMBOL-NUMS) for each set of SYMBOL-NUMS for each type set.
 +m4_define([b4_type_foreach],
 +          [m4_map([$1], m4_defn([b4_type_names]))])
  
  
  
diff --cc data/c-like.m4
index 0000000000000000000000000000000000000000,c2abce7e1df55e63b97579642c3f79cf1bafdbc6..732d18d4c1c82d04a76ac4e3e06382ceffb6bba2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,44 +1,43 @@@
 -[m4_if([$2], [[]],
 -       [m4_ifval([$3], [($1.$3)],
 -                 [$1])],
 -       [($1.$2)])])
+                                                             -*- 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 --cc data/c.m4
Simple merge
diff --cc data/glr.c
index d392622903a9f1e6197d4aed0a32bb8bc3f47600,08f6cd8d0db55dd4a6a6208566683b58ecd4e51b..935ee96a82fcbb016dc555638adcb25ff4202c86
@@@ -2294,12 -2302,10 +2294,10 @@@ yyrecoverSyntaxError (yyGLRStack* yysta
  #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
 +  /* 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 --cc data/glr.cc
Simple merge
diff --cc data/java.m4
Simple merge
diff --cc data/lalr1.cc
index 96936bbe67955cf17d11b62887475da705d8fffc,c1639033f3fbd5194f92bd2b44f56144637f5406..313dba271ce55f0623f6de62d5cf0951b636a973
  
  m4_include(b4_pkgdatadir/[c++.m4])
  
- [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
 +
 +# b4_integral_parser_table_declare(TABLE-NAME, CONTENT, COMMENT)
 +# --------------------------------------------------------------
 +# Declare "parser::yy<TABLE-NAME>_" which contents is CONTENT.
 +m4_define([b4_integral_parser_table_declare],
 +[m4_ifval([$3], [b4_c_comment([$3], [  ])
 +])dnl
 +  static const b4_int_type_for([$2]) yy$1_[[]];dnl
 +])
 +
 +# b4_integral_parser_table_define(TABLE-NAME, CONTENT, COMMENT)
 +# -------------------------------------------------------------
 +# Define "parser::yy<TABLE-NAME>_" which contents is CONTENT.
 +m4_define([b4_integral_parser_table_define],
 +[  const b4_int_type_for([$2])
 +  b4_parser_class_name::yy$1_[[]] =
 +  {
 +  $2
 +  };dnl
 +])
 +
 +
 +# b4_symbol_value_template(VAL, [TYPE])
 +# -------------------------------------
 +# Same as b4_symbol_value, but used in a template method.  It makes
 +# a difference when using variants.
 +m4_copy([b4_symbol_value], [b4_symbol_value_template])
 +
 +
 +# b4_lhs_value([TYPE])
 +# --------------------
 +# Expansion of $<TYPE>$.
 +m4_define([b4_lhs_value],
 +          [b4_symbol_value([yylhs.value], [$1])])
 +
 +
 +# b4_lhs_location()
 +# -----------------
 +# Expansion of @$.
 +m4_define([b4_lhs_location],
 +          [yylhs.location])
 +
 +
 +# b4_rhs_data(RULE-LENGTH, NUM)
 +# -----------------------------
 +# Return the data corresponding to the symbol #NUM, where the current
 +# rule has RULE-LENGTH symbols on RHS.
 +m4_define([b4_rhs_data],
 +          [yystack_@{b4_subtract($@)@}])
 +
 +
 +# b4_rhs_state(RULE-LENGTH, NUM)
 +# ------------------------------
 +# The state corresponding to the symbol #NUM, where the current
 +# rule has RULE-LENGTH symbols on RHS.
 +m4_define([b4_rhs_state],
 +          [b4_rhs_data([$1], [$2]).state])
 +
 +
 +# b4_rhs_value(RULE-LENGTH, NUM, [TYPE])
 +# --------------------------------------
 +# Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH
 +# symbols on RHS.
 +m4_define([b4_rhs_value],
 +          [b4_symbol_value([b4_rhs_data([$1], [$2]).value], [$3])])
 +
 +
 +# b4_rhs_location(RULE-LENGTH, NUM)
 +# ---------------------------------
 +# Expansion of @NUM, where the current rule has RULE-LENGTH symbols
 +# on RHS.
 +m4_define([b4_rhs_location],
 +          [b4_rhs_data([$1], [$2]).location])
 +
 +
 +# b4_symbol_action(SYMBOL-NUM, KIND)
 +# ----------------------------------
 +# Run the action KIND (destructor or printer) for SYMBOL-NUM.
 +# Same as in C, but using references instead of pointers.
 +m4_define([b4_symbol_action],
 +[b4_symbol_if([$1], [has_$2],
- m4_popdef([b4_at_dollar])dnl
- m4_popdef([b4_dollar_dollar])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([@oline@], [@ofile@])
 +        break;
 +
++m4_popdef([b4_symbol_value])[]dnl
++b4_dollar_popdef[]dnl
 +])])
 +
 +
 +m4_pushdef([b4_copyright_years],
 +           [2002-2012])
 +
  m4_define([b4_parser_class_name],
            [b4_percent_define_get([[parser_class_name]])])
  
@@@ -670,14 -536,12 +670,12 @@@ 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([yylval], [], [yylloc])dnl
 -/* User initialization code.  */
 -b4_user_initial_action
++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 stacks.  The initial state will be pushed in
 +  [  /* Initialize the stack.  The initial state will be set in
         yynewstate, since the latter expects the semantical and the
         location values to have been already stored, initialize these
         stacks with a primary value.  */
diff --cc data/lalr1.java
index d3729c6ef4fe7cb2823f9187371a589bf52052ce,d1410a10cf806b6598d71e90c44bc1d38a1e22a5..1cfaef38caf15686beb2d9731ae46215d5af92b5
@@@ -502,12 -458,10 +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
 +    /* 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 --cc data/local.mk
index 6337f769195f2b7c6a1389297bad21a4b55035c5,0000000000000000000000000000000000000000..b8db2d64af05571b20879b47b199053946f4d26a
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,45 @@@
 +## Copyright (C) 2002, 2005-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/>.
 +
 +dist_pkgdata_DATA =                             \
 +  data/README                                   \
 +  data/bison.m4                                 \
 +  data/c++-skel.m4                              \
 +  data/c++.m4                                   \
++  data/c-like.m4                                \
 +  data/c-skel.m4                                \
 +  data/c.m4                                     \
 +  data/glr.c                                    \
 +  data/glr.cc                                   \
 +  data/java-skel.m4                             \
 +  data/java.m4                                  \
 +  data/lalr1.cc                                 \
 +  data/lalr1.java                               \
 +  data/location.cc                              \
 +  data/stack.hh                                 \
 +  data/variant.hh                               \
 +  data/yacc.c
 +
 +m4sugardir = $(pkgdatadir)/m4sugar
 +dist_m4sugar_DATA =                             \
 +  data/m4sugar/foreach.m4                       \
 +  data/m4sugar/m4sugar.m4
 +
 +xsltdir = $(pkgdatadir)/xslt
 +dist_xslt_DATA =                                \
 +  data/xslt/bison.xsl                           \
 +  data/xslt/xml2dot.xsl                         \
 +  data/xslt/xml2text.xsl                        \
 +  data/xslt/xml2xhtml.xsl
diff --cc data/yacc.c
Simple merge
diff --cc doc/bison.texi
Simple merge
index 47d9fb654421817b5cc6511c7f759aeff685303e,5c624982a9a7795cfd3dfb5b17aa53c5aa97f0fe..1168a9c33e5d7e6604d025c63be2977d49b24edd
@@@ -202,14 -202,14 +202,14 @@@ muscle_code_grow (const char *key, cons
  
  
  void muscle_pair_list_grow (const char *muscle,
 -                          const char *a1, const char *a2)
 +                            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");
@@@ -263,12 -266,14 +266,14 @@@ static voi
  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_1grow (&muscle_obstack, ':');
    obstack_fgrow1 (&muscle_obstack, "%d", bound.line);
 -  obstack_1grow  (&muscle_obstack, '.');
 +  obstack_1grow (&muscle_obstack, '.');
    obstack_fgrow1 (&muscle_obstack, "%d", bound.column);
 -  obstack_1grow  (&muscle_obstack, '\0');
+   obstack_sgrow  (&muscle_obstack, "]]");
 +  obstack_1grow (&muscle_obstack, '\0');
    extension = obstack_finish (&muscle_obstack);
    muscle_grow (key, extension, "");
    obstack_free (&muscle_obstack, extension);
@@@ -503,9 -475,9 +511,9 @@@ muscle_percent_define_get_loc (char con
    char const *loc_name;
    loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")");
    if (!muscle_find_const (loc_name))
 -    fatal(_("%s: undefined %%define variable %s"),
 +    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 *
index 5f701ae41a242aaee5dc7fb840d12a178230db2d,ddbad4085572477af12b7477295c01ca829c2e89..1f33f8c15a9bf1fc67750a654bd93dccdce825e5
@@@ -35,63 -36,46 +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);                                 \
++    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
diff --cc src/output.c
index 26a6e7fe6c2fd06c83c9b3f256724d37bcbcd79e,d52a711393cf960b73c0a55ad8f69d752d699096..c227be223f4c89749696305e8aebc7f5ed3365d9
@@@ -161,23 -172,23 +171,23 @@@ prepare_symbols (void
      set_quoting_flags (qo, QA_SPLIT_TRIGRAPHS);
      for (i = 0; i < nsyms; i++)
        {
 -      char *cp = quotearg_alloc (symbols[i]->tag, -1, qo);
 -      /* Width of the next token, including the two quotes, the
 -         comma and the space.  */
 -      int width = strlen (cp) + 2;
 -
 -      if (j + width > 75)
 -        {
 -          obstack_sgrow (&format_obstack, "\n ");
 -          j = 1;
 -        }
 -
 -      if (i)
 -        obstack_1grow (&format_obstack, ' ');
 -      obstack_escape (&format_obstack, cp);
 +        char *cp = quotearg_alloc (symbols[i]->tag, -1, qo);
 +        /* Width of the next token, including the two quotes, the
 +           comma and the space.  */
 +        int width = strlen (cp) + 2;
 +
 +        if (j + width > 75)
 +          {
 +            obstack_sgrow (&format_obstack, "\n ");
 +            j = 1;
 +          }
 +
 +        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;
 +        obstack_1grow (&format_obstack, ',');
 +        j += width;
        }
      free (qo);
      obstack_sgrow (&format_obstack, " ]b4_null[");
@@@ -352,11 -307,10 +362,11 @@@ user_actions_output (FILE *out
    for (r = 0; r < nrules; ++r)
      if (rules[r].action)
        {
 -      fprintf (out, "b4_case(%d, [b4_syncline(%d, ", r + 1,
 -               rules[r].action_location.start.line);
 -      string_output (out, rules[r].action_location.start.file);
 -      fprintf (out, ")\n[    %s]])\n\n", rules[r].action);
 +        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);
  }
index 9d9d1a42f58209557821cf439fd49527624fcaef,e6a27c50dcfac14e72c74234c1b0b3e57dbf7a5f..518969948f0586a22d438f41606175240e32e19d
@@@ -387,12 -345,29 +387,17 @@@ grammar_declaration
      {
        grammar_start_symbol_set ($2, @2);
      }
 -| "%destructor" "{...}" generic_symlist
 -    {
 -      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_destructor_set (list, &code);
 -        symbol_list_free ($3);
 -      }
 -    }
 -| "%printer" "{...}" generic_symlist
 +| 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_printer_set (list, &code);
++          symbol_list_code_props_set (list, $1, &code);
+         symbol_list_free ($3);
+       }
      }
  | "%default-prec"
      {
diff --cc src/scan-code.h
Simple merge
diff --cc src/scan-code.l
index f6efeffdfec9898cb05c8f7e65f09763b689e9a6,31befe9172f9bc7e8d6c99f543e4f8c5649ff2af..2fe880ed6a371d03215cc19af83d16320a6759e7
@@@ -47,8 -46,11 +47,11 @@@ 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);
 +                                  location dollar_loc);
  static void handle_action_at (symbol_list *rule, char *cp, location at_loc);
  
  /* A string to be pushed to obstack after dollar/at has been handled. */
@@@ -177,6 -184,15 +185,15 @@@ ref      -?[0-9]+|{id}|"["{id}"]"|"$
      STRING_GROW;
      BEGIN SC_LINE_COMMENT;
    }
 -    warn_at (*loc, _("stray '%s'"), yytext);
+   [$@]  {
++    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>
  }
  
  
-   /*-----------------------------------------.
-   | 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.  */
+   [$@\[\]]    obstack_escape (&obstack_for_string, yytext);
  
- <*>.|\n STRING_GROW;
+   /* By default, grow the string obstack with the input.  */
+   .|\n        STRING_GROW;
  
 -  /* End of processing. */
 + /* End of processing. */
- <*><<EOF>>       {
-                    STRING_FINISH;
-                    return last_string;
-                  }
+   <<EOF>>     STRING_FINISH; return last_string;
+ }
  
  %%
  
@@@ -681,6 -677,31 +670,32 @@@ parse_ref (char *cp, symbol_list *rule
  int max_left_semantic_context = 0;
  
  
 -      ++cp;
+ /* 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 != '>')
 -       'text' is needed for error messages. */
++        ++cp;
+       /* The '>' symbol will be later replaced by '\0'. Original
 -      complain_at (dollar_loc, _("explicit type given in untyped grammar"));
++         '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 '$').      |
  |                                                                   |
@@@ -711,27 -731,14 +725,14 @@@ handle_action_dollar (symbol_list *rule
      }
  
    /* 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, '$');
 +                 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)
      {
        if (!type_name)
          {
            if (union_seen | tag_seen)
 -            complain_at (dollar_loc, _("$%s of %s has no declared type"),
 -                         cp, quote (effective_rule->content.sym->tag));
 +            complain_at (dollar_loc, complaint,
 +                         _("$%s of %s has no declared type"), cp,
 +                         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);
++                      "]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;
 +        symbol_list_n_get (effective_rule, n)->action_props.is_value_used =
 +          true;
        break;
      }
  }
diff --cc src/scan-skel.l
index 17382a73890ec54ac47cb7e473e331777b40ba62,862b34fd37e43131aef9a68ac3c66e285bae363f..b79e58323e0cc2a95fc1232c6437ed2a56099151
@@@ -87,11 -87,11 +87,11 @@@ static void fail_for_invalid_at (char c
  }
  
    /* This pattern must not match more than the previous @ patterns. */
 -@[^@{}`(\n]*  fail_for_invalid_at (yytext);
 -\n            out_lineno++; ECHO;
 -[^@\n]+       ECHO;
 +@[^@{}`(\n]* fail_for_invalid_at (yytext);
 +\n         out_lineno++; ECHO;
 +[^@\n]+    ECHO;
  
- <INITIAL><<EOF>> {
+ <<EOF>> {
    if (outname)
      {
        free (outname);
        }
    }
  
-   @.? { 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; }
 +  . { 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>> {
 -    fatal (_("unclosed %s directive in skeleton"), at_directive_argv[0]);
 +    complain (fatal, _("unclosed %s directive in skeleton"),
 +              at_directive_argv[0]);
    }
  }
  
diff --cc src/symlist.c
index bd9f3c233191402b8daa7cbe6d782d9881314966,69ac8a1b574c9684cd49157ab9a25184839dbeeb..2bbedb585c95028b5a58bec55577ab9a2c048c2f
@@@ -190,26 -223,43 +190,23 @@@ symbol_list_null (symbol_list *node
  }
  
  void
 -symbol_list_destructor_set (symbol_list *node, code_props const *destructor)
 -{
 -  switch (node->content_type)
 -    {
 -      case SYMLIST_SYMBOL:
 -        symbol_destructor_set (node->content.sym, destructor);
 -        break;
 -      case SYMLIST_TYPE:
 -        semantic_type_destructor_set (
 -          semantic_type_get (node->content.type_name), destructor);
 -        break;
 -      case SYMLIST_DEFAULT_TAGGED:
 -        default_tagged_destructor_set (destructor);
 -        break;
 -      case SYMLIST_DEFAULT_TAGLESS:
 -        default_tagless_destructor_set (destructor);
 -        break;
 -    }
 -}
 -
 -void
 -symbol_list_printer_set (symbol_list *node, code_props const *printer)
 +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_printer_set (node->content.sym, printer);
++        symbol_code_props_set (node->content.sym, kind, cprops);
 +        if (node->content.sym->status == undeclared)
 +          node->content.sym->status = used;
          break;
        case SYMLIST_TYPE:
 -        semantic_type_printer_set (
 -          semantic_type_get (node->content.type_name), printer);
 -        break;
 -      case SYMLIST_DEFAULT_TAGGED:
 -        default_tagged_printer_set (printer);
 -        break;
 -      case SYMLIST_DEFAULT_TAGLESS:
 -        default_tagless_printer_set (printer);
 +        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 --cc src/symlist.h
index dd63408339b791d5d8a6890f584d9408889bd004,63577f798b7862845c90b11a40f5e4572df954c7..03ffebef16572034acc847f03c7a5e78e9c98fe2
@@@ -113,9 -118,12 +113,8 @@@ uniqstr symbol_list_n_type_name_get (sy
  /* 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 for \c node as \c code at \c loc.  */
 -void symbol_list_destructor_set (symbol_list *node,
 -                                 code_props const *destructor);
 -
 -/** Set the \c \%printer for \c node as \c code at \c loc.  */
 -void symbol_list_printer_set (symbol_list *node,
 -                              code_props const *printer);
++/** 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 --cc src/symtab.h
index 9377800801e4d6bc793e6e622545de7d616a1e9a,7d8cf982650f36ff71f829976388eaa23a7563dd..49eebd4c2aaf34f286d639eaf49d0dce88f35d5e
@@@ -97,14 -71,20 +103,14 @@@ struct symbo
    /** Its \c \%type's location.  */
    location type_location;
  
 -  /** Any \c \%destructor declared specifically for this symbol.
 -
 -      Access this field only through <tt>symbol</tt>'s interface
 -      functions.  For example, if <tt>symbol::destructor = NULL</tt>, a
 -      default \c \%destructor or a per-type \c \%destructor might be
 -      appropriate, and \c symbol_destructor_get will compute the
 -      correct one.  */
 -  code_props destructor;
 -
 -  /** Any \c \%printer declared specifically for this symbol.
 +  /** Any \c \%destructor (resp. \%printer) declared specificially for this
 +      symbol.
  
 -      Access this field only through <tt>symbol</tt>'s interface functions.
 -      \sa symbol::destructor  */
 -  code_props printer;
 +      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;
    location prec_location;
diff --cc src/system.h
index 5613a92f711777a5ac9fb96ebd37b2ac3d5db392,06fd0f0a9d578901a14b14a3e4804dc4f660142d..1d0af8fabddb6e7b5c10678dc1288de5c9abd369
@@@ -169,40 -156,83 +169,83 @@@ typedef size_t uintptr_t
  | Obstacks.  |
  `-----------*/
  
 -# define obstack_chunk_alloc xmalloc
 -# define obstack_chunk_free  free
 -# include <obstack.h>
 +#define obstack_chunk_alloc xmalloc
 +#define obstack_chunk_free  free
 +#include <obstack.h>
  
 -# define obstack_sgrow(Obs, Str)                \
 +#define obstack_sgrow(Obs, Str) \
    obstack_grow (Obs, Str, strlen (Str))
  
 -# define obstack_fgrow1(Obs, Format, Arg1)      \
 +#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));              \
 +#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)  \
 +#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)    \
 +#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]" -> "@{foo@}", "$$" -> "$][$][". */
+ # define obstack_escape(Obs, Str)                       \
+   do {                                                  \
 -    char const *p;                                      \
 -    for (p = Str; *p; p++)                              \
 -      switch (*p)                                       \
++    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;    \
++        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]" -> "[[@{foo@}]]", "$$" -> "[[$][$][]]". */
+ # 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)
  
  
  
  | Extensions to use for the output files.  |
  `-----------------------------------------*/
  
 -# ifndef OUTPUT_EXT
 -#  define OUTPUT_EXT ".output"
 -# endif
 +#ifndef OUTPUT_EXT
 +# define OUTPUT_EXT ".output"
 +#endif
  
 -# ifndef TAB_EXT
 -#  define TAB_EXT ".tab"
 -# endif
 +#ifndef TAB_EXT
 +# define TAB_EXT ".tab"
 +#endif
  
- #ifndef DEFAULT_TMPDIR
- # define DEFAULT_TMPDIR "/tmp"
- #endif
  
  
  /*---------------------.
Simple merge
diff --cc tests/input.at
index 71505cf81b004b1a72feb60f309546b61b3176fd,ff1d3924d907f62413e2aa1e3f9cf23a77c8b285..d6f2084a3a43f8280da559ae7aad1cc89657fb34
@@@ -1491,3 -1369,100 +1491,102 @@@ AT_TEST([%define api.prefix foo], [-p b
  m4_popdef([AT_TEST])
  
  AT_CLEANUP
 -[[%token TOK
+ ## -------------- ##
+ ## 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]],
 -[[input.y:10.19: warning: stray '$'
 -input.y:10.23: warning: stray '@'
 -input.y:11.19: warning: stray '$'
 -input.y:11.23: warning: stray '@'
 -input.y:12.19: warning: stray '$'
 -input.y:12.23: warning: stray '@'
 -input.y:14.19: warning: stray '$'
 -input.y:14.23: warning: stray '@'
++[[%type <TYPE> exp
++%token <TYPE> TOK TOK2
+ %destructor     { $%; @%; } <*> exp TOK;
+ %initial-action { $%; @%; };
+ %printer        { $%; @%; } <*> exp TOK;
+ %%
+ exp: TOK        { $%; @%; $$ = $1; };
+ ]])
+ AT_BISON_CHECK([[input.y]], 0, [],
 -  a a[last]
++[[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:
 -    $<$1(DEAD action 3)>last
++  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 --cc tests/local.at
Simple merge