diff --git a/NEWS b/NEWS index c8cab5e0..a1bb0f91 100644 --- a/NEWS +++ b/NEWS @@ -242,6 +242,17 @@ GNU Bison NEWS * Noteworthy changes in release ?.? (????-??-??) [?] +** New value for %define variable: api.pure full + + The %define variable api.pure requests a pure (reentrant) parser. However, + for historical reasons, using it in a location-tracking Yacc parser resulted + in an yyerror function that did not take a location as a parameter. With this + new value, the user may request a better pure parser, where yyerror does take + a location as a parameter (in location-tracking parsers). + + The use of "%define api.pure true" is deprecated in favor of this new + "%define api.pure full". + ** Changes in the format of error messages This used to be the format of many error reports: diff --git a/data/yacc.c b/data/yacc.c index 32218409..2b6d10ac 100644 --- a/data/yacc.c +++ b/data/yacc.c @@ -67,24 +67,36 @@ m4_define_default([b4_stack_depth_init], [200]) ## ------------------------ ## b4_percent_define_default([[api.pure]], [[false]]) -b4_define_flag_if([pure]) -m4_define([b4_pure_flag], - [b4_percent_define_flag_if([[api.pure]], [[1]], [[0]])]) +b4_percent_define_check_values([[[[api.pure]], + [[false]], [[true]], [[]], [[full]]]]) -# b4_yacc_pure_if(IF-TRUE, IF-FALSE) -# ---------------------------------- -# Expand IF-TRUE, if %pure-parser and %parse-param, IF-FALSE otherwise. -m4_define([b4_yacc_pure_if], -[b4_pure_if([m4_ifset([b4_parse_param], - [$1], [$2])], - [$2])]) +m4_define([b4_pure_flag], [[0]]) +m4_case(b4_percent_define_get([[api.pure]]), + [false], [m4_define([b4_pure_flag], [[0]])], + [true], [m4_define([b4_pure_flag], [[1]])], + [], [m4_define([b4_pure_flag], [[1]])], + [full], [m4_define([b4_pure_flag], [[2]])]) +m4_define([b4_pure_if], +[m4_case(b4_pure_flag, + [0], [$2], + [1], [$1], + [2], [$1])]) + [m4_fatal([invalid api.pure value: ]$1)])]) + +# b4_yyerror_arg_loc_if(ARG) +# -------------------------- +# Expand ARG iff yyerror is to be given a location as argument. +m4_define([b4_yyerror_arg_loc_if], +[b4_locations_if([m4_case(b4_pure_flag, + [1], [m4_ifset([b4_parse_param], [$1])], + [2], [$1])])]) # b4_yyerror_args # --------------- # Arguments passed to yyerror: user args plus yylloc. m4_define([b4_yyerror_args], -[b4_yacc_pure_if([b4_locations_if([&yylloc, ])])dnl +[b4_yyerror_arg_loc_if([&yylloc, ])dnl m4_ifset([b4_parse_param], [b4_args(b4_parse_param), ])]) diff --git a/doc/bison.texi b/doc/bison.texi index 31a46ed5..a6ff03b2 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -4954,7 +4954,7 @@ declaration @samp{%define api.pure} says that you want the parser to be reentrant. It looks like this: @example -%define api.pure +%define api.pure full @end example The result is that the communication variables @code{yylval} and @@ -5004,7 +5004,7 @@ compatibility with the impure Yacc pull mode interface. Unless you know what you are doing, your declarations should look like this: @example -%define api.pure +%define api.pure full %define api.push-pull push @end example @@ -5508,9 +5508,41 @@ The parser namespace is @code{foo} and @code{yylex} is referenced as @item Purpose: Request a pure (reentrant) parser program. @xref{Pure Decl, ,A Pure (Reentrant) Parser}. -@item Accepted Values: Boolean +@item Accepted Values: @code{true}, @code{false}, @code{full} + +The value may be omitted: this is equivalent to specifying @code{true}, as is +the case for Boolean values. + +When @code{%define api.pure full} is used, the parser is made reentrant. This +changes the signature for @code{yylex} (@pxref{Pure Calling}), and also that of +@code{yyerror} when the tracking of locations has been activated, as shown +below. + +The @code{true} value is very similar to the @code{full} value, the only +difference is in the signature of @code{yyerror} on Yacc parsers without +@code{%parse-param}, for historical reasons. + +I.e., if @samp{%locations %define api.pure} is passed then the prototypes for +@code{yyerror} are: + +@example +void yyerror (char const *msg); /* Yacc parsers. */ +void yyerror (YYLTYPE *locp, char const *msg); /* GLR parsers. */ +@end example + +But if @samp{%locations %define api.pure %parse-param @{int *nastiness@}} is +used, then both parsers have the same signature: + +@example +void yyerror (YYLTYPE *llocp, int *nastiness, char const *msg); +@end example + +(@pxref{Error Reporting, ,The Error +Reporting Function @code{yyerror}}) @item Default Value: @code{false} + +@item History: the @code{full} value was introduced in Bison 2.7 @end itemize @c api.pure @@ -5637,7 +5669,7 @@ remain in the parser tables. @xref{Unreachable States}. @item Default Value: @code{false} @end itemize introduced as @code{lr.keep_unreachable_states} in 2.3b, renamed as -@code{lr.keep-unreachable-state} in 2.5, and as +@code{lr.keep-unreachable-states} in 2.5, and as @code{lr.keep-unreachable-state} in 2.8. @c lr.keep-unreachable-state @@ -6073,6 +6105,27 @@ In the grammar actions, use expressions like this to refer to the data: exp: @dots{} @{ @dots{}; *randomness += 1; @dots{} @} @end example +@noindent +Using the following: +@example +%parse-param @{int *randomness@} +@end example + +Results in these signatures: +@example +void yyerror (int *randomness, const char *msg); +int yyparse (int *randomness); +@end example + +@noindent +Or, if both @code{%define api.pure full} (or just @code{%define api.pure}) +and @code{%locations} are used: + +@example +void yyerror (YYLTYPE *llocp, int *randomness, const char *msg); +int yyparse (int *randomness); +@end example + @node Push Parser Function @section The Push Parser Function @code{yypush_parse} @findex yypush_parse @@ -6324,7 +6377,7 @@ The data type of @code{yylloc} has the name @code{YYLTYPE}. @node Pure Calling @subsection Calling Conventions for Pure Parsers -When you use the Bison declaration @samp{%define api.pure} to request a +When you use the Bison declaration @code{%define api.pure full} to request a pure, reentrant parser, the global communication variables @code{yylval} and @code{yylloc} cannot be used. (@xref{Pure Decl, ,A Pure (Reentrant) Parser}.) In such parsers the two global variables are replaced by @@ -6369,6 +6422,7 @@ Specify that @var{argument-declaration} are additional declarations, which is equivalent to repeating @code{%param}. @end deffn +@noindent For instance: @example @@ -6385,7 +6439,7 @@ int yylex (scanner_mode *mode, environment_type *env); int yyparse (parser_mode *mode, environment_type *env); @end example -If @samp{%define api.pure} is added: +If @samp{%define api.pure full} is added: @example int yylex (YYSTYPE *lvalp, scanner_mode *mode, environment_type *env); @@ -6393,7 +6447,8 @@ int yyparse (parser_mode *mode, environment_type *env); @end example @noindent -and finally, if both @samp{%define api.pure} and @code{%locations} are used: +and finally, if both @samp{%define api.pure full} and @code{%locations} are +used: @example int yylex (YYSTYPE *lvalp, YYLTYPE *llocp, @@ -6458,50 +6513,16 @@ error recovery if you have written suitable error recovery grammar rules immediately return 1. Obviously, in location tracking pure parsers, @code{yyerror} should have -an access to the current location. -This is indeed the case for the GLR -parsers, but not for the Yacc parser, for historical reasons. I.e., if -@samp{%locations %define api.pure} is passed then the prototypes for -@code{yyerror} are: +an access to the current location. With @code{%define api.pure}, this is +indeed the case for the GLR parsers, but not for the Yacc parser, for +historical reasons, and this is the why @code{%define api.pure full} should be +prefered over @code{%define api.pure}. + +When @code{%locations %define api.pure full} is used, @code{yyerror} has the +following signature: @example -void yyerror (char const *msg); /* Yacc parsers. */ -void yyerror (YYLTYPE *locp, char const *msg); /* GLR parsers. */ -@end example - -If @samp{%parse-param @{int *nastiness@}} is used, then: - -@example -void yyerror (int *nastiness, char const *msg); /* Yacc parsers. */ -void yyerror (int *nastiness, char const *msg); /* GLR parsers. */ -@end example - -Finally, GLR and Yacc parsers share the same @code{yyerror} calling -convention for absolutely pure parsers, i.e., when the calling -convention of @code{yylex} @emph{and} the calling convention of -@samp{%define api.pure} are pure. -I.e.: - -@example -/* Location tracking. */ -%locations -/* Pure yylex. */ -%define api.pure -%lex-param @{int *nastiness@} -/* Pure yyparse. */ -%parse-param @{int *nastiness@} -%parse-param @{int *randomness@} -@end example - -@noindent -results in the following signatures for all the parser kinds: - -@example -int yylex (YYSTYPE *lvalp, YYLTYPE *llocp, int *nastiness); -int yyparse (int *nastiness, int *randomness); -void yyerror (YYLTYPE *locp, - int *nastiness, int *randomness, - char const *msg); +void yyerror (YYLTYPE *locp, char const *msg); @end example @noindent @@ -7675,9 +7696,9 @@ mysterious behavior altogether. You simply need to activate a more powerful parser table construction algorithm by using the @code{%define lr.type} directive. -@deffn {Directive} {%define lr.type @var{TYPE}} +@deffn {Directive} {%define lr.type} @var{type} Specify the type of parser tables within the LR(1) family. The accepted -values for @var{TYPE} are: +values for @var{type} are: @itemize @item @code{lalr} (default) @@ -7865,9 +7886,9 @@ split the parse instead. To adjust which states have default reductions enabled, use the @code{%define lr.default-reduction} directive. -@deffn {Directive} {%define lr.default-reduction @var{WHERE}} +@deffn {Directive} {%define lr.default-reduction} @var{where} Specify the kind of states that are permitted to contain default reductions. -The accepted values of @var{WHERE} are: +The accepted values of @var{where} are: @itemize @item @code{most} (default for LALR and IELR) @item @code{consistent} @@ -7905,7 +7926,7 @@ that solves these problems for canonical LR, IELR, and LALR without sacrificing @code{%nonassoc}, default reductions, or state merging. You can enable LAC with the @code{%define parse.lac} directive. -@deffn {Directive} {%define parse.lac @var{VALUE}} +@deffn {Directive} {%define parse.lac} @var{value} Enable LAC to improve syntax error handling. @itemize @item @code{none} (default) @@ -8001,9 +8022,9 @@ resolution because they are useless in the generated parser. However, keeping unreachable states is sometimes useful when trying to understand the relationship between the parser and the grammar. -@deffn {Directive} {%define lr.keep-unreachable-state @var{VALUE}} +@deffn {Directive} {%define lr.keep-unreachable-state} @var{value} Request that Bison allow unreachable states to remain in the parser tables. -@var{VALUE} must be a Boolean. The default is @code{false}. +@var{value} must be a Boolean. The default is @code{false}. @end deffn There are a few caveats to consider: @@ -10278,7 +10299,7 @@ depends whether you use unions, or variants. @node Split Symbols @subsubsection Split Symbols -Therefore the interface is as follows. +The interface is as follows. @deftypemethod {parser} {int} yylex (semantic_type* @var{yylval}, location_type* @var{yylloc}, @var{type1} @var{arg1}, ...) @deftypemethodx {parser} {int} yylex (semantic_type* @var{yylval}, @var{type1} @var{arg1}, ...) @@ -10977,8 +10998,7 @@ You can create documentation for generated parsers using Javadoc. Contrary to C parsers, Java parsers do not use global variables; the state of the parser is always local to an instance of the parser class. Therefore, all Java parsers are ``pure'', and the @code{%pure-parser} -and @samp{%define api.pure} directives does not do anything when used in -Java. +and @code{%define api.pure} directives do nothing when used in Java. Push parsers are currently unsupported in Java and @code{%define api.push-pull} have no effect. @@ -11612,7 +11632,7 @@ or @quotation My parser includes support for an @samp{#include}-like feature, in which case I run @code{yyparse} from @code{yyparse}. This fails -although I did specify @samp{%define api.pure}. +although I did specify @samp{%define api.pure full}. @end quotation These problems typically come not from Bison itself, but from diff --git a/tests/actions.at b/tests/actions.at index 099237c3..8278293d 100644 --- a/tests/actions.at +++ b/tests/actions.at @@ -79,7 +79,7 @@ AT_CLEANUP m4_pushdef([AT_TEST], [AT_SETUP([Initial location: $1 $2]) -AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "$1" $2 %parse-param { int x }]) +AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "$1" $2]) AT_DATA_GRAMMAR([[input.y]], [[%defines /* FIXME: Required by lalr1.cc in Bison 2.6. */ %locations @@ -87,7 +87,6 @@ AT_DATA_GRAMMAR([[input.y]], %skeleton "$1" ]$2[ ]$3[ -%parse-param { int x } // Useless, but used to force yyerror purity. %code { # include @@ -113,11 +112,11 @@ exp: { ]AT_SKEL_CC_IF([[std::cerr << @$ << std::endl]], int main (void) {]AT_SKEL_CC_IF([[ - yy::parser p (0); + yy::parser p; p.set_debug_level (!!getenv("YYDEBUG")); return p.parse ();]], [[ yydebug = !!getenv("YYDEBUG"); - return !!yyparse (0);]])[ + return !!yyparse (]AT_PARAM_IF([0])[);]])[ } ]]) @@ -132,10 +131,12 @@ AT_CLEANUP ## FIXME: test Java, and iterate over skeletons. AT_TEST([yacc.c]) -AT_TEST([yacc.c], [%define api.pure]) +AT_TEST([yacc.c], [%define api.pure full]) +AT_TEST([yacc.c], [%define api.pure %parse-param { int x }]) AT_TEST([yacc.c], [%define api.push-pull both]) -AT_TEST([yacc.c], [%define api.push-pull both %define api.pure]) +AT_TEST([yacc.c], [%define api.push-pull both %define api.pure full]) AT_TEST([glr.c]) +AT_TEST([glr.c], [%define api.pure]) AT_TEST([lalr1.cc]) AT_TEST([glr.cc]) @@ -146,7 +147,7 @@ AT_TEST([glr.cc]) ## Weirdly enough, to trigger the warning with GCC 4.7, we must not ## use fprintf, so run the test twice: once to check the warning ## (absence thereof), and another time to check the value. -AT_TEST([yacc.c], [%define api.pure], +AT_TEST([yacc.c], [%define api.pure full], [[%{ # define YYLTYPE int # define YY_LOCATION_PRINT(Stream, Loc) \ @@ -157,7 +158,7 @@ AT_TEST([yacc.c], [%define api.pure], ]], [@&t@]) -AT_TEST([yacc.c], [%define api.pure], +AT_TEST([yacc.c], [%define api.pure full], [[%{ # define YYLTYPE int # define YY_LOCATION_PRINT(Stream, Loc) \ diff --git a/tests/calc.at b/tests/calc.at index f336b69e..565b31cc 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -606,8 +606,8 @@ AT_CHECK_CALC_LALR([%verbose]) AT_CHECK_CALC_LALR([%yacc]) AT_CHECK_CALC_LALR([%define parse.error verbose]) -AT_CHECK_CALC_LALR([%define api.pure %locations]) -AT_CHECK_CALC_LALR([%define api.push-pull both %define api.pure %locations]) +AT_CHECK_CALC_LALR([%define api.pure full %locations]) +AT_CHECK_CALC_LALR([%define api.push-pull both %define api.pure full %locations]) AT_CHECK_CALC_LALR([%define parse.error verbose %locations]) AT_CHECK_CALC_LALR([%define parse.error verbose %locations %defines %define api.prefix "calc" %verbose %yacc]) @@ -617,8 +617,8 @@ AT_CHECK_CALC_LALR([%debug]) AT_CHECK_CALC_LALR([%define parse.error verbose %debug %locations %defines %name-prefix "calc" %verbose %yacc]) AT_CHECK_CALC_LALR([%define parse.error verbose %debug %locations %defines %define api.prefix "calc" %verbose %yacc]) -AT_CHECK_CALC_LALR([%define api.pure %define parse.error verbose %debug %locations %defines %name-prefix "calc" %verbose %yacc]) -AT_CHECK_CALC_LALR([%define api.push-pull both %define api.pure %define parse.error verbose %debug %locations %defines %define api.prefix "calc" %verbose %yacc]) +AT_CHECK_CALC_LALR([%define api.pure full %define parse.error verbose %debug %locations %defines %name-prefix "calc" %verbose %yacc]) +AT_CHECK_CALC_LALR([%define api.push-pull both %define api.pure full %define parse.error verbose %debug %locations %defines %define api.prefix "calc" %verbose %yacc]) AT_CHECK_CALC_LALR([%define api.pure %define parse.error verbose %debug %locations %defines %define api.prefix "calc" %verbose %yacc %parse-param {semantic_value *result} %parse-param {int *count}]) diff --git a/tests/local.at b/tests/local.at index e24c0acb..121dedf2 100644 --- a/tests/local.at +++ b/tests/local.at @@ -152,10 +152,6 @@ m4_pushdef([AT_PURE_IF], [m4_bmatch([$3], [%define *api\.pure\|%pure-parser], [m4_bmatch([$3], [%define *api\.pure *"?false"?], [$2], [$1])], [$2])]) -m4_pushdef([AT_PURE_AND_LOC_IF], -[m4_bmatch([$3], [%locations], [AT_PURE_IF($@)], [$2])]) -m4_pushdef([AT_GLR_OR_PARAM_IF], -[m4_bmatch([$3], [%glr-parser\|%parse-param], [$1], [$2])]) m4_pushdef([AT_NAME_PREFIX], [m4_bmatch([$3], [\(%define api\.prefix\|%name-prefix\) ".*"], [m4_bregexp([$3], [\(%define api\.prefix\|%name-prefix\) "\([^""]*\)"], [\2])], @@ -171,14 +167,26 @@ m4_pushdef([AT_API_prefix], [yy])]) m4_pushdef([AT_API_PREFIX], [m4_toupper(AT_API_prefix)]) -# yyerror receives the location if %location & %pure & (%glr or %parse-param). +# yyerror receives the location if %location, and if the parser is pure. For +# historical reasons, with the "yacc.c" skeleton, the location is not passed +# unless an additional "%parse-param" is present, or if the purity is defined +# as "full". m4_pushdef([AT_YYERROR_ARG_LOC_IF], -[AT_GLR_OR_PARAM_IF([AT_PURE_AND_LOC_IF([$1], [$2])], +[AT_LOCATION_IF([AT_PURE_IF([m4_bmatch([$3], + m4_quote(m4_join([\|], + [%define api.pure "?full"?], + [%glr-parser], + [%parse-param], + [%skeleton "?glr.c"?])), + [$1], [$2])], + [$2])], [$2])]) -# yyerror always sees the locations (when activated), except if -# (yacc & pure & !param). FIXME: This is wrong. See the manual. + +# yyerror always sees the locations (when activated) if the parser is impure. +# When the parser is pure, yyerror sees the location if it is received as an +# argument. m4_pushdef([AT_YYERROR_SEES_LOC_IF], -[AT_LOCATION_IF([AT_YACC_IF([AT_PURE_IF([AT_PARAM_IF([$1], [$2])], +[AT_LOCATION_IF([AT_YACC_IF([AT_PURE_IF([AT_YYERROR_ARG_LOC_IF([$1], [$2])], [$1])], [$1])], [$2])]) @@ -254,8 +262,6 @@ m4_popdef([AT_API_prefix]) m4_popdef([AT_TOKEN_PREFIX]) m4_popdef([AT_TOKEN_CTOR_IF]) m4_popdef([AT_NAME_PREFIX]) -m4_popdef([AT_GLR_OR_PARAM_IF]) -m4_popdef([AT_PURE_AND_LOC_IF]) m4_popdef([AT_LOCATION_TYPE_IF]) m4_popdef([AT_LOCATION_IF]) m4_popdef([AT_PARSE_PARAMS]) @@ -360,6 +366,7 @@ static }]dnl ]) +# AT_YYERROR_FORMALS # AT_YYERROR_PROTOTYPE # AT_YYERROR_DECLARE_EXTERN # AT_YYERROR_DECLARE @@ -368,7 +375,7 @@ static # Must be called inside a AT_BISON_OPTION_PUSHDEFS/POPDEFS pair. m4_define([AT_YYERROR_FORMALS], [m4_case(AT_LANG, -[c], [AT_YYERROR_ARG_LOC_IF([AT_YYLTYPE *llocp, ])AT_PARSE_PARAMS [const char *msg]])[]dnl +[c], [AT_YYERROR_ARG_LOC_IF([AT_YYLTYPE const * const llocp, ])AT_PARSE_PARAMS [const char *msg]])[]dnl ]) m4_define([AT_YYERROR_PROTOTYPE],