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
|
# Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH
|
||||||
# symbols on RHS.
|
# symbols on RHS.
|
||||||
m4_define([b4_rhs_value],
|
m4_define([_b4_rhs_value],
|
||||||
[b4_symbol_value([b4_rhs_data([$1], [$2]).value], [$3])])
|
[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)
|
# b4_rhs_location(RULE-LENGTH, NUM)
|
||||||
# ---------------------------------
|
# ---------------------------------
|
||||||
|
|||||||
@@ -5982,6 +5982,66 @@ introduced in Bison 3.0
|
|||||||
@c api.token.prefix
|
@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
|
@c ================================================== api.value.type
|
||||||
@deffn Directive {%define api.value.type} @var{support}
|
@deffn Directive {%define api.value.type} @var{support}
|
||||||
@deffnx Directive {%define api.value.type} @{@var{type}@}
|
@deffnx Directive {%define api.value.type} @{@var{type}@}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
%defines
|
%defines
|
||||||
%define api.token.constructor
|
%define api.token.constructor
|
||||||
%define api.value.type variant
|
%define api.value.type variant
|
||||||
|
%define api.value.automove
|
||||||
%define parse.assert
|
%define parse.assert
|
||||||
%locations
|
%locations
|
||||||
|
|
||||||
@@ -96,11 +97,11 @@ result:
|
|||||||
|
|
||||||
list:
|
list:
|
||||||
%empty { /* Generates an empty string list */ }
|
%empty { /* Generates an empty string list */ }
|
||||||
| list item { $$ = std::move ($1); $$.emplace_back (std::move ($2)); }
|
| list item { $$ = $1; $$.emplace_back ($2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
item:
|
item:
|
||||||
TEXT { $$ = std::move ($1); }
|
TEXT { $$ = $1; }
|
||||||
| NUMBER { $$ = make_string_uptr (to_string ($1)); }
|
| NUMBER { $$ = make_string_uptr (to_string ($1)); }
|
||||||
;
|
;
|
||||||
%%
|
%%
|
||||||
|
|||||||
69
tests/c++.at
69
tests/c++.at
@@ -241,6 +241,21 @@ AT_DATA_GRAMMAR([list.y],
|
|||||||
return *this;
|
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
|
friend
|
||||||
std::ostream& operator<< (std::ostream& o, const string& s)
|
std::ostream& operator<< (std::ostream& o, const string& s)
|
||||||
{
|
{
|
||||||
@@ -384,8 +399,59 @@ namespace yy
|
|||||||
]AT_MAIN_DEFINE[
|
]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_FOR_EACH_CXX([
|
||||||
AT_FULL_COMPILE([list])
|
AT_FULL_COMPILE([list])
|
||||||
|
|
||||||
|
# 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,
|
AT_PARSER_CHECK([./list], 0,
|
||||||
[[(0, 1, 2, 4, 6)
|
[[(0, 1, 2, 4, 6)
|
||||||
]],
|
]],
|
||||||
@@ -411,6 +477,7 @@ Destroy: "6"
|
|||||||
Destroy: (0, 1, 2, 4)
|
Destroy: (0, 1, 2, 4)
|
||||||
Destroy: (0, 1, 2, 4, 6)
|
Destroy: (0, 1, 2, 4, 6)
|
||||||
]])
|
]])
|
||||||
|
fi
|
||||||
])
|
])
|
||||||
|
|
||||||
AT_BISON_OPTION_POPDEFS
|
AT_BISON_OPTION_POPDEFS
|
||||||
@@ -419,11 +486,13 @@ AT_CLEANUP
|
|||||||
|
|
||||||
AT_TEST([[%skeleton "lalr1.cc"]])
|
AT_TEST([[%skeleton "lalr1.cc"]])
|
||||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert]])
|
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 %locations]])
|
||||||
AT_TEST([[%skeleton "lalr1.cc" %define parse.assert %code {\n#define TWO_STAGE_BUILD\n}]])
|
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]])
|
||||||
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_}]])
|
||||||
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]])
|
||||||
|
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])
|
m4_popdef([AT_TEST])
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ m4_define([AT_BISON_OPTION_PUSHDEFS],
|
|||||||
m4_define([_AT_BISON_OPTION_PUSHDEFS],
|
m4_define([_AT_BISON_OPTION_PUSHDEFS],
|
||||||
[m4_if([$1$2], $[1]$[2], [],
|
[m4_if([$1$2], $[1]$[2], [],
|
||||||
[m4_fatal([$0: Invalid arguments: $@])])dnl
|
[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_pushdef([AT_DEFINES_IF],
|
||||||
[m4_bmatch([$3], [%defines], [$1], [$2])])
|
[m4_bmatch([$3], [%defines], [$1], [$2])])
|
||||||
m4_pushdef([AT_DEBUG_IF],
|
m4_pushdef([AT_DEBUG_IF],
|
||||||
|
|||||||
Reference in New Issue
Block a user