"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
# 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]))
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.
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],
#include <stdexcept>
#include <string>
#include <iostream>]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])])])[
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]])])
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])])])
# include <stdexcept>
# include <string>]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[
]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])])])[
* 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
@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
@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
@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.
@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
@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
@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 <master/location.hh> @}
+@end example
+
@node C++ Parser Interface
@subsection C++ Parser Interface
@c - define parser_class_name
#include <quotearg.h>
#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);
"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
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)
{
#ifndef GRAPHVIZ_H_
# define GRAPHVIZ_H_
+#include "state.h"
+
/// Begin a Dot graph.
/// \param fout output stream.
void start_graph (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_ */
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", },
};
}
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
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
+#include <quotearg.h>
#include "system.h"
#include "LR0.h"
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;
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
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");
}
}
transitions const *trans = s->transitions;
+ /* Display reductions. */
+ output_red (s, s->reductions, fgraph);
+
if (!trans->num && !s->reductions)
return;
#include <unistd.h>
#include <inttypes.h>
+#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. */
(obstack_1grow (Obs, '\0'), (char *) obstack_finish (Obs))
-
-
/*-----------------------------------------.
| Extensions to use for the output files. |
`-----------------------------------------*/
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])
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])
# 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
]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.
AT_CHECK_JAVA_MINIMAL([[
%define stype "java.awt.Color"
%type<java.awt.Color> start;
-%define location_type "MyLoc"
+%define api.location.type "MyLoc"
%define position_type "MyPos"
%code { class MyPos {} }]], [[$$ = $<java.awt.Color>1;]], [[MyPos]])
AT_CHECK([[grep 'java.awt.Color' YYParser.java]], [0], [ignore])
AT_CHECK_JAVA_MINIMAL_W_LEXER([[
%define stype "java.awt.Color"
%type<java.awt.Color> start;
-%define location_type "MyLoc"
+%define api.location.type "MyLoc"
%define position_type "MyPos"
%code { class MyPos {} }]], [], [[return EOF;]], [],
[[$$ = $<java.awt.Color>1;]],
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],
[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])