mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 12:23:04 +00:00
c++: introduce api.value.automove
Based on work by Frank Heckenbach. See http://lists.gnu.org/archive/html/bug-bison/2018-04/msg00000.html and http://lists.gnu.org/archive/html/bug-bison/2018-09/msg00019.html. * data/lalr1.cc (b4_rhs_value): Use YY_MOVE api.rhs.automove is set. * doc/bison.texi (%define Summary): Document api.rhs.automove. * examples/variant-11.yy: Use it. * tests/local.at (AT_AUTOMOVE_IF): New. * tests/c++.at (Variants): Check move semantics.
This commit is contained in:
@@ -83,9 +83,14 @@ m4_define([b4_rhs_state],
|
||||
# --------------------------------------
|
||||
# Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH
|
||||
# symbols on RHS.
|
||||
m4_define([b4_rhs_value],
|
||||
m4_define([_b4_rhs_value],
|
||||
[b4_symbol_value([b4_rhs_data([$1], [$2]).value], [$3])])
|
||||
|
||||
m4_define([b4_rhs_value],
|
||||
[b4_percent_define_ifdef([api.value.automove],
|
||||
[YY_MOVE(_b4_rhs_value($@))],
|
||||
[_b4_rhs_value($@)])])
|
||||
|
||||
|
||||
# b4_rhs_location(RULE-LENGTH, NUM)
|
||||
# ---------------------------------
|
||||
|
||||
@@ -5982,6 +5982,66 @@ introduced in Bison 3.0
|
||||
@c api.token.prefix
|
||||
|
||||
|
||||
@c ================================================== api.value.automove
|
||||
@deffn Directive {%define api.value.automove}
|
||||
|
||||
@itemize @bullet
|
||||
@item Language(s):
|
||||
C++
|
||||
|
||||
@item Purpose:
|
||||
Let occurrences of semantic values of the right-hand sides of a rule be
|
||||
implicitly turned in rvalues. When enabled, a grammar such as:
|
||||
|
||||
@example
|
||||
exp:
|
||||
"number" @{ $$ = make_number ($1); @}
|
||||
| exp "+" exp @{ $$ = make_binary (add, $1, $3); @}
|
||||
| "(" exp ")" @{ $$ = $2; @}
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
is actually compiled as if you had written:
|
||||
|
||||
@example
|
||||
exp:
|
||||
"number" @{ $$ = make_number (std::move ($1)); @}
|
||||
| exp "+" exp @{ $$ = make_binary (add,
|
||||
std::move ($1),
|
||||
std::move ($3)); @}
|
||||
| "(" exp ")" @{ $$ = std::move ($2); @}
|
||||
@end example
|
||||
|
||||
Using a value several times with automove enabled is typically an error.
|
||||
For instance, instead of:
|
||||
|
||||
@example
|
||||
exp: "twice" exp @{ $$ = make_binary (add, $2, $2); @}
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
write:
|
||||
|
||||
@example
|
||||
exp: "twice" exp @{ auto v = $2; $$ = make_binary (add, v, v); @}
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
It is tempting to use @code{std::move} on one of the @code{v}, but the
|
||||
argument evaluation order in C++ is unspecified.
|
||||
|
||||
@item Accepted Values:
|
||||
Boolean.
|
||||
|
||||
@item Default Value:
|
||||
@code{false}
|
||||
@item History:
|
||||
introduced in Bison 3.2
|
||||
@end itemize
|
||||
@end deffn
|
||||
@c api.value.automove
|
||||
|
||||
|
||||
@c ================================================== api.value.type
|
||||
@deffn Directive {%define api.value.type} @var{support}
|
||||
@deffnx Directive {%define api.value.type} @{@var{type}@}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
%defines
|
||||
%define api.token.constructor
|
||||
%define api.value.type variant
|
||||
%define api.value.automove
|
||||
%define parse.assert
|
||||
%locations
|
||||
|
||||
@@ -96,11 +97,11 @@ result:
|
||||
|
||||
list:
|
||||
%empty { /* Generates an empty string list */ }
|
||||
| list item { $$ = std::move ($1); $$.emplace_back (std::move ($2)); }
|
||||
| list item { $$ = $1; $$.emplace_back ($2); }
|
||||
;
|
||||
|
||||
item:
|
||||
TEXT { $$ = std::move ($1); }
|
||||
TEXT { $$ = $1; }
|
||||
| NUMBER { $$ = make_string_uptr (to_string ($1)); }
|
||||
;
|
||||
%%
|
||||
|
||||
71
tests/c++.at
71
tests/c++.at
@@ -241,6 +241,21 @@ AT_DATA_GRAMMAR([list.y],
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined __cplusplus && 201103L <= __cplusplus
|
||||
string (string&& s)
|
||||
: val_(std::move(s.val_))
|
||||
{
|
||||
s.val_.clear();
|
||||
}
|
||||
|
||||
string& operator= (string&& s)
|
||||
{
|
||||
val_ = std::move(s.val_);
|
||||
s.val_.clear ();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
friend
|
||||
std::ostream& operator<< (std::ostream& o, const string& s)
|
||||
{
|
||||
@@ -384,9 +399,60 @@ namespace yy
|
||||
]AT_MAIN_DEFINE[
|
||||
]])
|
||||
|
||||
AT_DATA_SOURCE([[modern.cc]],
|
||||
[[#include <iostream>
|
||||
int main()
|
||||
{
|
||||
#if defined __cplusplus && 201103L <= __cplusplus
|
||||
std::cout << "Modern C++: " << __cplusplus << '\n';
|
||||
return 0;
|
||||
#else
|
||||
std::cout << "Legac++\n";
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
]])
|
||||
|
||||
AT_FOR_EACH_CXX([
|
||||
AT_FULL_COMPILE([list])
|
||||
AT_PARSER_CHECK([./list], 0,
|
||||
|
||||
# Are we compiling with modern C++ enabled?
|
||||
AT_COMPILE_CXX([modern])
|
||||
AT_CHECK([./modern], [ignore], [ignore])
|
||||
if test $at_status = 0; then
|
||||
modern=true
|
||||
else
|
||||
modern=false
|
||||
fi
|
||||
|
||||
if AT_AUTOMOVE_IF([$modern], [false]); then
|
||||
AT_PARSER_CHECK([./list], 0,
|
||||
[[(0, 1, 2, 4, 6)
|
||||
]],
|
||||
[[Destroy: ""
|
||||
Destroy: ""
|
||||
Destroy: 1
|
||||
Destroy: ""
|
||||
Destroy: ()
|
||||
Destroy: ""
|
||||
Destroy: ""
|
||||
Destroy: ()
|
||||
Destroy: ""
|
||||
Destroy: 3
|
||||
Destroy: ()
|
||||
Destroy: ""
|
||||
Destroy: ""
|
||||
Destroy: ()
|
||||
Destroy: ()
|
||||
Destroy: 5
|
||||
Destroy: ()
|
||||
Destroy: ""
|
||||
Destroy: ""
|
||||
Destroy: ()
|
||||
Destroy: (0, 1, 2, 4, 6)
|
||||
]])
|
||||
else
|
||||
AT_PARSER_CHECK([./list], 0,
|
||||
[[(0, 1, 2, 4, 6)
|
||||
]],
|
||||
[[Destroy: "0"
|
||||
@@ -411,6 +477,7 @@ Destroy: "6"
|
||||
Destroy: (0, 1, 2, 4)
|
||||
Destroy: (0, 1, 2, 4, 6)
|
||||
]])
|
||||
fi
|
||||
])
|
||||
|
||||
AT_BISON_OPTION_POPDEFS
|
||||
@@ -419,11 +486,13 @@ AT_CLEANUP
|
||||
|
||||
AT_TEST([[%skeleton "lalr1.cc"]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.value.automove]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %locations]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_}]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations]])
|
||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %define api.token.constructor %define api.token.prefix {TOK_} %locations %define api.value.automove]])
|
||||
|
||||
m4_popdef([AT_TEST])
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@ m4_define([AT_BISON_OPTION_PUSHDEFS],
|
||||
m4_define([_AT_BISON_OPTION_PUSHDEFS],
|
||||
[m4_if([$1$2], $[1]$[2], [],
|
||||
[m4_fatal([$0: Invalid arguments: $@])])dnl
|
||||
m4_pushdef([AT_AUTOMOVE_IF],
|
||||
[m4_bmatch([$3], [%define api\.value\.automove], [$1], [$2])])
|
||||
m4_pushdef([AT_DEFINES_IF],
|
||||
[m4_bmatch([$3], [%defines], [$1], [$2])])
|
||||
m4_pushdef([AT_DEBUG_IF],
|
||||
|
||||
Reference in New Issue
Block a user