lalr1.cc: support move semantics

Modern C++ (i.e., C++11 and later) introduced "move only" types: types such
as std::unique_ptr<T> that can never be duplicated.  They must never be
copied (by assignments and constructors), they must be "moved".  The
implementation of lalr1.cc used to copy symbols (including their semantic
values).  This commit ensures that values are only moved in modern C++, yet
remain compatible with C++98/C++03.

Suggested by Frank Heckenbach, who provided a full implementation on
top of C++17's std::variant.
See http://lists.gnu.org/archive/html/bug-bison/2018-03/msg00002.html,
and https://lists.gnu.org/archive/html/bison-patches/2018-04/msg00002.html.

Symbols (terminal/non terminal) are handled by several functions that used
to take const-refs, which resulted eventually in a copy pushed on the stack.
With modern C++ (C++11 and later) the callers must use std::move, and the
callees must take their arguments as rvalue refs (foo&&).  In order to avoid
duplicating these functions to support both legacy C++ and modern C++, let's
introduce macros (YY_MOVE, YY_RVREF, etc.)  that rely on copy-semantics for
C++98/03, and move-semantics for modern C++.

That's easy for inner types, when the parser's functions pass arguments to
each other.  Functions facing the user (make_NUMBER, make_STRING, etc.)
should support both rvalue-refs (for instance to support move-only types:
make_INT (std::make_unique<int> (1))), and lvalue-refs (so that we can pass
a variable: make_INT (my_int)).  To avoid the multiplication of the
signatures (there is also the location), let's take the argument by value.

See:
https://lists.gnu.org/archive/html/bison-patches/2018-09/msg00024.html.

* data/c++.m4 (b4_cxx_portability): New.
(basic_symbol): In C++11, replace copy-ctors with move-ctors.
In C++11, replace copies with moves.
* data/lalr1.cc (stack_symbol_type, yypush_): Likewise.
Use YY_MOVE to avoid useless copies.
* data/variant.hh (variant): Support move-semantics.
(make_SYMBOL): In C++11, in order to support both read-only lvalues,
and rvalues, take the argument as a copy.
* data/stack.hh (yypush_): Use rvalue-refs in C++11.
* tests/c++.at: Use move semantics.

* tests/headers.at: Adjust to the new macros (YY_MOVE, etc.).

* configure.ac (CXX98_CXXFLAGS, CXX11_CXXFLAGS, CXX14_CXXFLAGS)
(CXX17_CXXFLAGS, ENABLE_CXX11): New.
* tests/atlocal.in: Receive them.

* examples/variant.yy: Don't define things in std.
* examples/variant-11.test, examples/variant-11.yy: New.
Check the support of move-only types.
* examples/README, examples/local.mk: Adjust.
This commit is contained in:
Akim Demaille
2018-08-12 18:05:47 +02:00
parent e3257f88e2
commit f19ecae3b2
13 changed files with 332 additions and 89 deletions

View File

@@ -54,6 +54,8 @@ AC_PROG_CC_STDC
AC_PROG_CXX AC_PROG_CXX
AC_LANG_PUSH([C++]) AC_LANG_PUSH([C++])
gl_WARN_ADD([-fno-exceptions], [NO_EXCEPTIONS_CXXFLAGS]) gl_WARN_ADD([-fno-exceptions], [NO_EXCEPTIONS_CXXFLAGS])
gl_WARN_ADD([-std=c++11], [CXX11_CXXFLAGS])
AM_CONDITIONAL([ENABLE_CXX11], [test x"$CXX11_CXXFLAGS" != x])
AC_LANG_POP([C++]) AC_LANG_POP([C++])
# Gnulib (early checks). # Gnulib (early checks).

View File

