yacc: comply with recent POSIX updates: declare yyerror and yylex

In POSIX Yacc mode, declare yyerror and yylex unless already #defined,
or if YYERROR_IS_DECLARED/YYLEX_IS_DECLARED are defined (for
consistency with Bison's YYSTYPE_IS_DECLARED/YYLTYPE_IS_DECLARED).
See <https://austingroupbugs.net/view.php?id=1388#c5220>.

* data/skeletons/c.m4 (b4_function_declare): Resurect.
(b4_lex_formals): Since we will possibly expose this prototype
in the header, take the prefix into account.
* data/skeletons/yacc.c (b4_declare_yyerror_and_yylex): New.
(b4_shared_declarations): Use it.

* tests/local.at (AT_YACC_IF): New.
When in Yacc mode, set the `yacc` Autotest keyword.
(AT_YYERROR_DECLARE(c)): Don't declare in Yacc mode,
to avoid clashes (since this signature is static).
(AT_YYERROR_DEFINE(c)): Don't define as static in Yacc mode.
* tests/regression.at (Early token definitions with --yacc): Specify
that we are in Yacc mode.
This commit is contained in:
Akim Demaille
2021-08-05 08:39:24 +02:00
parent dabde7c560
commit 3c5f73fe51
8 changed files with 95 additions and 30 deletions

8
NEWS
View File

@@ -9,6 +9,14 @@ GNU Bison NEWS
now generates a *.gv file by default, instead of *.dot. A transition
started in Bison 3.4.
To comply with the latest POSIX standard, in Yacc compatibility mode
(options `-y`/`--yacc`) Bison now generates prototypes for yyerror and
yylex. In some situations, this is breaking compatibility: if the user
has already declared these functions but with some differences (e.g., to
declare them as static, or to use specific attributes), the generated
parser will fail to compile. To disable these prototypes, #define yyerror
(to `yyerror`), and likewise for yylex.
** Deprecated features
Support for the YYPRINT macro is removed. It worked only with yacc.c and

4
TODO
View File

@@ -1,8 +1,4 @@
* Soon
** POSIX updates
See the recent changes about function prototypes in POSIX Yacc. Implement
them.
** scan-code
The default case is scanning char-per-char.

View File

@@ -112,8 +112,8 @@ b4_percent_define_default([[api.symbol.prefix]], [[YYSYMBOL_]])
# All the yylex formal arguments.
# b4_lex_param arrives quoted twice, but we want to keep only one level.
m4_define([b4_lex_formals],
[b4_pure_if([[[[YYSTYPE *yylvalp]], [[&yylval]]][]dnl
b4_locations_if([, [[YYLTYPE *yyllocp], [&yylloc]]])])dnl
[b4_pure_if([[[b4_api_PREFIX[STYPE *yylvalp]], [[&yylval]]][]dnl
b4_locations_if([, [b4_api_PREFIX[LTYPE *yyllocp], [&yylloc]]])])dnl
m4_ifdef([b4_lex_param], [, ]b4_lex_param)])
@@ -662,6 +662,14 @@ m4_define([b4_formal],
[$1])
# b4_function_declare(NAME, RETURN-VALUE, [DECL1, NAME1], ...)
# ------------------------------------------------------------
# Declare the function NAME.
m4_define([b4_function_declare],
[$2 $1 (b4_formals(m4_shift2($@)));[]dnl
])
## --------------------- ##
## Calling C functions. ##

View File

@@ -102,6 +102,16 @@ m4_define([b4_yyerror_arg_loc_if],
[1], [m4_ifset([b4_parse_param], [$1])],
[2], [$1])])])
# b4_yyerror_formals
# ------------------
m4_define([b4_yyerror_formals],
[b4_pure_if([b4_locations_if([, [[const ]b4_api_PREFIX[LTYPE *yyllocp], [&yylloc]]])[]dnl
m4_ifdef([b4_parse_param], [, b4_parse_param])[]dnl
,])dnl
[[const char *msg], [msg]]])
# b4_yyerror_args
# ---------------
# Arguments passed to yyerror: user args plus yylloc.
@@ -352,17 +362,32 @@ m4_define([b4_declare_yyparse],
])
# b4_declare_yyerror_and_yylex
# ----------------------------
# Comply with POSIX Yacc.
# <https://austingroupbugs.net/view.php?id=1388#c5220>
m4_define([b4_declare_yyerror_and_yylex],
[b4_yacc_if([[#if !defined ]b4_prefix[error && !defined ]b4_api_PREFIX[ERROR_IS_DECLARED
]b4_function_declare([b4_prefix[error]], void, b4_yyerror_formals)[
#endif
#if !defined ]b4_prefix[lex && !defined ]b4_api_PREFIX[LEX_IS_DECLARED
]b4_function_declare([b4_prefix[lex]], int, b4_lex_formals)[
#endif
]])dnl
])
# b4_shared_declarations
# ----------------------
# Declaration that might either go into the header (if --header)
# or open coded in the parser body.
# Declarations that might either go into the header (if --header)
# or into the implementation file.
m4_define([b4_shared_declarations],
[b4_cpp_guard_open([b4_spec_mapped_header_file])[
]b4_declare_yydebug[
]b4_percent_code_get([[requires]])[
]b4_token_enums_defines[
]b4_declare_yylstype[
]b4_declare_yyerror_and_yylex[
]b4_declare_yyparse[
]b4_percent_code_get([[provides]])[
]b4_cpp_guard_close([b4_spec_mapped_header_file])[]dnl

View File

@@ -6117,11 +6117,13 @@ states and what is done for each type of lookahead token in that state.
@end deffn
@deffn {Directive} %yacc
Pretend the option @option{--yacc} was given, i.e., imitate Yacc, including
its naming conventions. Only makes sense with the @file{yacc.c}
Pretend the option @option{--yacc} was given
(@pxref{option-yacc,,@option{--yacc}}), i.e., imitate Yacc, including its
naming conventions. Only makes sense with the @file{yacc.c}
skeleton. @xref{Tuning the Parser}, for more.
Of course @code{%yacc} is a Bison extension@dots{}
Of course, being a Bison extension, @code{%yacc} is somewhat
self-contradictory@dots{}
@end deffn
@@ -11832,8 +11834,9 @@ Pretend that @code{%locations} was specified. @xref{Decl Summary}.
@item -p @var{prefix}
@itemx --name-prefix=@var{prefix}
Pretend that @code{%name-prefix "@var{prefix}"} was specified (@pxref{Decl
Summary}). Obsoleted by @option{-Dapi.prefix=@var{prefix}}. @xref{Multiple
Parsers}.
Summary}). The option @option{-p} is specified by POSIX. When POSIX
compatibility is not a requirement, @option{-Dapi.prefix=@var{prefix}} is a
better option (@pxref{Multiple Parsers}).
@item -l
@itemx --no-lines
@@ -11865,26 +11868,46 @@ This is similar to how most shells resolve commands.
Pretend that @code{%token-table} was specified. @xref{Decl Summary}.
@item -y
@itemx --yacc
Act more like the traditional @command{yacc} command. This can cause
different diagnostics to be generated (it implies @option{-Wyacc}), and may
change behavior in other minor ways. Most importantly, imitate Yacc's
output file name conventions, so that the parser implementation file is
called @file{y.tab.c}, and the other outputs are called @file{y.output} and
@file{y.tab.h}. Also, generate @code{#define} statements in addition to an
@code{enum} to associate token codes with token kind names. Thus, the
following shell script can substitute for Yacc, and the Bison distribution
contains such a script for compatibility with POSIX:
@itemx @anchor{option-yacc} --yacc
Act more like the traditional @command{yacc} command:
@itemize
@item
Generate different diagnostics (it implies @option{-Wyacc}).
@item
Generate @code{#define} statements in addition to an @code{enum} to
associate token codes with token kind names.
@item
Generate prototypes for @code{yyerror} and @code{yylex} (since Bison 3.8):
@example
#! /bin/sh
bison -y "$@@"
int yylex (void);
void yyerror (const char *);
@end example
As a Bison extension, additional arguments required by @code{%pure-parser},
@code{%locations}, @code{%lex-param} and @code{%parse-param} are taken into
account. You may disable @code{yyerror}'s prototype with @samp{#define
yyerror yyerror} (as specified by POSIX), or with @samp{#define
YYERROR_IS_DECLARED} (a Bison extension). Likewise for @code{yylex}.
@item
Imitate Yacc's output file name conventions, so that the parser
implementation file is called @file{y.tab.c}, and the other outputs are
called @file{y.output} and @file{y.tab.h}. Do not use @option{--yacc} just
to change the output file names since it also triggers all the
aforementioned behavior changes; rather use @samp{-o y.tab.c}.
@end itemize
The @option{-y}/@option{--yacc} option is intended for use with traditional
Yacc grammars. This option only makes sense for the default C skeleton,
@file{yacc.c}. If your grammar uses Bison extensions Bison cannot be
Yacc-compatible, even if this option is specified.
Thus, the following shell script can substitute for Yacc, and the Bison
distribution contains such a @command{yacc} script for compatibility with
POSIX:
@example
#! /bin/sh
bison -y "$@@"
@end example
@end table
@node Output Files

View File

@@ -27,6 +27,7 @@ synonyms.
- report: for automaton dumps
- %union
- variant
- yacc: POSIX yacc (%yacc)
# Calculator
The grammar features several special directives:

View File

@@ -275,6 +275,8 @@ m4_pushdef([AT_MULTISTART_IF],
[m4_bmatch([$3], [%start [_a-zA-Z]+ [_a-zA-Z]+], [$1], [$2])])
m4_pushdef([AT_PARAM_IF],
[m4_bmatch([$3], [%parse-param], [$1], [$2])])
m4_pushdef([AT_YACC_IF],
[m4_bmatch([$3], [%yacc], [$1], [$2])])
# Comma-terminated list of formals parse-parameters.
# E.g., %parse-param { int x } %parse-param {int y} -> "int x, int y, ".
@@ -427,6 +429,7 @@ AT_LOCATION_TYPE_SPAN_IF(
AT_GLR_IF([AT_KEYWORDS([glr])])
AT_MULTISTART_IF([AT_KEYWORDS([multistart])])
AT_PUSH_IF([AT_KEYWORDS([push])])
AT_YACC_IF([AT_KEYWORDS([yacc])])
])# _AT_BISON_OPTION_PUSHDEFS
@@ -466,6 +469,7 @@ m4_popdef([AT_VALUE_UNION_IF])
m4_popdef([AT_PUSH_IF])
m4_popdef([AT_PURE_IF])
m4_popdef([AT_PARSER_CLASS])
m4_popdef([AT_YACC_IF])
m4_popdef([AT_PARAM_IF])
m4_popdef([AT_MULTISTART_IF])
m4_popdef([AT_LEXPARAM_IF])
@@ -683,7 +687,7 @@ m4_define([AT_YYERROR_DECLARE_EXTERN(c)],
m4_define([AT_YYERROR_DECLARE(c)],
[[#include <stdio.h>
]AT_LOCATION_PRINT_DECLARE[
static ]AT_YYERROR_DECLARE_EXTERN])
]AT_YACC_IF([], [[static ]AT_YYERROR_DECLARE_EXTERN])])
# "%define parse.error custom" uses a different format, easy to check.
@@ -729,7 +733,7 @@ yyreport_syntax_error (const yypcontext_t *ctx]AT_PARAM_IF([, AT_PARSE_PARAMS])[
]])[
/* A C error reporting function. */
static
]AT_YACC_IF([], [static])[
]AT_YYERROR_PROTOTYPE[
{]m4_bpatsubst(m4_defn([AT_PARSE_PARAMS]),
[[^,]+[^A-Za-z_0-9]\([A-Za-z_][A-Za-z_0-9]*\),* *], [

View File

@@ -87,7 +87,7 @@ AT_SETUP([Early token definitions with --yacc])
# Found in GCJ: they expect the tokens to be defined before the user
# prologue, so that they can use the token definitions in it.
AT_BISON_OPTION_PUSHDEFS
AT_BISON_OPTION_PUSHDEFS([%yacc])
# Not AT_DATA_GRAMMAR, which uses %code, which is not supported by Yacc.
AT_DATA([input.y],
@@ -112,7 +112,7 @@ exp: MY_TOKEN;
]])
AT_BISON_OPTION_POPDEFS
AT_BISON_CHECK([-y -o input.c input.y])
AT_BISON_CHECK([--yacc -o input.c input.y])
AT_COMPILE([input.o])
AT_CLEANUP