diff --git a/NEWS b/NEWS index eaa0a3de..d3a860b5 100644 --- a/NEWS +++ b/NEWS @@ -301,6 +301,21 @@ GNU Bison NEWS "function declared 'noreturn' should not return") have also been addressed. +** New %define variable: api.location.type (glr.cc, lalr1.cc) + + The %define variable api.location.type defines the name of the type to use + for locations. When defined, Bison no longer generates the position.hh + and location.hh files, nor does the parser will include them: the user is + then responsible to define her type. + + This can be used in programs with several parsers to factor their location + and position files: let one of them generate them, and the others just use + them. + + This feature was actually introduced, but not documented, in Bison 2.5, + under the name "location_type" (which is maintained for backward + compatibility). + * Noteworthy changes in release 2.6.2 (2012-08-03) [stable] ** Bug fixes diff --git a/data/c++.m4 b/data/c++.m4 index 86fd291f..f4235ab8 100644 --- a/data/c++.m4 +++ b/data/c++.m4 @@ -28,7 +28,7 @@ b4_percent_define_default([[parser_class_name]], [[parser]]) # Don't do that so that we remember whether we're using a user # request, or the default value. # -# b4_percent_define_default([[location_type]], [[location]]) +# b4_percent_define_default([[api.location.type]], [[location]]) b4_percent_define_default([[filename_type]], [[std::string]]) b4_percent_define_default([[api.namespace]], m4_defn([b4_prefix])) @@ -130,7 +130,7 @@ m4_define([b4_public_types_declare], typedef ]b4_api_PREFIX[STYPE semantic_type; #endif]b4_locations_if([ /// Symbol locations. - typedef b4_percent_define_get([[location_type]], + typedef b4_percent_define_get([[api.location.type]], [[location]]) location_type;])[ /// Syntax errors thrown from user actions. diff --git a/data/glr.cc b/data/glr.cc index 8f0027ee..88dad042 100644 --- a/data/glr.cc +++ b/data/glr.cc @@ -47,7 +47,7 @@ m4_define([b4_pure_flag], [1]) m4_include(b4_pkgdatadir/[c++.m4]) -b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [m4_include(b4_pkgdatadir/[location.cc])])]) m4_define([b4_parser_class_name], @@ -219,14 +219,14 @@ b4_percent_code_get([[requires]])[ #include #include #include ]b4_defines_if([ -b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [[#include "location.hh"]])])])[ ]b4_YYDEBUG_define[ ]b4_namespace_open[ ]b4_defines_if([], -[b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +[b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [b4_position_define b4_location_define])])])[ diff --git a/data/java.m4 b/data/java.m4 index 988ac39a..c16ab0b1 100644 --- a/data/java.m4 +++ b/data/java.m4 @@ -197,8 +197,8 @@ m4_define([b4_throws], [b4_percent_define_get([[throws]])]) b4_percent_define_default([[init_throws]], []) m4_define([b4_init_throws], [b4_percent_define_get([[init_throws]])]) -b4_percent_define_default([[location_type]], [Location]) -m4_define([b4_location_type], [b4_percent_define_get([[location_type]])]) +b4_percent_define_default([[api.location.type]], [Location]) +m4_define([b4_location_type], [b4_percent_define_get([[api.location.type]])]) b4_percent_define_default([[position_type]], [Position]) m4_define([b4_position_type], [b4_percent_define_get([[position_type]])]) diff --git a/data/lalr1.cc b/data/lalr1.cc index 13f33d1e..31687589 100644 --- a/data/lalr1.cc +++ b/data/lalr1.cc @@ -120,7 +120,7 @@ m4_pushdef([b4_copyright_years], m4_define([b4_parser_class_name], [b4_percent_define_get([[parser_class_name]])]) -b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [# Backward compatibility. m4_define([b4_location_constructors]) m4_include(b4_pkgdatadir/[location.cc])])]) @@ -139,7 +139,7 @@ m4_define([b4_shared_declarations], # include # include ]b4_defines_if([[ # include "stack.hh" -]b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +]b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [[# include "location.hh"]])])])[ ]b4_YYDEBUG_define[ @@ -148,7 +148,7 @@ m4_define([b4_shared_declarations], ]b4_defines_if([], [b4_stack_define -b4_locations_if([b4_percent_define_ifdef([[location_type]], [], +b4_locations_if([b4_percent_define_ifdef([[api.location.type]], [], [b4_position_define b4_location_define])])])[ diff --git a/doc/bison.texi b/doc/bison.texi index 5b233a48..d18167fa 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -331,6 +331,7 @@ C++ Location Values * C++ position:: One point in the source file * C++ location:: Two points in the source file +* User Defined Location Type:: Required interface for locations A Complete C++ Example @@ -5473,6 +5474,22 @@ The parser namespace is @code{foo} and @code{yylex} is referenced as @end itemize @c namespace +@c ================================================== api.location.type +@item @code{api.location.type} +@findex %define api.location.type + +@itemize @bullet +@item Language(s): C++ + +@item Purpose: Define the location type. +@xref{User Defined Location Type}. + +@item Accepted Values: String + +@item Default Value: none + +@item History: introduced in Bison 2.7 +@end itemize @c ================================================== api.prefix @item api.prefix @@ -5481,7 +5498,7 @@ The parser namespace is @code{foo} and @code{yylex} is referenced as @itemize @bullet @item Language(s): All -@item Purpose: Rename exported symbols +@item Purpose: Rename exported symbols. @xref{Multiple Parsers, ,Multiple Parsers in the Same Program}. @item Accepted Values: String @@ -9564,8 +9581,10 @@ in the following files: @table @file @item position.hh @itemx location.hh -The definition of the classes @code{position} and @code{location}, -used for location tracking when enabled. @xref{C++ Location Values}. +The definition of the classes @code{position} and @code{location}, used for +location tracking when enabled. These files are not generated if the +@code{%define} variable @code{api.location.type} is defined. @xref{C++ +Location Values}. @item stack.hh An auxiliary class @code{stack} used by the parser. @@ -9724,10 +9743,13 @@ is some time and/or some talented C++ hacker willing to contribute to Bison. @c - %define filename_type "const symbol::Symbol" When the directive @code{%locations} is used, the C++ parser supports -location tracking, see @ref{Tracking Locations}. Two auxiliary classes -define a @code{position}, a single point in a file, and a @code{location}, a -range composed of a pair of @code{position}s (possibly spanning several -files). +location tracking, see @ref{Tracking Locations}. + +By default, two auxiliary classes define a @code{position}, a single point +in a file, and a @code{location}, a range composed of a pair of +@code{position}s (possibly spanning several files). But if the +@code{%define} variable @code{api.location.type} is defined, then these +classes will not be generated, and the user defined type will be used. @tindex uint In this section @code{uint} is an abbreviation for @code{unsigned int}: in @@ -9736,6 +9758,7 @@ genuine code only the latter is used. @menu * C++ position:: One point in the source file * C++ location:: Two points in the source file +* User Defined Location Type:: Required interface for locations @end menu @node C++ position @@ -9839,6 +9862,63 @@ Report @var{p} on @var{o}, taking care of special cases such as: no @code{filename} defined, or equal filename/line or column. @end deftypefun +@node User Defined Location Type +@subsubsection User Defined Location Type +@findex %define api.location.type + +Instead of using the built-in types you may use the @code{%define} variable +@code{api.location.type} to specify your own type: + +@example +%define api.location.type @var{LocationType} +@end example + +The requirements over your @var{LocationType} are: +@itemize +@item +it must be copyable; + +@item +in order to compute the (default) value of @code{@@$} in a reduction, the +parser basically runs +@example +@@$.begin = @@$1.begin; +@@$.end = @@$@var{N}.end; // The location of last right-hand side symbol. +@end example +@noindent +so there must be copyable @code{begin} and @code{end} members; + +@item +alternatively you may redefine the computation of the default location, in +which case these members are not required (@pxref{Location Default Action}); + +@item +if traces are enabled, then there must exist an @samp{std::ostream& + operator<< (std::ostream& o, const @var{LocationType}& s)} function. +@end itemize + +@sp 1 + +In programs with several C++ parsers, you may also use the @code{%define} +variable @code{api.location.type} to share a common set of built-in +definitions for @code{position} and @code{location}. For instance, one +parser @file{master/parser.yy} might use: + +@example +%defines +%locations +%define namespace "master::" +@end example + +@noindent +to generate the @file{master/position.hh} and @file{master/location.hh} +files, reused by other parsers as follows: + +@example +%define location_type "master::location" +%code requires @{ #include @} +@end example + @node C++ Parser Interface @subsection C++ Parser Interface @c - define parser_class_name diff --git a/src/graphviz.c b/src/graphviz.c index 0007f9e0..e62ee1d4 100644 --- a/src/graphviz.c +++ b/src/graphviz.c @@ -25,12 +25,14 @@ #include #include "files.h" +#include "gram.h" #include "graphviz.h" +#include "tables.h" /* Return an unambiguous printable representation for NAME, suitable for C strings. Use slot 2 since the user may use slots 0 and 1. */ -static char const * +static char * quote (char const *name) { return quotearg_n_style (2, c_quoting_style, name); @@ -51,12 +53,13 @@ start_graph (FILE *fout) "digraph %s\n" "{\n", quote (grammar_file)); + fprintf (fout, "node [shape=box]\n"); } void output_node (int id, char const *label, FILE *fout) { - fprintf (fout, " %d [label=%s]\n", id, quote (label)); + fprintf (fout, " %d [label=\"%s\"]\n", id, label); } void @@ -69,6 +72,98 @@ output_edge (int source, int destination, char const *label, fputs ("]\n", fout); } +char const * +escape (char const *name) +{ + char *q = quote (name); + q[strlen (q) - 1] = '\0'; + return q + 1; +} + +static void +no_reduce_bitset_init (state const *s, bitset *no_reduce_set) +{ + int n; + *no_reduce_set = bitset_create (ntokens, BITSET_FIXED); + bitset_zero (*no_reduce_set); + FOR_EACH_SHIFT (s->transitions, n) + bitset_set (*no_reduce_set, TRANSITION_SYMBOL (s->transitions, n)); + for (n = 0; n < s->errs->num; ++n) + if (s->errs->symbols[n]) + bitset_set (*no_reduce_set, s->errs->symbols[n]->number); +} + +static bool +print_token (struct obstack *out, bool first, char const *tok) +{ + char const *q = escape (tok); + + if (! first) + obstack_sgrow (out, ","); + obstack_sgrow (out, q); + return false; +} + +void +output_red (state const *s, reductions const *reds, FILE *fout) +{ + bitset no_reduce_set; + int j; + int source = s->number; + struct obstack oout; + + no_reduce_bitset_init (s, &no_reduce_set); + obstack_init (&oout); + + for (j = 0; j < reds->num; ++j) + { + bool disabled = false; + bool first = true; + int ruleno = reds->rules[j]->user_number; + rule *default_reduction = NULL; + + if (yydefact[s->number] != 0) + default_reduction = &rules[yydefact[s->number] - 1]; + + /* First, print the edges that represent each possible reduction for + the given state. */ + obstack_printf (&oout, " %1$d -> \"%1$dR%2$d\" [label=\"", + source, ruleno); + if (default_reduction && default_reduction == reds->rules[j]) + first = print_token (&oout, true, "$default"); + else + { + int i; + for (i = 0; i < ntokens; i++) + if (bitset_test (reds->lookahead_tokens[j], i)) + { + first = print_token (&oout, first, symbols[i]->tag); + if (bitset_test (no_reduce_set, i)) + disabled = true; + } + } + obstack_sgrow (&oout, "\" style=solid]\n"); + + /* Then, print the reduction's representation. Done later since + we need to know whether this reduction is disabled. */ + obstack_printf (&oout, + " \"%dR%d\" " + "[style=filled shape=diamond fillcolor=%s " + "label=\"R%d\"]\n", + source, ruleno, + disabled ? "firebrick1" : "yellowgreen", + ruleno); + + /* If no lookahead tokens were valid transitions, this reduction is + actually disabled, so don't print it. */ + if (first) + (void) obstack_finish0 (&oout); + else + fprintf (fout, obstack_finish0 (&oout)); + } + obstack_free (&oout, 0); +} + void finish_graph (FILE *fout) { diff --git a/src/graphviz.h b/src/graphviz.h index f27c1105..371b15c5 100644 --- a/src/graphviz.h +++ b/src/graphviz.h @@ -22,6 +22,8 @@ #ifndef GRAPHVIZ_H_ # define GRAPHVIZ_H_ +#include "state.h" + /// Begin a Dot graph. /// \param fout output stream. void start_graph (FILE *fout); @@ -42,8 +44,18 @@ void output_node (int id, char const *label, FILE *fout); void output_edge (int source, int destination, char const *label, char const *style, FILE *fout); +/// Output a reduction. +/// \param s current state +/// \param reds the set of reductions +/// \param fout output stream. +void output_red (state const *s, reductions const *reds, FILE *fout); + /// End a Dot graph. /// \param fout output stream. void finish_graph (FILE *fout); +/// Escape a lookahead token. +/// \param name the token. +char const *escape (char const *name); + #endif /* ! GRAPHVIZ_H_ */ diff --git a/src/muscle-tab.c b/src/muscle-tab.c index 4b268983..1789280f 100644 --- a/src/muscle-tab.c +++ b/src/muscle-tab.c @@ -398,6 +398,7 @@ muscle_percent_variable_update (char const *variable, location variable_loc) const conversion_type conversion[] = { { "api.push_pull", "api.push-pull", }, + { "location_type", "api.location.type", }, { "lr.keep_unreachable_states", "lr.keep-unreachable-states", }, { "namespace", "api.namespace", }, }; @@ -416,23 +417,17 @@ muscle_percent_variable_update (char const *variable, location variable_loc) } void -muscle_percent_define_insert (char const *variable, location variable_loc, +muscle_percent_define_insert (char const *var, location variable_loc, char const *value, muscle_percent_define_how how) { - char const *name; - char const *loc_name; - char const *syncline_name; - char const *how_name; - - /* Permit certain names with underscores for backward compatibility. */ - variable = muscle_percent_variable_update (variable, variable_loc); - - name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); - loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")"); - syncline_name = + /* Backward compatibility. */ + char const *variable = muscle_percent_variable_update (var, variable_loc); + char const *name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); + char const *loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")"); + char const *syncline_name = UNIQSTR_CONCAT ("percent_define_syncline(", variable, ")"); - how_name = UNIQSTR_CONCAT ("percent_define_how(", variable, ")"); + char const *how_name = UNIQSTR_CONCAT ("percent_define_how(", variable, ")"); /* Command-line options are processed before the grammar file. */ if (how == MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE diff --git a/src/print_graph.c b/src/print_graph.c index 4847c4cc..ca1dc24e 100644 --- a/src/print_graph.c +++ b/src/print_graph.c @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include +#include #include "system.h" #include "LR0.h" @@ -54,7 +55,7 @@ print_core (struct obstack *oout, state *s) snritems = nitemset; } - obstack_printf (oout, "%d", s->number); + obstack_printf (oout, "state %d\\n", s->number); for (i = 0; i < snritems; i++) { item_number *sp; @@ -68,15 +69,15 @@ print_core (struct obstack *oout, state *s) r = item_number_as_rule_number (*sp); - obstack_printf (oout, "\n%s -> ", rules[r].lhs->tag); + obstack_printf (oout, "%d: %s -> ", r, escape (rules[r].lhs->tag)); for (sp = rules[r].rhs; sp < sp1; sp++) - obstack_printf (oout, "%s ", symbols[*sp]->tag); + obstack_printf (oout, "%s ", escape (symbols[*sp]->tag)); obstack_1grow (oout, '.'); for (/* Nothing */; *sp >= 0; ++sp) - obstack_printf (oout, " %s", symbols[*sp]->tag); + obstack_printf (oout, " %s", escape (symbols[*sp]->tag)); /* Experimental feature: display the lookahead tokens. */ if (report_flag & report_lookahead_tokens @@ -92,15 +93,17 @@ print_core (struct obstack *oout, state *s) bitset_iterator biter; int k; char const *sep = ""; - obstack_sgrow (oout, "["); + obstack_1grow (oout, '['); BITSET_FOR_EACH (biter, reds->lookahead_tokens[redno], k, 0) { - obstack_printf (oout, "%s%s", sep, symbols[k]->tag); + obstack_sgrow (oout, sep); + obstack_sgrow (oout, escape (symbols[k]->tag)); sep = ", "; } - obstack_sgrow (oout, "]"); + obstack_1grow (oout, ']'); } } + obstack_sgrow (oout, "\\l"); } } @@ -117,6 +120,9 @@ print_actions (state const *s, FILE *fgraph) transitions const *trans = s->transitions; + /* Display reductions. */ + output_red (s, s->reductions, fgraph); + if (!trans->num && !s->reductions) return; diff --git a/src/system.h b/src/system.h index 0395a0c0..987ebe2a 100644 --- a/src/system.h +++ b/src/system.h @@ -55,6 +55,10 @@ #include #include +#define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) +#define STREQ(L, R) (strcmp(L, R) == 0) +#define STRNEQ(L, R) (!STREQ(L, R)) + #ifndef UINTPTR_MAX /* This isn't perfect, but it's good enough for Bison, which needs only to hash pointers. */ @@ -220,8 +224,6 @@ typedef size_t uintptr_t; (obstack_1grow (Obs, '\0'), (char *) obstack_finish (Obs)) - - /*-----------------------------------------. | Extensions to use for the output files. | `-----------------------------------------*/ diff --git a/tests/calc.at b/tests/calc.at index 1a481cf1..b959bfaf 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -716,7 +716,7 @@ m4_define([AT_CHECK_CALC_LALR1_CC], AT_CHECK_CALC_LALR1_CC([]) AT_CHECK_CALC_LALR1_CC([%locations]) -AT_CHECK_CALC_LALR1_CC([%locations %define location_type Span]) +AT_CHECK_CALC_LALR1_CC([%locations %define api.location.type Span]) AT_CHECK_CALC_LALR1_CC([%defines %locations %define parse.error verbose %name-prefix "calc" %verbose %yacc]) AT_CHECK_CALC_LALR1_CC([%locations %define parse.error verbose %define api.prefix "calc" %verbose %yacc]) @@ -750,7 +750,7 @@ m4_define([AT_CHECK_CALC_GLR_CC], AT_CHECK_CALC_GLR_CC([]) AT_CHECK_CALC_GLR_CC([%locations]) -AT_CHECK_CALC_GLR_CC([%locations %define location_type Span]) +AT_CHECK_CALC_GLR_CC([%locations %define api.location.type Span]) AT_CHECK_CALC_GLR_CC([%defines %define parse.error verbose %name-prefix "calc" %verbose %yacc]) AT_CHECK_CALC_GLR_CC([%define parse.error verbose %define api.prefix "calc" %verbose %yacc]) diff --git a/tests/headers.at b/tests/headers.at index 73095ce0..4192c05b 100644 --- a/tests/headers.at +++ b/tests/headers.at @@ -131,7 +131,7 @@ AT_SETUP([Several parsers]) # self-contained, and can be compiled by a C++ compiler. m4_pushdef([AT_TEST], [AT_BISON_OPTION_PUSHDEFS([%define api.prefix "$1_" $2]) -AT_DATA_GRAMMAR([$1.AT_SKEL_CC_IF([yy], [y])], +AT_DATA_GRAMMAR([$1.y], [[%define api.prefix "$1_" $2 %error-verbose @@ -161,7 +161,7 @@ exp: ]AT_YYLEX_DEFINE(["$1"])[ ]]) -AT_BISON_CHECK([-d -o AT_SKEL_CC_IF([$1.cc $1.yy], [$1.c $1.y])]) +AT_BISON_CHECK([-d -o $1.AT_SKEL_CC_IF([cc], [c]) $1.y]) # Check there is no 'yy' left. # C++ output relies on namespaces and still uses yy a lot. diff --git a/tests/java.at b/tests/java.at index ec0f4a8e..9b48453f 100644 --- a/tests/java.at +++ b/tests/java.at @@ -751,7 +751,7 @@ AT_SETUP([Java stype, position_class and location_class]) AT_CHECK_JAVA_MINIMAL([[ %define stype "java.awt.Color" %type start; -%define location_type "MyLoc" +%define api.location.type "MyLoc" %define position_type "MyPos" %code { class MyPos {} }]], [[$$ = $1;]], [[MyPos]]) AT_CHECK([[grep 'java.awt.Color' YYParser.java]], [0], [ignore]) @@ -761,7 +761,7 @@ AT_CHECK([[$EGREP -v ' */?\*' YYParser.java | grep 'Location']], [1], [ignore]) AT_CHECK_JAVA_MINIMAL_W_LEXER([[ %define stype "java.awt.Color" %type start; -%define location_type "MyLoc" +%define api.location.type "MyLoc" %define position_type "MyPos" %code { class MyPos {} }]], [], [[return EOF;]], [], [[$$ = $1;]], diff --git a/tests/local.at b/tests/local.at index f9babdfa..7458412a 100644 --- a/tests/local.at +++ b/tests/local.at @@ -137,7 +137,7 @@ m4_pushdef([AT_LEXPARAM_IF], m4_pushdef([AT_LOCATION_IF], [m4_bmatch([$3], [%locations], [$1], [$2])]) m4_pushdef([AT_LOCATION_TYPE_IF], -[m4_bmatch([$3], [%define location_type], [$1], [$2])]) +[m4_bmatch([$3], [%define \(api\.location\.type\|location_type\)], [$1], [$2])]) m4_pushdef([AT_PARAM_IF], [m4_bmatch([$3], [%parse-param], [$1], [$2])]) m4_pushdef([AT_PURE_IF], diff --git a/tests/synclines.at b/tests/synclines.at index 041ae194..63ae6858 100644 --- a/tests/synclines.at +++ b/tests/synclines.at @@ -216,4 +216,54 @@ exp: '0'; [input.y:8: #error "8" ]) +## -------------------- ## +## %code top syncline. ## +## -------------------- ## + +AT_TEST([%code top syncline], +[[%code top { +#error "2" +} +%{ +]AT_YYERROR_DECLARE_EXTERN[ +]AT_YYLEX_DECLARE_EXTERN[ +%} +%% +exp: '0'; +%% +]], +[input.y:2: #error "2" +]) + +m4_popdef([AT_TEST]) + +## ----------- ## +## %no-lines. ## +## ----------- ## + +m4_pushdef([AT_TEST], +[AT_SETUP([%no-lines]) + +AT_BISON_OPTION_PUSHDEFS([%skeleton "$1" %defines]) +AT_DATA_GRAMMAR([input.y], +[%skeleton "$1" %defines +%{ +]AT_YYERROR_DECLARE_EXTERN[ +]AT_YYLEX_DECLARE_EXTERN[ +%} +%% +exp: '0' +]) +AT_BISON_CHECK([--no-lines -o input.AT_SKEL_CC_IF([cc], [c]) -d input.y]) +AT_CHECK([[grep '#line' ]AT_SKEL_CC_IF([*.cc *.hh], [*.c *.h])], 1) +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])