@@ -50,6 +50,25 @@ m4_define([b4_inline],
[m4_fatal([$0: invalid argument: $1])])]) [m4_fatal([$0: invalid argument: $1])])])
# b4_cxx_portability
# ------------------
m4_define([b4_cxx_portability],
[// Support move semantics when possible.
#if defined __cplusplus && 201103L <= __cplusplus
# define YY_MOVE std::move
# define YY_MOVE_OR_COPY move
# define YY_MOVE_REF(Type) Type&&
# define YY_RVREF(Type) Type&&
# define YY_COPY(Type) Type
#else
# define YY_MOVE
# define YY_MOVE_OR_COPY copy
# define YY_MOVE_REF(Type) Type&
# define YY_RVREF(Type) const Type&
# define YY_COPY(Type) const Type&
#endif[]dnl
])
## ---------------- ## ## ---------------- ##
## Default values. ## ## Default values. ##
@@ -219,19 +238,20 @@ m4_define([b4_symbol_type_declare],
/// Default constructor. /// Default constructor.
basic_symbol (); basic_symbol ();
/// Copy constructor. /// Move or copy constructor.
basic_symbol (const basic_symbol& other); basic_symbol (YY_RVREF (basic_symbol) other);
]b4_variant_if([[ ]b4_variant_if([[
/// Constructor for valueless symbols, and symbols from each type. /// Constructor for valueless symbols, and symbols from each type.
]b4_type_foreach([b4_basic_symbol_constructor_declare])], [[ ]b4_type_foreach([b4_basic_symbol_constructor_declare])], [[
/// Constructor for valueless symbols. /// Constructor for valueless symbols.
basic_symbol (typename Base::kind_type t]b4_locations_if([, basic_symbol (typename Base::kind_type t]b4_locations_if([,
const location_type& l])[); YY_MOVE_REF (location_type) l])[);
/// Constructor for symbols with semantic value. /// Constructor for symbols with semantic value.
basic_symbol (typename Base::kind_type t, basic_symbol (typename Base::kind_type t,
const semantic_type& v]b4_locations_if([, YY_RVREF (semantic_type) v]b4_locations_if([,
const location_type& l])[);]])[ YY_RVREF (location_type) l])[);]])[
/// Destroy the symbol. /// Destroy the symbol.
~basic_symbol (); ~basic_symbol ();
@@ -312,13 +332,13 @@ m4_define([b4_public_types_define],
{} {}
template <typename Base> template <typename Base>
]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (const basic_symbol& other) ]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (YY_RVREF (basic_symbol) other)
: Base (other) : Base (YY_MOVE (other))
, value (]b4_variant_if([], [other.value])[)]b4_locations_if([ , value (]b4_variant_if([], [YY_MOVE (other.value)]))b4_locations_if([
, location (other.location)])[ , location (YY_MOVE (other.location))])[
{]b4_variant_if([ {]b4_variant_if([
b4_symbol_variant([other.type_get ()], [value], [copy], b4_symbol_variant([other.type_get ()], [value], [YY_MOVE_OR_COPY],
[other.value])])[ [YY_MOVE (other.value)])])[
} }
]b4_variant_if([[ ]b4_variant_if([[
@@ -328,7 +348,7 @@ m4_define([b4_public_types_define],
template <typename Base> template <typename Base>
]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join( ]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join(
[typename Base::kind_type t], [typename Base::kind_type t],
b4_locations_if([const location_type& l]))[) b4_locations_if([YY_MOVE_REF (location_type) l]))[)
: Base (t) : Base (t)
, value ()]b4_locations_if([ , value ()]b4_locations_if([
, location (l)])[ , location (l)])[
@@ -337,14 +357,14 @@ m4_define([b4_public_types_define],
template <typename Base> template <typename Base>
]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join( ]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join(
[typename Base::kind_type t], [typename Base::kind_type t],
[const semantic_type& v], [YY_RVREF (semantic_type) v],
b4_locations_if([const location_type& l]))[) b4_locations_if([YY_RVREF (location_type) l]))[)
: Base (t) : Base (t)
, value (]b4_variant_if([], [v])[)]b4_locations_if([ , value (]b4_variant_if([], [YY_MOVE (v)])[)]b4_locations_if([
, location (l)])[ , location (YY_MOVE (l))])[
{]b4_variant_if([[ {]b4_variant_if([[
(void) v; (void) v;
]b4_symbol_variant([this->type_get ()], [value], [copy], [v])])[}]])[ ]b4_symbol_variant([this->type_get ()], [value], [YY_MOVE_OR_COPY], [YY_MOVE (v)])])[}]])[
template <typename Base> template <typename Base>
]b4_parser_class_name[::basic_symbol<Base>::~basic_symbol () ]b4_parser_class_name[::basic_symbol<Base>::~basic_symbol ()
@@ -385,9 +405,9 @@ m4_define([b4_public_types_define],
{ {
super_type::move (s); super_type::move (s);
]b4_variant_if([b4_symbol_variant([this->type_get ()], [value], [move], ]b4_variant_if([b4_symbol_variant([this->type_get ()], [value], [move],
[s.value])], [YY_MOVE (s.value)])],
[value = s.value;])[]b4_locations_if([ [value = YY_MOVE (s.value);])[]b4_locations_if([
location = s.location;])[ location = YY_MOVE (s.location);])[
} }
// by_type. // by_type.

View File

@@ -153,13 +153,17 @@ m4_define([b4_shared_declarations],
# include <iostream> # include <iostream>
# include <stdexcept> # include <stdexcept>
# include <string> # include <string>
# include <vector>]b4_defines_if([[ # include <vector>
]b4_cxx_portability[
]b4_defines_if([[
# include "stack.hh" # include "stack.hh"
]b4_bison_locations_if([[# include "location.hh"]])])[ ]b4_bison_locations_if([[# include "location.hh"]])])[
]b4_variant_if([b4_variant_includes])[ ]b4_variant_if([b4_variant_includes])[
]b4_attribute_define[ ]b4_attribute_define[
]b4_null_define[ ]b4_null_define[
]b4_YYDEBUG_define[ ]b4_YYDEBUG_define[
]b4_namespace_open[ ]b4_namespace_open[
@@ -318,12 +322,12 @@ b4_location_define])])[
typedef basic_symbol<by_state> super_type; typedef basic_symbol<by_state> super_type;
/// Construct an empty symbol. /// Construct an empty symbol.
stack_symbol_type (); stack_symbol_type ();
/// Copy construct (for efficiency). /// Move or copy construction.
stack_symbol_type (const stack_symbol_type& that); stack_symbol_type (YY_RVREF (stack_symbol_type) that);
/// Steal the contents from \a sym to build this. /// Steal the contents from \a sym to build this.
stack_symbol_type (state_type s, symbol_type& sym); stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym);
/// Assignment, needed by push_back. /// Assignment, needed by push_back.
stack_symbol_type& operator= (stack_symbol_type& that); stack_symbol_type& operator= (YY_MOVE_REF (stack_symbol_type) that);
}; };
/// Stack type. /// Stack type.
@@ -337,7 +341,7 @@ b4_location_define])])[
/// if null, no trace is output. /// if null, no trace is output.
/// \param sym the symbol /// \param sym the symbol
/// \warning the contents of \a s.value is stolen. /// \warning the contents of \a s.value is stolen.
void yypush_ (const char* m, stack_symbol_type& sym); void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym);
/// Push a new look ahead token on the state on the stack. /// Push a new look ahead token on the state on the stack.
/// \param m a debug message to display /// \param m a debug message to display
@@ -345,7 +349,7 @@ b4_location_define])])[
/// \param s the state /// \param s the state
/// \param sym the symbol (for its value and location). /// \param sym the symbol (for its value and location).
/// \warning the contents of \a sym.value is stolen. /// \warning the contents of \a sym.value is stolen.
void yypush_ (const char* m, state_type s, symbol_type& sym); void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym);
/// Pop \a n symbols the three stacks. /// Pop \a n symbols the three stacks.
void yypop_ (unsigned n = 1); void yypop_ (unsigned n = 1);
@@ -588,34 +592,33 @@ m4_if(b4_prefix, [yy], [],
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type () ]b4_parser_class_name[::stack_symbol_type::stack_symbol_type ()
{} {}
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (const stack_symbol_type& that) ]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that)
: super_type (that.state]b4_variant_if([], [, that.value])[]b4_locations_if([, that.location])[) : super_type (YY_MOVE (that.state)]b4_variant_if([], [, YY_MOVE (that.value)])b4_locations_if([, YY_MOVE (that.location)])[)
{]b4_variant_if([ {]b4_variant_if([
b4_symbol_variant([that.type_get ()], b4_symbol_variant([that.type_get ()],
[value], [copy], [that.value])])[ [value], [YY_MOVE_OR_COPY], [YY_MOVE (that.value)])])[
} }
]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (state_type s, symbol_type& that) ]b4_parser_class_name[::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that)
: super_type (s]b4_variant_if([], [, that.value])[]b4_locations_if([, that.location])[) : super_type (s]b4_variant_if([], [, YY_MOVE (that.value)])[]b4_locations_if([, YY_MOVE (that.location)])[)
{]b4_variant_if([ {]b4_variant_if([
b4_symbol_variant([that.type_get ()], b4_symbol_variant([that.type_get ()],
[value], [move], [that.value])])[ [value], [move], [YY_MOVE (that.value)])])[
// that is emptied. // that is emptied.
that.type = empty_symbol; that.type = empty_symbol;
} }
]b4_parser_class_name[::stack_symbol_type& ]b4_parser_class_name[::stack_symbol_type&
]b4_parser_class_name[::stack_symbol_type::operator= (stack_symbol_type& that) ]b4_parser_class_name[::stack_symbol_type::operator= (YY_MOVE_REF (stack_symbol_type) that)
{ {
state = that.state; state = that.state;
]b4_variant_if([b4_symbol_variant([that.type_get ()], ]b4_variant_if([b4_symbol_variant([that.type_get ()],
[value], [move], [that.value])], [value], [move], [YY_MOVE (that.value)])],
[[value = that.value;]])[]b4_locations_if([ [[value = YY_MOVE (that.value);]])[]b4_locations_if([
location = that.location;])[ location = YY_MOVE (that.location);])[
return *this; return *this;
} }
template <typename Base> template <typename Base>
void void
]b4_parser_class_name[::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const ]b4_parser_class_name[::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const
@@ -649,18 +652,22 @@ m4_if(b4_prefix, [yy], [],
#endif #endif
void void
]b4_parser_class_name[::yypush_ (const char* m, stack_symbol_type& sym) ]b4_parser_class_name[::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym)
{ {
if (m) if (m)
YY_SYMBOL_PRINT (m, sym); YY_SYMBOL_PRINT (m, sym);
yystack_.push (sym); yystack_.push (YY_MOVE (sym));
} }
void void
]b4_parser_class_name[::yypush_ (const char* m, state_type s, symbol_type& sym) ]b4_parser_class_name[::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym)
{ {
stack_symbol_type t (s, sym); #if defined __cplusplus && 201103L <= __cplusplus
yypush_ (m, t); yypush_ (m, stack_symbol_type (s, YY_MOVE (sym)));
#else
stack_symbol_type ss(s, sym);
yypush_ (m, ss);
#endif
} }
void void
@@ -756,7 +763,7 @@ b4_dollar_popdef])[]dnl
location values to have been already stored, initialize these location values to have been already stored, initialize these
stacks with a primary value. */ stacks with a primary value. */
yystack_.clear (); yystack_.clear ();
yypush_ (YY_NULLPTR, 0, yyla); yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla));
// A new symbol was pushed on the stack. // A new symbol was pushed on the stack.
yynewstate: yynewstate:
@@ -818,7 +825,7 @@ b4_dollar_popdef])[]dnl
--yyerrstatus_; --yyerrstatus_;
// Shift the lookahead token. // Shift the lookahead token.
yypush_ ("Shifting", yyn, yyla); yypush_ ("Shifting", yyn, YY_MOVE (yyla));
goto yynewstate; goto yynewstate;
/*-----------------------------------------------------------. /*-----------------------------------------------------------.
@@ -887,7 +894,7 @@ b4_dollar_popdef])[]dnl
YY_STACK_PRINT (); YY_STACK_PRINT ();
// Shift the result of the reduction. // Shift the result of the reduction.
yypush_ (YY_NULLPTR, yylhs); yypush_ (YY_NULLPTR, YY_MOVE (yylhs));
} }
goto yynewstate; goto yynewstate;
@@ -976,7 +983,7 @@ b4_dollar_popdef])[]dnl
// Shift the error token. // Shift the error token.
error_token.state = yyn; error_token.state = yyn;
yypush_ ("Shifting", error_token); yypush_ ("Shifting", YY_MOVE (error_token));
} }
goto yynewstate; goto yynewstate;

View File

@@ -62,7 +62,7 @@ m4_define([b4_stack_define],
/// ///
/// Close to move-semantics. /// Close to move-semantics.
void void
push (T& t) push (YY_MOVE_REF (T) t)
{ {
seq_.push_back (T()); seq_.push_back (T());
operator[](0).move (t); operator[](0).move (t);
@@ -142,6 +142,8 @@ b4_copyright([Stack handling for Bison parsers in C++])[
# include <vector> # include <vector>
]b4_cxx_portability[
]b4_namespace_open[ ]b4_namespace_open[
]b4_stack_define[ ]b4_stack_define[
]b4_namespace_close[ ]b4_namespace_close[

View File

@@ -101,11 +101,11 @@ m4_define([b4_variant_define],
/// Construct and fill. /// Construct and fill.
template <typename T> template <typename T>
variant (const T& t)]b4_parse_assert_if([ variant (YY_RVREF (T) t)]b4_parse_assert_if([
: yytypeid_ (&typeid (T))])[ : yytypeid_ (&typeid (T))])[
{ {
YYASSERT (sizeof (T) <= S); YYASSERT (sizeof (T) <= S);
new (yyas_<T> ()) T (t); new (yyas_<T> ()) T (YY_MOVE (t));
} }
/// Destruction, allowed only if empty. /// Destruction, allowed only if empty.
@@ -183,10 +183,26 @@ m4_define([b4_variant_define],
move (self_type& other) move (self_type& other)
{ {
build<T> (); build<T> ();
# if defined __cplusplus && 201103L <= __cplusplus
as<T> () = YY_MOVE (other.as<T> ());
# else
swap<T> (other); swap<T> (other);
# endif
other.destroy<T> (); other.destroy<T> ();
} }
# if defined __cplusplus && 201103L <= __cplusplus
/// Move the content of \a other to this.
template <typename T>
void
move (self_type&& other)
{
build<T> ();
as<T> () = YY_MOVE (other.as<T> ());
other.destroy<T> ();
}
#endif
/// Copy the content of \a other to this. /// Copy the content of \a other to this.
template <typename T> template <typename T>
void void
@@ -293,8 +309,8 @@ m4_define([b4_symbol_constructor_declare_],
symbol_type symbol_type
make_[]b4_symbol_([$1], [id]) (dnl make_[]b4_symbol_([$1], [id]) (dnl
b4_join(b4_symbol_if([$1], [has_type], b4_join(b4_symbol_if([$1], [has_type],
[const b4_symbol([$1], [type])& v]), [YY_COPY (b4_symbol([$1], [type])) v]),
b4_locations_if([const location_type& l]))); b4_locations_if([YY_COPY (location_type) l])));
])])]) ])])])
@@ -318,12 +334,12 @@ m4_define([b4_symbol_constructor_define_],
b4_parser_class_name::symbol_type b4_parser_class_name::symbol_type
b4_parser_class_name::make_[]b4_symbol_([$1], [id]) (dnl b4_parser_class_name::make_[]b4_symbol_([$1], [id]) (dnl
b4_join(b4_symbol_if([$1], [has_type], b4_join(b4_symbol_if([$1], [has_type],
[const b4_symbol([$1], [type])& v]), [YY_COPY (b4_symbol([$1], [type])) v]),
b4_locations_if([const location_type& l]))) b4_locations_if([YY_COPY (location_type) l])))
{ {
return symbol_type (b4_join([token::b4_symbol([$1], [id])], return symbol_type (b4_join([token::b4_symbol([$1], [id])],
b4_symbol_if([$1], [has_type], [v]), b4_symbol_if([$1], [has_type], [YY_MOVE (v)]),
b4_locations_if([l]))); b4_locations_if([YY_MOVE (l)])));
} }
])])]) ])])])
@@ -335,8 +351,8 @@ b4_join(b4_symbol_if([$1], [has_type],
m4_define([b4_basic_symbol_constructor_declare], m4_define([b4_basic_symbol_constructor_declare],
[[ basic_symbol (]b4_join( [[ basic_symbol (]b4_join(
[typename Base::kind_type t], [typename Base::kind_type t],
b4_symbol_if([$1], [has_type], const b4_symbol([$1], [type])[& v]), b4_symbol_if([$1], [has_type], [YY_RVREF (b4_symbol([$1], [type])) v]),
b4_locations_if([const location_type& l]))[); b4_locations_if([YY_RVREF (location_type) l]))[);
]]) ]])
# b4_basic_symbol_constructor_define # b4_basic_symbol_constructor_define
@@ -346,11 +362,11 @@ m4_define([b4_basic_symbol_constructor_define],
[[ template <typename Base> [[ template <typename Base>
]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join( ]b4_parser_class_name[::basic_symbol<Base>::basic_symbol (]b4_join(
[typename Base::kind_type t], [typename Base::kind_type t],
b4_symbol_if([$1], [has_type], const b4_symbol([$1], [type])[& v]), b4_symbol_if([$1], [has_type], [YY_RVREF (b4_symbol([$1], [type])) v]),
b4_locations_if([const location_type& l]))[) b4_locations_if([YY_RVREF (location_type) l]))[)
: Base (t)]b4_symbol_if([$1], [has_type], [ : Base (t)]b4_symbol_if([$1], [has_type], [
, value (v)])[]b4_locations_if([ , value (YY_MOVE (v))])[]b4_locations_if([
, location (l)])[ , location (YY_MOVE (l))])[
{} {}
]]) ]])

View File

@@ -13,6 +13,10 @@ A C++ example that uses variants (they allow to use any C++ type as semantic
value type) and symbol constructors (they ensure consistency between value type) and symbol constructors (they ensure consistency between
declared token type and effective semantic value). declared token type and effective semantic value).
* variant-11.yy
Another C++ example, closely related to the previous one, but exhibiting
support for C++11's move semantics.
----- -----
Local Variables: Local Variables:
@@ -22,19 +26,11 @@ End:
Copyright (C) 2018 Free Software Foundation, Inc. Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler. Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
This program is free software: you can redistribute it and/or modify any later version published by the Free Software Foundation; with no
it under the terms of the GNU General Public License as published by Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
the Free Software Foundation, either version 3 of the License, or Texts. A copy of the license is included in the "GNU Free
(at your option) any later version. Documentation License" file as part of this distribution.
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/>.
# LocalWords: mfcalc calc parsers yy # LocalWords: mfcalc calc parsers yy

View File

@@ -46,14 +46,24 @@ $(extracted): %D%/extracted.stamp
## Examples. ## ## Examples. ##
## ---------- ## ## ---------- ##
examplesdir = $(docdir)/examples examplesdir = $(docdir)/examples
dist_examples_DATA = %D%/README %D%/variant.yy dist_examples_DATA = %D%/README %D%/variant.yy %D%/variant-11.yy
check_PROGRAMS += %D%/variant check_PROGRAMS += %D%/variant
nodist_%C%_variant_SOURCES = %D%/variant.yy nodist_%C%_variant_SOURCES = %D%/variant.yy
%C%_variant_CPPFLAGS = -I$(top_builddir) %C%_variant_CPPFLAGS = -I$(top_builddir)
dist_TESTS += %D%/variant.test dist_TESTS += %D%/variant.test
%D%/variant.cc: $(BISON_IN) $(dist_pkgdata_DATA)
if ENABLE_CXX11
check_PROGRAMS += %D%/variant-11
nodist_%C%_variant_11_SOURCES = %D%/variant-11.yy
%C%_variant_11_CXXFLAGS = $(CXX11_CXXFLAGS)
%C%_variant_11_CPPFLAGS = -I$(top_builddir)
dist_TESTS += %D%/variant-11.test
%D%/variant-11.cc: $(BISON_IN) $(dist_pkgdata_DATA)
endif
include %D%/calc++/local.mk include %D%/calc++/local.mk
include %D%/mfcalc/local.mk include %D%/mfcalc/local.mk

19
examples/variant-11.test Normal file
View File

@@ -0,0 +1,19 @@
#! /bin/sh
# Copyright (C) 2018 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/>.
: >input
run 0 "{I have three numbers for you., 1, 2, 3, And that's all!}"

159
examples/variant-11.yy Normal file
View File

@@ -0,0 +1,159 @@
/*
Copyright (C) 2008-2015, 2018 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/>.
*/
%debug
%language "c++"
%defines
%define api.token.constructor
%define api.value.type variant
%define parse.assert
%locations
%code requires // *.hh
{
#include <memory> // std::unique_ptr
#include <string>
#include <vector>
using string_uptr = std::unique_ptr<std::string>;
using string_uptrs = std::vector<string_uptr>;
}
%code // *.cc
{
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
namespace yy
{
// Prototype of the yylex function providing subsequent tokens.
static parser::symbol_type yylex ();
// Print a vector of strings.
std::ostream&
operator<< (std::ostream& o, const string_uptrs& ss)
{
o << '{';
const char *sep = "";
for (const auto& s: ss)
{
o << sep << *s;
sep = ", ";
}
return o << '}';
}
}
template <typename... Args>
string_uptr
make_string_uptr (Args&&... args)
{
// std::make_unique is C++14.
return std::unique_ptr<std::string>(new std::string{std::forward<Args>(args)...});
}
// Convert to string.
template <typename T>
std::string
to_string (const T& t)
{
auto&& o = std::ostringstream{};
o << t;
return o.str ();
}
}
%token <string_uptr> TEXT;
%token <int> NUMBER;
%printer { yyo << '(' << &$$ << ") " << $$; } <*>;
%printer { yyo << *$$; } <string_uptr>;
%token END_OF_FILE 0;
%type <string_uptr> item;
%type <string_uptrs> list;
%%
result:
list { std::cout << $1 << '\n'; }
;
list:
%empty { /* Generates an empty string list */ }
| list item { $$ = std::move ($1); $$.emplace_back (std::move ($2)); }
;
item:
TEXT { $$ = std::move ($1); }
| NUMBER { $$ = make_string_uptr (to_string ($1)); }
;
%%
namespace yy
{
// The yylex function providing subsequent tokens:
// TEXT "I have three numbers for you."
// NUMBER 1
// NUMBER 2
// NUMBER 3
// TEXT "And that's all!"
// END_OF_FILE
static
parser::symbol_type
yylex ()
{
static auto count = 0u;
auto stage = count;
++count;
auto loc = parser::location_type{nullptr, stage + 1, stage + 1};
switch (stage)
{
case 0:
return parser::make_TEXT (make_string_uptr ("I have three numbers for you."), std::move (loc));
case 1:
case 2:
case 3:
return parser::make_NUMBER (stage, std::move (loc));
case 4:
return parser::make_TEXT (make_string_uptr ("And that's all!"), std::move (loc));
default:
return parser::make_END_OF_FILE (std::move (loc));
}
}
// Mandatory error function
void
parser::error (const parser::location_type& loc, const std::string& msg)
{
std::cerr << loc << ": " << msg << '\n';
}
}
int
main ()
{
auto&& p = yy::parser{};
p.set_debug_level (!!getenv ("YYDEBUG"));
return p.parse ();
}
// Local Variables:
// mode: C++
// End:

