mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 20:33:03 +00:00
See e3fdc37049: in C++ we generate
explicitly the code for the default action instead of simply copying
blindly the semantic value buffer. This is important when copying
raw memory is not enough, as exemplified by move-only types.
This is currently tested by examples/c++/variant.yy and variant-11.yy.
But it is safer to also have a test in the main test suite.
* tests/local.at (AT_REQUIRE_CXX_STD): Fix.
(AT_BISON_OPTION_PUSHDEFS, AT_BISON_OPTION_POPDEFS): Define/undefine
AT_BISON_OPTIONS.
* tests/c++.at (Default action): New.
1542 lines
34 KiB
Plaintext
1542 lines
34 KiB
Plaintext
# Checking the C++ Features. -*- Autotest -*-
|
|
|
|
# Copyright (C) 2004-2005, 2007-2015, 2018-2019 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/>.
|
|
|
|
AT_BANNER([[C++ Features.]])
|
|
|
|
|
|
## -------------------------- ##
|
|
## C++ Locations Unit Tests. ##
|
|
## -------------------------- ##
|
|
|
|
AT_SETUP([C++ Locations Unit Tests])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%locations %skeleton "lalr1.cc"])
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%code {#include <sstream>}
|
|
%locations
|
|
%debug
|
|
%skeleton "lalr1.cc"
|
|
%code
|
|
{
|
|
]AT_YYERROR_DECLARE[
|
|
]AT_YYLEX_DECLARE[
|
|
}
|
|
%%
|
|
exp: %empty;
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]AT_YYLEX_DEFINE[
|
|
|
|
template <typename T>
|
|
bool
|
|
check (const T& in, const std::string& s)
|
|
{
|
|
std::stringstream os;
|
|
os << in;
|
|
if (os.str () != s)
|
|
{
|
|
std::cerr << "fail: " << os.str () << ", expected: " << s << '\n';
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
int fail = 0;
|
|
]AT_YYLTYPE[ loc; fail += check (loc, "1.1");
|
|
fail += check (loc + 10, "1.1-10");
|
|
loc += 10; fail += check (loc, "1.1-10");
|
|
loc += -5; fail += check (loc, "1.1-5");
|
|
fail += check (loc - 5, "1.1");
|
|
loc -= 5; fail += check (loc, "1.1");
|
|
// Check that we don't go below.
|
|
// http://lists.gnu.org/archive/html/bug-bison/2013-02/msg00000.html
|
|
loc -= 10; fail += check (loc, "1.1");
|
|
|
|
loc.columns (10); loc.lines (10); fail += check (loc, "1.1-11.0");
|
|
loc.lines (-2); fail += check (loc, "1.1-9.0");
|
|
loc.lines (-10); fail += check (loc, "1.1");
|
|
|
|
]AT_YYLTYPE[ loc2 (YY_NULLPTR, 5, 10);
|
|
fail += check (loc2, "5.10");
|
|
fail += check (loc + loc2, "1.1-5.9");
|
|
loc += loc2; fail += check (loc, "1.1-5.9");
|
|
return !fail;
|
|
}
|
|
]])
|
|
|
|
AT_FOR_EACH_CXX([
|
|
AT_FULL_COMPILE([input])
|
|
AT_PARSER_CHECK([input], 0)
|
|
])
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
|
|
## -------------------------------------- ##
|
|
## C++ Variant-based Symbols Unit Tests. ##
|
|
## -------------------------------------- ##
|
|
|
|
# Not checking the grammar, only the variants and variant based
|
|
# symbols.
|
|
|
|
AT_SETUP([C++ Variant-based Symbols Unit Tests])
|
|
|
|
AT_KEYWORDS([variant])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %debug $1])
|
|
# Store strings and integers in a vector of strings.
|
|
AT_DATA_GRAMMAR([list.yy],
|
|
[[%skeleton "lalr1.cc"
|
|
%define api.value.type variant
|
|
%define parse.assert
|
|
%debug
|
|
|
|
%code top
|
|
{
|
|
// Get access to stack_symbol_type for the tests.
|
|
# define private public
|
|
}
|
|
%code provides
|
|
{
|
|
]AT_YYLEX_DECLARE[
|
|
}
|
|
|
|
%token <int> INT "int"
|
|
%type <std::vector<int>> exp
|
|
|
|
%printer { yyo << $$; } <int>
|
|
%printer
|
|
{
|
|
for (std::vector<int>::const_iterator i = $$.begin (); i != $$.end (); ++i)
|
|
{
|
|
if (i != $$.begin ())
|
|
yyo << ", ";
|
|
yyo << *i;
|
|
}
|
|
} <std::vector<int>>
|
|
|
|
%code requires { #include <vector> }
|
|
%code { int yylex (yy::parser::semantic_type* lvalp); }
|
|
|
|
%%
|
|
exp: "int" { $$.push_back ($1); }
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]AT_YYLEX_DEFINE[
|
|
|
|
template <typename Exp, typename Eff>
|
|
void assert_eq (const Exp& exp, const Eff& eff)
|
|
{
|
|
if (getenv ("DEBUG"))
|
|
std::cerr << "Assert: " << exp << " == " << eff << '\n';
|
|
if (exp != eff)
|
|
std::cerr << "Assertion failed: " << exp << " != " << eff << '\n';
|
|
}
|
|
|
|
int main()
|
|
{
|
|
using yy::parser;
|
|
// symbol_type: construction, accessor.
|
|
{
|
|
parser::symbol_type s = parser::make_INT (12);
|
|
assert_eq (s.value.as<int> (), 12);
|
|
}
|
|
|
|
// symbol_type: move constructor.
|
|
#if 201103L <= YY_CPLUSPLUS
|
|
{
|
|
auto s = parser::make_INT (42);
|
|
auto s2 = std::move (s);
|
|
assert_eq (s2.value.as<int> (), 42);
|
|
// Used to crash here, because s was improperly cleared, and
|
|
// its destructor tried to delete its (moved) value.
|
|
}
|
|
#endif
|
|
|
|
// symbol_type: copy constructor.
|
|
{
|
|
parser::symbol_type s = parser::make_INT (51);
|
|
parser::symbol_type s2 = s;
|
|
assert_eq (s.value.as<int> (), 51);
|
|
assert_eq (s2.value.as<int> (), 51);
|
|
}
|
|
|
|
// stack_symbol_type: construction, accessor.
|
|
{
|
|
#if 201103L <= YY_CPLUSPLUS
|
|
auto ss = parser::stack_symbol_type(1, parser::make_INT(123));
|
|
#else
|
|
parser::symbol_type s = parser::make_INT (123);
|
|
parser::stack_symbol_type ss(1, s);
|
|
#endif
|
|
assert_eq (ss.value.as<int> (), 123);
|
|
}
|
|
|
|
// Pushing on the stack.
|
|
// Sufficiently many so that it will be resized.
|
|
// Probably 3 times (starting at 200).
|
|
{
|
|
parser::stack_type st;
|
|
const int mucho = 1700;
|
|
const int int_reduction_state = 1; // Read list.output to find it.
|
|
for (int i = 0; i < mucho; ++i)
|
|
{
|
|
#if 201103L <= YY_CPLUSPLUS
|
|
st.push(parser::stack_symbol_type{int_reduction_state,
|
|
parser::make_INT (i)});
|
|
#else
|
|
parser::symbol_type s = parser::make_INT (i);
|
|
parser::stack_symbol_type ss (int_reduction_state, s);
|
|
st.push (ss);
|
|
#endif
|
|
}
|
|
for (int i = mucho - 1; 0 <= i; --i)
|
|
{
|
|
assert_eq (st[0].value.as<int>(), i);
|
|
st.pop ();
|
|
}
|
|
}
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([[-o list.cc list.yy]])
|
|
AT_FOR_EACH_CXX([
|
|
AT_COMPILE_CXX([list])
|
|
AT_PARSER_CHECK([list])
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
## --------------------------------------------------- ##
|
|
## Multiple occurrences of $n and api.value.automove. ##
|
|
## --------------------------------------------------- ##
|
|
|
|
AT_SETUP([Multiple occurrences of $n and api.value.automove])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
|
|
|
|
AT_DATA_GRAMMAR([input.yy],
|
|
[[%skeleton "lalr1.cc"
|
|
%define api.value.automove
|
|
%token <int> NUMBER "number"
|
|
%type <int> exp
|
|
%%
|
|
exp:
|
|
"number" { $$ = $1; $$; }
|
|
| "twice" exp { $$ = $2 + $2; }
|
|
| "thrice" exp[val] { $$ = $2 + $val + $2; }
|
|
]])
|
|
|
|
AT_BISON_CHECK([[-fcaret input.yy]], [0], [],
|
|
[[input.yy:16.33-34: warning: multiple occurrences of $2 with api.value.automove [-Wother]
|
|
16 | | "twice" exp { $$ = $2 + $2; }
|
|
| ^~
|
|
input.yy:17.33-36: warning: multiple occurrences of $2 with api.value.automove [-Wother]
|
|
17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
|
|
| ^~~~
|
|
input.yy:17.40-41: warning: multiple occurrences of $2 with api.value.automove [-Wother]
|
|
17 | | "thrice" exp[val] { $$ = $2 + $val + $2; }
|
|
| ^~
|
|
]])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
|
|
## ---------- ##
|
|
## Variants. ##
|
|
## ---------- ##
|
|
|
|
# Check that the variants are properly supported, including in error
|
|
# recovery.
|
|
|
|
# AT_TEST([DIRECTIVES])
|
|
# ---------------------
|
|
# Check the support of variants in C++, with the additional DIRECTIVES.
|
|
m4_pushdef([AT_TEST],
|
|
[AT_SETUP([Variants $1])
|
|
|
|
AT_KEYWORDS([variant])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%debug $1])
|
|
# Store strings and integers in a vector of strings.
|
|
AT_DATA_GRAMMAR([list.y],
|
|
[[%debug
|
|
%define api.value.type variant
|
|
]m4_bpatsubst([$1], [\\n], [
|
|
])[
|
|
|
|
%code top // code for the .cc file.
|
|
{
|
|
#include <cstdlib> // abort, getenv
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
class string
|
|
{
|
|
public:
|
|
string () {}
|
|
|
|
string (const std::string& s)
|
|
: val_(s)
|
|
{}
|
|
|
|
string (const string& s)
|
|
: val_(s.val_)
|
|
{}
|
|
|
|
string& operator= (const string& s)
|
|
{
|
|
val_ = s.val_;
|
|
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)
|
|
{
|
|
return o << s.val_;
|
|
}
|
|
|
|
private:
|
|
std::string val_;
|
|
};
|
|
|
|
typedef std::vector<string> strings_type;
|
|
|
|
namespace yy
|
|
{
|
|
// Must be available early, as is used in %destructor.
|
|
std::ostream&
|
|
operator<<(std::ostream& o, const strings_type& s)
|
|
{
|
|
o << '(';
|
|
for (strings_type::const_iterator i = s.begin (); i != s.end (); ++i)
|
|
{
|
|
if (i != s.begin ())
|
|
o << ", ";
|
|
o << *i;
|
|
}
|
|
return o << ')';
|
|
}
|
|
}
|
|
}
|
|
|
|
%code // code for the .cc file.
|
|
{
|
|
namespace yy
|
|
{
|
|
static
|
|
]AT_YYLEX_PROTOTYPE[;
|
|
|
|
// Conversion to string.
|
|
template <typename T>
|
|
inline
|
|
string
|
|
to_string (const T& t)
|
|
{
|
|
std::ostringstream o;
|
|
o << t;
|
|
return string (o.str ());
|
|
}
|
|
}
|
|
}
|
|
|
|
%token <::string> TEXT;
|
|
%token <int> NUMBER;
|
|
%token END_OF_FILE 0;
|
|
%token COMMA ","
|
|
|
|
// Starting with :: to ensure we don't output "<::" which starts by the
|
|
// digraph for the left square bracket.
|
|
%type <::string> item;
|
|
// Using the template type to exercize its parsing.
|
|
%type <::std::vector<string>> list;
|
|
|
|
%printer { yyo << $$; } <int> <::string> <::std::vector<string>>;
|
|
%destructor { std::cerr << "Destroy: " << $$ << '\n'; } <*>;
|
|
%destructor { std::cerr << "Destroy: \"" << $$ << "\"\n"; } <::string>;
|
|
%%
|
|
|
|
result:
|
|
list { std::cout << $][1 << '\n'; }
|
|
;
|
|
|
|
list:
|
|
item { $$.push_back ($][1); }
|
|
| list "," item { $$ = $][1; $$.push_back ($][3); }
|
|
| list error { $$ = $][1; }
|
|
;
|
|
|
|
item:
|
|
TEXT { $$ = $][1; }
|
|
| NUMBER { int v = $][1; if (v == 3) YYERROR; else $$ = to_string (v); }
|
|
;
|
|
%%
|
|
]AT_TOKEN_CTOR_IF([],
|
|
[[#ifdef TWO_STAGE_BUILD
|
|
# define BUILD(Type, Value) build<Type> () = Value
|
|
#else
|
|
# define BUILD(Type, Value) build (Value)
|
|
#endif
|
|
]])[
|
|
#define STAGE_MAX 5
|
|
namespace yy
|
|
{
|
|
static
|
|
]AT_YYLEX_PROTOTYPE[
|
|
{
|
|
// The 5 is a syntax error whose recovery requires that we discard
|
|
// the lookahead. This tests a regression, see
|
|
// <http://savannah.gnu.org/support/?108481>.
|
|
static char const *input = "0,1,2,3,45,6";
|
|
switch (int stage = *input++)
|
|
{
|
|
case 0:]AT_TOKEN_CTOR_IF([[
|
|
return parser::make_END_OF_FILE (]AT_LOCATION_IF([location ()])[);]],
|
|
[AT_LOCATION_IF([
|
|
*llocp = location ();])[
|
|
return parser::token::END_OF_FILE;]])[
|
|
|
|
case ',':]AT_TOKEN_CTOR_IF([[
|
|
return parser::make_COMMA (]AT_LOCATION_IF([location ()])[);]],
|
|
[AT_LOCATION_IF([
|
|
*llocp = location ();])[
|
|
return parser::token::COMMA;]])[
|
|
|
|
default:
|
|
stage = stage - '0';
|
|
if (stage % 2)
|
|
{]AT_TOKEN_CTOR_IF([[
|
|
return parser::make_NUMBER (stage]AT_LOCATION_IF([, location ()])[);]], [[
|
|
lvalp->BUILD (int, stage);]AT_LOCATION_IF([
|
|
*llocp = location ();])[
|
|
return parser::token::NUMBER;]])[
|
|
}
|
|
else
|
|
{]AT_TOKEN_CTOR_IF([[
|
|
return parser::make_TEXT (to_string (stage)]AT_LOCATION_IF([, location ()])[);]], [[
|
|
lvalp->BUILD (string, to_string (stage));]AT_LOCATION_IF([
|
|
*llocp = location ();])[
|
|
return parser::token::TEXT;]])[
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
]AT_YYERROR_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_FULL_COMPILE([list])
|
|
|
|
# Are we compiling with modern C++ enabled?
|
|
AT_COMPILE_CXX([modern])
|
|
here=. # Pacify cfg.mk's sc_at_parser_check.
|
|
AT_CHECK([$here/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"
|
|
Destroy: "0"
|
|
Destroy: 1
|
|
Destroy: "1"
|
|
Destroy: (0)
|
|
Destroy: "2"
|
|
Destroy: "2"
|
|
Destroy: (0, 1)
|
|
Destroy: ""
|
|
Destroy: 3
|
|
Destroy: (0, 1, 2)
|
|
Destroy: "4"
|
|
Destroy: "4"
|
|
Destroy: (0, 1, 2)
|
|
Destroy: (0, 1, 2, 4)
|
|
Destroy: 5
|
|
Destroy: (0, 1, 2, 4)
|
|
Destroy: "6"
|
|
Destroy: "6"
|
|
Destroy: (0, 1, 2, 4)
|
|
Destroy: (0, 1, 2, 4, 6)
|
|
]])
|
|
fi
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
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])
|
|
|
|
|
|
|
|
## ------------------------------------ ##
|
|
## Variants and Typed Midrule Actions. ##
|
|
## ------------------------------------ ##
|
|
|
|
AT_SETUP([Variants and Typed Midrule Actions])
|
|
|
|
# See http://lists.gnu.org/archive/html/bug-bison/2017-06/msg00000.html.
|
|
#
|
|
# Check that typed midrule actions behave properly (pre-construction
|
|
# of $$ before the user action, support of %printer and %destructor,
|
|
# etc.).
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
|
|
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%skeleton "lalr1.cc"
|
|
%defines
|
|
|
|
%debug
|
|
%define parse.assert
|
|
%define api.value.type variant
|
|
%define api.token.constructor
|
|
%define parse.error verbose
|
|
|
|
%code
|
|
{
|
|
#include <iostream>
|
|
namespace yy
|
|
{
|
|
static yy::parser::symbol_type yylex();
|
|
}
|
|
}
|
|
|
|
%token <int> NUMBER;
|
|
%type <int> expr;
|
|
%token EOI 0;
|
|
%printer { yyo << $$; } <int>;
|
|
%destructor { std::cerr << "destroy: " << $$ << '\n'; } <int>
|
|
%%
|
|
expr:
|
|
NUMBER { $$ = $1 * 10; }
|
|
| expr <int>{ $$ = 20; } NUMBER
|
|
{
|
|
std::cerr << "expr: " << $1 << ' ' << $2 << ' ' << $3 << '\n';
|
|
$$ = 40;
|
|
}
|
|
;
|
|
|
|
%%
|
|
namespace yy
|
|
{
|
|
parser::symbol_type yylex()
|
|
{
|
|
static int loc = 0;
|
|
switch (loc++)
|
|
{
|
|
case 0:
|
|
return parser::make_NUMBER (1);
|
|
case 1:
|
|
return parser::make_NUMBER (30);
|
|
default:
|
|
return parser::make_EOI ();
|
|
}
|
|
}
|
|
|
|
void parser::error(const std::string& message)
|
|
{
|
|
std::cerr << message << '\n';
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
yy::parser p;
|
|
p.set_debug_level (1);
|
|
return p.parse();
|
|
}
|
|
]])
|
|
|
|
AT_FOR_EACH_CXX([
|
|
AT_FULL_COMPILE([[input]])
|
|
# This used to print "Discarding 'a'." again at the end.
|
|
AT_PARSER_CHECK([[input]], [[0]], [[]],
|
|
[[Starting parse
|
|
Entering state 0
|
|
Reading a token: Next token is token NUMBER (1)
|
|
Shifting token NUMBER (1)
|
|
Entering state 1
|
|
Reducing stack by rule 1 (line 34):
|
|
$1 = token NUMBER (1)
|
|
-> $$ = nterm expr (10)
|
|
destroy: 1
|
|
Stack now 0
|
|
Entering state 2
|
|
Reading a token: Next token is token NUMBER (30)
|
|
Reducing stack by rule 2 (line 35):
|
|
-> $$ = nterm @1 (20)
|
|
Stack now 2 0
|
|
Entering state 4
|
|
Next token is token NUMBER (30)
|
|
Shifting token NUMBER (30)
|
|
Entering state 5
|
|
Reducing stack by rule 3 (line 35):
|
|
$1 = nterm expr (10)
|
|
$2 = nterm @1 (20)
|
|
$3 = token NUMBER (30)
|
|
expr: 10 20 30
|
|
-> $$ = nterm expr (40)
|
|
destroy: 30
|
|
destroy: 20
|
|
destroy: 10
|
|
Stack now 0
|
|
Entering state 2
|
|
Reading a token: Next token is token EOI ()
|
|
Shifting token EOI ()
|
|
Entering state 3
|
|
Cleanup: popping token EOI ()
|
|
Cleanup: popping nterm expr (40)
|
|
destroy: 40
|
|
]])
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
|
|
## ----------------------- ##
|
|
## Doxygen Documentation. ##
|
|
## ----------------------- ##
|
|
|
|
m4_define([AT_CHECK_DOXYGEN],
|
|
[m4_case([$1],
|
|
[Public], [m4_pushdef([AT_DOXYGEN_PRIVATE], [NO])],
|
|
[Private], [m4_pushdef([AT_DOXYGEN_PRIVATE], [YES])],
|
|
[m4_fatal([invalid argument: $1])])
|
|
AT_SETUP([Doxygen $1 Documentation])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %locations])
|
|
AT_DATA([input.yy],
|
|
[[%require "3.2"
|
|
%skeleton "lalr1.cc"
|
|
%locations
|
|
%defines
|
|
%debug
|
|
%%
|
|
exp: %empty;
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]])
|
|
|
|
AT_BISON_CHECK([-o input.cc input.yy])
|
|
|
|
AT_DATA([Doxyfile],
|
|
[# The PROJECT_NAME tag is a single word (or a sequence of words
|
|
# surrounded by quotes) that should identify the project.
|
|
PROJECT_NAME = "Bison C++ Parser"
|
|
|
|
# The QUIET tag can be used to turn on/off the messages that are
|
|
# generated by doxygen. Possible values are YES and NO. If left blank
|
|
# NO is used.
|
|
QUIET = YES
|
|
|
|
# The WARNINGS tag can be used to turn on/off the warning messages
|
|
# that are generated by doxygen. Possible values are YES and NO. If
|
|
# left blank NO is used.
|
|
WARNINGS = YES
|
|
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate
|
|
# warnings for undocumented members. If EXTRACT_ALL is set to YES then
|
|
# this flag will automatically be disabled.
|
|
WARN_IF_UNDOCUMENTED = YES
|
|
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings
|
|
# for potential errors in the documentation, such as not documenting
|
|
# some parameters in a documented function, or documenting parameters
|
|
# that don't exist or using markup commands wrongly.
|
|
WARN_IF_DOC_ERROR = YES
|
|
# The WARN_FORMAT tag determines the format of the warning messages
|
|
# that doxygen can produce. The string should contain the $file,
|
|
# $line, and $text tags, which will be replaced by the file and line
|
|
# number from which the warning originated and the warning text.
|
|
WARN_FORMAT = "$file:$line: $text"
|
|
|
|
# If the EXTRACT_ALL tag is set to YES doxygen will assume all
|
|
# entities in documentation are documented, even if no documentation
|
|
# was available. Private class members and static file members will
|
|
# be hidden unless the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set
|
|
# to YES
|
|
EXTRACT_ALL = YES
|
|
|
|
# If the EXTRACT_PRIVATE tag is set to YES all private members of a
|
|
# class will be included in the documentation.
|
|
EXTRACT_PRIVATE = AT_DOXYGEN_PRIVATE
|
|
|
|
# If the EXTRACT_STATIC tag is set to YES all static members of a file
|
|
# will be included in the documentation.
|
|
EXTRACT_STATIC = AT_DOXYGEN_PRIVATE
|
|
])
|
|
|
|
AT_CHECK([doxygen --version || exit 77], 0, ignore)
|
|
AT_CHECK([doxygen], 0, [], [ignore])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
m4_popdef([AT_DOXYGEN_PRIVATE])
|
|
])# AT_CHECK_DOXYGEN
|
|
|
|
AT_CHECK_DOXYGEN([Public])
|
|
AT_CHECK_DOXYGEN([Private])
|
|
|
|
|
|
## ------------ ##
|
|
## Namespaces. ##
|
|
## ------------ ##
|
|
|
|
# AT_TEST(NAMESPACE-DECL, [COMPILE-ERROR])
|
|
# ----------------------------------------
|
|
# See if Bison can handle %define namespace "NAMESPACE-DECL". If COMPILE-ERROR
|
|
# is specified, then Bison should accept the input, but compilation will fail,
|
|
# so don't check compilation.
|
|
m4_pushdef([AT_TEST],
|
|
[AT_BISON_OPTION_PUSHDEFS([%language "C++" %define api.namespace {$1}])
|
|
AT_DATA_GRAMMAR([[input.yy]],
|
|
[[%language "C++"
|
|
%define api.namespace {]$1[}
|
|
%union { int i; }
|
|
%define global_tokens_and_yystype
|
|
%locations
|
|
|
|
%code {
|
|
// YYSTYPE contains a namespace reference.
|
|
int yylex (YYSTYPE *lval, const ]$1[::parser::location_type*) {
|
|
lval->i = 3;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
%%
|
|
|
|
start: ;
|
|
|
|
%%
|
|
|
|
void
|
|
]$1[::parser::error (const ]$1[::parser::location_type &loc,
|
|
const std::string &msg)
|
|
{
|
|
std::cerr << "At " << loc << ": " << msg << '\n';
|
|
}
|
|
|
|
]AT_MAIN_DEFINE[
|
|
]])
|
|
|
|
|
|
AT_BISON_CHECK([[-o input.cc input.yy]])
|
|
|
|
m4_if([$#], [1],
|
|
[AT_FOR_EACH_CXX([
|
|
AT_COMPILE_CXX([[input]])
|
|
AT_PARSER_CHECK([[input]])])])
|
|
AT_BISON_OPTION_POPDEFS
|
|
])
|
|
|
|
AT_SETUP([[Relative namespace references]])
|
|
AT_TEST([[foo]])
|
|
AT_TEST([[foo::bar]])
|
|
AT_TEST([[foo::bar::baz]])
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([[Absolute namespace references]])
|
|
AT_TEST([[::foo]])
|
|
AT_TEST([[::foo::bar]])
|
|
AT_TEST([[::foo::bar::baz]])
|
|
AT_TEST([[@tb@::foo]])
|
|
AT_TEST([[ @tb@ ::foo::bar]])
|
|
AT_TEST([[ ::foo::bar::baz]])
|
|
AT_CLEANUP
|
|
|
|
AT_SETUP([[Syntactically invalid namespace references]])
|
|
AT_TEST([[:foo:bar]], [[-]])
|
|
AT_TEST([[foo: :bar]], [[-]])
|
|
# This one is interesting because '[3]' is encoded as '@<:@3@:>@', which
|
|
# contains single occurrences of ':'.
|
|
AT_TEST([[foo[3]::bar::baz]], [[-]])
|
|
AT_TEST([[foo::bar,baz]], [[-]])
|
|
AT_TEST([[foo::bar::(baz /* Pacify Emacs ) */]], [[-]])
|
|
AT_CLEANUP
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
|
|
## -------------------------------------- ##
|
|
## Syntax error discarding no lookahead. ##
|
|
## -------------------------------------- ##
|
|
|
|
# After a syntax error, lalr1.cc used to not check whether there
|
|
# actually is a lookahead before discarding the lookahead. As a result,
|
|
# it mistakenly invoked the destructor for the previous lookahead.
|
|
|
|
AT_SETUP([[Syntax error discarding no lookahead]])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"])
|
|
|
|
AT_DATA_GRAMMAR([[input.y]],
|
|
[[%skeleton "lalr1.cc"
|
|
|
|
%code {
|
|
#include <string>
|
|
int yylex (yy::parser::semantic_type *);
|
|
#define USE(Args)
|
|
}
|
|
|
|
%define parse.error verbose
|
|
|
|
%nonassoc 'a' ;
|
|
|
|
%destructor {
|
|
std::cerr << "Discarding 'a'.\n";
|
|
} 'a'
|
|
|
|
%%
|
|
|
|
start: error-reduce consistent-error 'a' { USE ($3); };
|
|
|
|
error-reduce:
|
|
'a' 'a' consistent-error 'a' { USE (($1, $2, $4)); }
|
|
| 'a' error { std::cerr << "Reducing 'a'.\n"; USE ($1); }
|
|
;
|
|
|
|
consistent-error:
|
|
'a'
|
|
| %empty %prec 'a'
|
|
;
|
|
|
|
// Provide another context in which all rules are useful so that this
|
|
// test case looks a little more realistic.
|
|
start: 'b' consistent-error ;
|
|
|
|
%%
|
|
|
|
int
|
|
yylex (yy::parser::semantic_type *)
|
|
{
|
|
static char const *input = "aa";
|
|
return *input++;
|
|
}
|
|
|
|
void
|
|
yy::parser::error (const std::string &m)
|
|
{
|
|
std::cerr << m << '\n';
|
|
}
|
|
|
|
]AT_MAIN_DEFINE[
|
|
]])
|
|
|
|
AT_FOR_EACH_CXX([
|
|
AT_FULL_COMPILE([[input]])
|
|
# This used to print "Discarding 'a'." again at the end.
|
|
AT_PARSER_CHECK([[input]], [[1]], [[]],
|
|
[[syntax error
|
|
Discarding 'a'.
|
|
Reducing 'a'.
|
|
]])
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
|
|
## --------------------------- ##
|
|
## Syntax error as exception. ##
|
|
## --------------------------- ##
|
|
|
|
# AT_TEST([BISON-DIRECTIVES = ''])
|
|
m4_pushdef([AT_TEST],
|
|
[AT_SETUP([[Syntax error as exception: $1]])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([$1 %debug])
|
|
|
|
AT_DATA_GRAMMAR([[input.yy]],
|
|
[[$1
|
|
%defines
|
|
|
|
%code
|
|
{
|
|
#include <cstdlib>
|
|
int yylex (yy::parser::semantic_type *);
|
|
}
|
|
|
|
%define parse.error verbose
|
|
%define parse.trace
|
|
%%
|
|
|
|
start: with-recovery | '!' without-recovery;
|
|
|
|
with-recovery:
|
|
%empty
|
|
| with-recovery item
|
|
| with-recovery error { std::cerr << "caught error\n"; }
|
|
;
|
|
|
|
without-recovery:
|
|
%empty
|
|
| without-recovery item
|
|
;
|
|
|
|
item:
|
|
'a'
|
|
| 's'
|
|
{
|
|
throw syntax_error ("invalid expression");
|
|
}
|
|
|
|
%%
|
|
|
|
void
|
|
yy::parser::error (const std::string &m)
|
|
{
|
|
std::cerr << "error: " << m << '\n';
|
|
}
|
|
]AT_MAIN_DEFINE[
|
|
]])
|
|
|
|
# Another file to check syntax_error's linkage.
|
|
AT_DATA_SOURCE([scan.cc],
|
|
[[#include "input.hh"
|
|
|
|
// 'a': valid item, 's': syntax error, 'l': lexical error.
|
|
int
|
|
yylex (yy::parser::semantic_type *lval)
|
|
{
|
|
switch (int res = getchar ())
|
|
{
|
|
// Don't choke on echo's \n.
|
|
case '\n':
|
|
return yylex (lval);
|
|
case 'l':
|
|
throw yy::parser::syntax_error ("invalid character");
|
|
default:
|
|
return res;
|
|
}
|
|
}
|
|
]])
|
|
|
|
AT_BISON_CHECK([[-o input.cc input.yy]])
|
|
|
|
AT_FOR_EACH_CXX([
|
|
AT_LANG_COMPILE([[input]], [[input.cc scan.cc]])
|
|
|
|
# Leave enough valid tokens to make sure we recovered from the
|
|
# previous error, otherwise we might hide some error messages
|
|
# (discarded during error recoevery).
|
|
echo "asaaalaa" >in
|
|
AT_PARSER_CHECK([[input < in]], [[0]], [[]],
|
|
[[error: invalid expression
|
|
caught error
|
|
error: invalid character
|
|
caught error
|
|
]])
|
|
|
|
echo "!as" >in
|
|
AT_PARSER_CHECK([[input < in]], [1], [],
|
|
[[error: invalid expression
|
|
]])
|
|
|
|
echo "!al" >in
|
|
AT_PARSER_CHECK([[input < in]], [1], [],
|
|
[[error: invalid character
|
|
]])
|
|
|
|
]) # AT_FOR_EACH_CXX
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
])
|
|
|
|
AT_TEST([%skeleton "lalr1.cc"])
|
|
AT_TEST([%skeleton "glr.cc"])
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
|
|
|
|
## ------------------ ##
|
|
## Exception safety. ##
|
|
## ------------------ ##
|
|
|
|
# AT_TEST([BISON-DIRECTIVES = ''], [WITH-RECOVERY = "with"])
|
|
# ----------------------------------------------------------
|
|
# Check that no object is leaked when exceptions are thrown.
|
|
# WITH-RECOVERY = "with" or "without".
|
|
m4_pushdef([AT_TEST],
|
|
[AT_SETUP([[Exception safety $2 error recovery $1]])
|
|
|
|
AT_SKIP_IF_EXCEPTION_SUPPORT_IS_POOR
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1])
|
|
|
|
AT_DATA_GRAMMAR([[input.yy]],
|
|
[[%skeleton "lalr1.cc"
|
|
%debug
|
|
%define parse.error verbose
|
|
$1
|
|
%code requires
|
|
{
|
|
#include <cassert>
|
|
#include <cstdlib> // size_t and getenv.
|
|
#include <iostream>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
bool debug = false;
|
|
|
|
/// A class that tracks its instances.
|
|
struct Object
|
|
{
|
|
char val;
|
|
|
|
Object ()
|
|
: val ('?')
|
|
{
|
|
log (this, "Object::Object");
|
|
Object::instances.insert (this);
|
|
}
|
|
|
|
Object (const Object& that)
|
|
: val (that.val)
|
|
{
|
|
log (this, "Object::Object");
|
|
Object::instances.insert (this);
|
|
}
|
|
|
|
Object (char v)
|
|
: val (v)
|
|
{
|
|
log (this, "Object::Object");
|
|
Object::instances.insert (this);
|
|
}
|
|
|
|
~Object ()
|
|
{
|
|
log (this, "Object::~Object");
|
|
objects::iterator i = instances.find (this);
|
|
// Make sure this object is alive.
|
|
assert (i != instances.end ());
|
|
Object::instances.erase (i);
|
|
}
|
|
|
|
Object& operator= (const Object& that)
|
|
{
|
|
val = that.val;
|
|
return *this;
|
|
}
|
|
|
|
Object& operator= (char v)
|
|
{
|
|
val = v;
|
|
return *this;
|
|
}
|
|
|
|
// Static part.
|
|
typedef std::set<const Object*> objects;
|
|
static objects instances;
|
|
|
|
static bool
|
|
empty ()
|
|
{
|
|
return instances.empty ();
|
|
}
|
|
|
|
static void
|
|
log (Object const *o, const std::string& msg)
|
|
{
|
|
if (debug)
|
|
{
|
|
if (o)
|
|
std::cerr << o << "->";
|
|
std::cerr << msg << " {";
|
|
const char* sep = " ";
|
|
for (objects::const_iterator i = instances.begin(),
|
|
i_end = instances.end();
|
|
i != i_end;
|
|
++i)
|
|
{
|
|
std::cerr << sep << *i;
|
|
sep = ", ";
|
|
}
|
|
std::cerr << " }\n";
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
%code
|
|
{
|
|
#include <cassert>
|
|
#include <cstring> // strchr
|
|
#include <stdexcept>
|
|
int yylex (yy::parser::semantic_type *);
|
|
Object::objects Object::instances;
|
|
static char const *input;
|
|
}
|
|
|
|
]AT_VARIANT_IF([[
|
|
%printer
|
|
{
|
|
yyo << &$$ << " '" << $$.val << '\'';
|
|
if ($$.val == 'p')
|
|
throw std::runtime_error ("printer");
|
|
} <Object>;
|
|
|
|
%token <Object> 'a' 'E' 'e' 'p' 'R' 's' 'T'
|
|
%type <Object> list item
|
|
]], [[
|
|
%union
|
|
{
|
|
Object *obj;
|
|
}
|
|
%destructor { delete $$; } <obj>;
|
|
%printer
|
|
{
|
|
yyo << $$ << " '" << $$->val << '\'';
|
|
if ($$->val == 'p')
|
|
throw std::runtime_error ("printer");
|
|
} <obj>;
|
|
|
|
%token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T'
|
|
%type <obj> list item
|
|
]])[
|
|
|
|
%initial-action
|
|
{
|
|
if (strchr (input, 'i'))
|
|
throw std::runtime_error ("initial-action");
|
|
}
|
|
|
|
%%
|
|
|
|
start: list {]AT_VARIANT_IF([], [ delete $][1]; )[};
|
|
|
|
list:
|
|
item { $$ = $][1; }
|
|
// Right recursion to load the stack.
|
|
| item list { $$ = $][1; ]AT_VARIANT_IF([], [delete $][2]; )[}
|
|
;
|
|
|
|
item:
|
|
'a' { $$ = $][1; }
|
|
| 'e' { YYUSE ($$); YYUSE ($][1); error ("syntax error"); }
|
|
// Not just 'E', otherwise we reduce when 'E' is the lookahead, and
|
|
// then the stack is emptied, defeating the point of the test.
|
|
| 'E' 'a' { YYUSE ($][1); $$ = $][2; }
|
|
| 'R' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYERROR; }
|
|
| 'p' { $$ = $][1; }
|
|
| 's' { $$ = $][1; throw std::runtime_error ("reduction"); }
|
|
| 'T' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYABORT; }
|
|
]m4_if([$2], [with],
|
|
[[| error { $$ = ]AT_VARIANT_IF([], [new ])[Object ('R'); yyerrok; }]])[
|
|
;
|
|
%%
|
|
|
|
int
|
|
yylex (yy::parser::semantic_type *lvalp)
|
|
{
|
|
// 'a': no error.
|
|
// 'e': user action calls error.
|
|
// 'E': syntax error, with yyerror that throws.
|
|
// 'i': initial action throws.
|
|
// 'l': yylex throws.
|
|
// 'R': call YYERROR in the action
|
|
// 's': reduction throws.
|
|
// 'T': call YYABORT in the action
|
|
switch (char res = *input++)
|
|
{
|
|
case 'l':
|
|
throw std::runtime_error ("yylex");
|
|
default:
|
|
lvalp->]AT_VARIANT_IF([build<Object> (res)],
|
|
[obj = new Object (res)])[;
|
|
goto zero;
|
|
zero:
|
|
case 0:
|
|
return res;
|
|
}
|
|
}
|
|
|
|
/* A C++ error reporting function. */
|
|
void
|
|
yy::parser::error (const std::string& m)
|
|
{
|
|
throw std::runtime_error (m);
|
|
}
|
|
|
|
int
|
|
main (int argc, const char *argv[])
|
|
{
|
|
switch (argc)
|
|
{
|
|
case 2:
|
|
input = argv[1];
|
|
break;
|
|
case 3:
|
|
assert (std::string(argv[1]) == "--debug");
|
|
debug = 1;
|
|
input = argv[2];
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
|
|
yy::parser parser;
|
|
debug |= !!getenv ("YYDEBUG");
|
|
parser.set_debug_level (debug);
|
|
int res = 2;
|
|
try
|
|
{
|
|
res = parser.parse ();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cerr << "exception caught: " << e.what () << '\n';
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "unknown exception caught\n";
|
|
}
|
|
Object::log (YY_NULLPTR, "end");
|
|
assert (Object::empty());
|
|
return res;
|
|
}
|
|
]])
|
|
AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
|
|
|
|
AT_FOR_EACH_CXX([
|
|
AT_COMPILE_CXX([[input]])
|
|
|
|
AT_PARSER_CHECK([[input aaaas]], [[2]], [[]],
|
|
[[exception caught: reduction
|
|
]])
|
|
|
|
AT_PARSER_CHECK([[input aaaal]], [[2]], [[]],
|
|
[[exception caught: yylex
|
|
]])
|
|
|
|
AT_PARSER_CHECK([[input i]], [[2]], [[]],
|
|
[[exception caught: initial-action
|
|
]])
|
|
|
|
AT_PARSER_CHECK([[input aaaap]])
|
|
|
|
AT_PARSER_CHECK([[input --debug aaaap]], [[2]], [[]], [[stderr]])
|
|
AT_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
|
|
|
|
AT_PARSER_CHECK([[input aaaae]], [[2]], [[]],
|
|
[[exception caught: syntax error
|
|
]])
|
|
|
|
AT_PARSER_CHECK([[input aaaaE]], [[2]], [[]],
|
|
[[exception caught: syntax error, unexpected $end, expecting 'a'
|
|
]])
|
|
|
|
AT_PARSER_CHECK([[input aaaaT]], [[1]])
|
|
|
|
AT_PARSER_CHECK([[input aaaaR]], [m4_if([$2], [with], [0], [1])])
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
|
|
AT_CLEANUP
|
|
])
|
|
|
|
AT_TEST([], [with])
|
|
AT_TEST([], [without])
|
|
AT_TEST([%define api.value.type variant], [with])
|
|
AT_TEST([%define api.value.type variant], [without])
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
## ------------------------------------- ##
|
|
## C++ GLR parser identifier shadowing. ##
|
|
## ------------------------------------- ##
|
|
|
|
AT_SETUP([[C++ GLR parser identifier shadowing]])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS
|
|
AT_DATA_GRAMMAR([input.yy], [
|
|
%skeleton "glr.cc"
|
|
|
|
%union
|
|
{
|
|
int ival;
|
|
}
|
|
|
|
%token <ival> ZERO;
|
|
|
|
%code
|
|
{
|
|
int yylex (yy::parser::semantic_type *lvalp);
|
|
}
|
|
|
|
%%
|
|
exp: ZERO
|
|
|
|
%%
|
|
|
|
int yylex (yy::parser::semantic_type *lvalp)
|
|
{
|
|
// Note: this argument is unused, but named on purpose. There used to be a
|
|
// bug with a macro that erroneously expanded this identifier to
|
|
// yystackp->yyval.
|
|
YYUSE (lvalp);
|
|
return yy::parser::token::ZERO;
|
|
}
|
|
|
|
void yy::parser::error (std::string const&)
|
|
{}
|
|
|
|
int main ()
|
|
{}
|
|
])
|
|
|
|
AT_BISON_CHECK([[-o input.cc input.yy]])
|
|
AT_FOR_EACH_CXX([AT_COMPILE_CXX([[input]])])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ------------------ ##
|
|
## Shared locations. ##
|
|
## ------------------ ##
|
|
|
|
AT_SETUP([Shared locations])
|
|
|
|
# AT_TEST([PREFIX], [DIRECTIVES])
|
|
# -------------------------------
|
|
# Generate and compile to *.o. Make sure there is no (allowed) YY*
|
|
# nor yy* identifiers in the header after applying api.prefix. Check
|
|
# that headers can be compiled by a C++ compiler.
|
|
m4_pushdef([AT_TEST],
|
|
[AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" %define api.namespace {$1} $2])
|
|
AT_LOC_PUSHDEF([begin.line], [begin.column], [end.line], [end.column])
|
|
AT_DATA_GRAMMAR([$1.yy],
|
|
[[%skeleton "lalr1.cc"
|
|
%define api.namespace {$1}
|
|
$2
|
|
%code {
|
|
]AT_YYERROR_DECLARE[
|
|
]AT_YYLEX_DECLARE[
|
|
}
|
|
%%
|
|
exp: '0';
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]AT_YYLEX_DEFINE(["0"])[
|
|
]])
|
|
|
|
AT_BISON_CHECK([-fcaret -o $1.cc $1.yy])
|
|
AT_LANG_COMPILE([$1.o], [], [-Iinclude])
|
|
|
|
AT_LOC_POPDEF
|
|
AT_BISON_OPTION_POPDEFS
|
|
])
|
|
|
|
mkdir -p include/ast
|
|
|
|
AT_TEST([x1],
|
|
[%defines
|
|
%locations
|
|
%define api.location.file "include/ast/loc.hh"
|
|
%define api.location.include {<ast/loc.hh>}])
|
|
|
|
# Check the CPP guard and Doxyen comments.
|
|
AT_CHECK([sed -ne '/INCLUDED/p;/\\file/{p;n;p;}' include/ast/loc.hh], [],
|
|
[[ ** \file ast/loc.hh
|
|
** Define the x1::location class.
|
|
#ifndef YY_YY_AST_LOC_HH_INCLUDED
|
|
# define YY_YY_AST_LOC_HH_INCLUDED
|
|
#endif // !YY_YY_AST_LOC_HH_INCLUDED
|
|
]])
|
|
|
|
AT_TEST([x2],
|
|
[%defines
|
|
%locations
|
|
%code requires {#include <ast/loc.hh>}
|
|
%define api.location.type {x1::location}])
|
|
|
|
m4_popdef([AT_TEST])
|
|
|
|
AT_DATA([main.cc],
|
|
[AT_DATA_SOURCE_PROLOGUE
|
|
[#include "x1.hh"
|
|
#include "x2.hh"
|
|
|
|
#define RUN(S) \
|
|
do { \
|
|
S::parser parser; \
|
|
int res = parser.parse(); \
|
|
if (res) \
|
|
std::cerr << #S": " << res << '\n'; \
|
|
} while (false)
|
|
|
|
int
|
|
main (void)
|
|
{
|
|
RUN(x1);
|
|
RUN(x2);
|
|
}
|
|
]])# main.cc
|
|
|
|
|
|
AT_COMPILE_CXX([parser], [[x[12].o main.cc]], [-Iinclude])
|
|
AT_PARSER_CHECK([parser], [0])
|
|
|
|
AT_CLEANUP
|
|
|
|
|
|
|
|
## ---------------- ##
|
|
## Default action. ##
|
|
## ---------------- ##
|
|
|
|
# In C++ we generate explicitly the code for the default action
|
|
# instead of simply copying blindly the semantic value buffer. This
|
|
# is important when copying raw memory is not enough, as exemplified
|
|
# by move-only types.
|
|
|
|
AT_SETUP([Default action])
|
|
AT_KEYWORDS([action])
|
|
|
|
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc"
|
|
%define api.token.constructor
|
|
%define api.value.type variant])
|
|
|
|
AT_DATA_GRAMMAR([test.y],
|
|
[[%code requires {
|
|
#include <memory> // unique_ptr
|
|
}
|
|
%code {
|
|
]AT_YYERROR_DECLARE[
|
|
]AT_YYLEX_DECLARE[
|
|
}
|
|
]AT_BISON_OPTIONS[
|
|
%define api.value.automove
|
|
%token ONE TWO EOI 0
|
|
%type <std::unique_ptr<int>> ONE TWO one two one.opt two.opt
|
|
%%
|
|
exp: one.opt two.opt { std::cout << *$][1 << ", " << *$][2 << '\n'; }
|
|
one.opt: one | %empty {}
|
|
two.opt: two | %empty {}
|
|
one: ONE
|
|
two: TWO
|
|
%%
|
|
]AT_YYERROR_DEFINE[
|
|
]AT_YYLEX_DEFINE(["12"],
|
|
[ if (res == '1')
|
|
return yy::parser::make_ONE (std::make_unique<int> (10));
|
|
else if (res == '2')
|
|
return yy::parser::make_TWO (std::make_unique<int> (20));
|
|
else
|
|
return yy::parser::make_EOI ();
|
|
])[
|
|
]AT_MAIN_DEFINE[
|
|
]])
|
|
|
|
AT_LANG_FOR_EACH_STD([
|
|
AT_REQUIRE_CXX_STD(14, [echo "$at_std not supported"; continue])
|
|
AT_FULL_COMPILE([[test]], [], [], [], [-fcaret])
|
|
AT_PARSER_CHECK([[test]], 0, [[10, 20
|
|
]])
|
|
])
|
|
|
|
AT_BISON_OPTION_POPDEFS
|
|
|
|
AT_CLEANUP
|