View File

@@ -37,16 +37,12 @@ typedef std::vector<std::string> strings_type;
#include <iterator> #include <iterator>
#include <sstream> #include <sstream>
// Prototype of the yylex function providing subsequent tokens.
namespace yy namespace yy
{ {
// Prototype of the yylex function providing subsequent tokens.
static parser::symbol_type yylex (); static parser::symbol_type yylex ();
}
// Printing a vector of strings. // Print a vector of strings.
// Koening look up will look into std, since that's an std::vector.
namespace std
{
std::ostream& std::ostream&
operator<< (std::ostream& o, const strings_type& ss) operator<< (std::ostream& o, const strings_type& ss)
{ {
@@ -62,10 +58,10 @@ typedef std::vector<std::string> strings_type;
} }
} }
// Conversion to string. // Convert to string.
template <typename T> template <typename T>
std::string std::string
string_cast (const T& t) to_string (const T& t)
{ {
std::ostringstream o; std::ostringstream o;
o << t; o << t;
@@ -94,7 +90,7 @@ list:
item: item:
TEXT { std::swap ($$, $1); } TEXT { std::swap ($$, $1); }
| NUMBER { $$ = string_cast ($1); } | NUMBER { $$ = to_string ($1); }
; ;
%% %%

View File

@@ -54,6 +54,9 @@ BISON_CXX_WORKS='@BISON_CXX_WORKS@'
# Compiler flags to disable exception support. # Compiler flags to disable exception support.
NO_EXCEPTIONS_CXXFLAGS='@NO_EXCEPTIONS_CXXFLAGS@' NO_EXCEPTIONS_CXXFLAGS='@NO_EXCEPTIONS_CXXFLAGS@'
# Requiring a specific C++ standard.
CXX11_CXXFLAGS='@CXX11_CXXFLAGS@'
# Be sure that the C++ compiler is not broken because of gnulib. This # Be sure that the C++ compiler is not broken because of gnulib. This
# cannot be checked in configure (gnulib is not parameterized yet), # cannot be checked in configure (gnulib is not parameterized yet),
# and checking this in every C++ test in AC_COMPILE_CXX is too costly. # and checking this in every C++ test in AC_COMPILE_CXX is too costly.

View File

@@ -151,8 +151,12 @@ int main()
// stack_symbol_type: construction, accessor. // stack_symbol_type: construction, accessor.
{ {
#if defined __cplusplus && 201103L <= __cplusplus
auto ss = parser::stack_symbol_type(1, parser::make_INT(123));
#else
parser::symbol_type s = parser::make_INT(123); parser::symbol_type s = parser::make_INT(123);
parser::stack_symbol_type ss(1, s); parser::stack_symbol_type ss(1, s);
#endif
std::cerr << ss.value.as<int>() << '\n'; std::cerr << ss.value.as<int>() << '\n';
} }
@@ -161,8 +165,12 @@ int main()
parser::stack_type st; parser::stack_type st;
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
#if defined __cplusplus && 201103L <= __cplusplus
auto ss = parser::stack_symbol_type(1, parser::make_INT(123));
#else
parser::symbol_type s = parser::make_INT(123); parser::symbol_type s = parser::make_INT(123);
parser::stack_symbol_type ss(1, s); parser::stack_symbol_type ss(1, s);
#endif
st.push(ss); st.push(ss);
} }
} }

View File

@@ -311,15 +311,20 @@ AT_TEST([x8], [%define api.pure %define api.push-pull both])
AT_CHECK([[$PERL -n -0777 -e ' AT_CHECK([[$PERL -n -0777 -e '
s{/\*.*?\*/}{}gs; s{/\*.*?\*/}{}gs;
s{//.*}{}g; s{//.*}{}g;
s{\b(YYChar s{\b((defined|if)\ YYDEBUG
|YYChar
|YYPUSH_MORE(?:_DEFINED)? |YYPUSH_MORE(?:_DEFINED)?
|YYUSE |YYUSE
|YY_ATTRIBUTE(?:_PURE|_UNUSED)? |YY_ATTRIBUTE(?:_PURE|_UNUSED)?
|YY_COPY
|YY_IGNORE_MAYBE_UNINITIALIZED_(?:BEGIN|END) |YY_IGNORE_MAYBE_UNINITIALIZED_(?:BEGIN|END)
|YY_INITIAL_VALUE |YY_INITIAL_VALUE
|YY_\w+_INCLUDED |YY_MOVE
|YY_MOVE_OR_COPY
|YY_MOVE_REF
|YY_NULLPTR |YY_NULLPTR
|(defined|if)\ YYDEBUG |YY_RVREF
|YY_\w+_INCLUDED
)\b}{}gx; )\b}{}gx;
while (/^(.*YY.*)$/gm) while (/^(.*YY.*)$/gm)
{ {