mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-18 00:33:03 +00:00
lalr1.cc: check exception safety of error handling
* tests/c++.at (Exception safety): Don't use swap here, it is useless. Cover more test cases: yyerror, YYERROR, YYABORT, and error recovery. (Object): Instead of just keeping a counter of instances, keep a list of them.
This commit is contained in:
106
tests/c++.at
106
tests/c++.at
@@ -195,33 +195,62 @@ AT_DATA_GRAMMAR([[input.yy]],
|
|||||||
[[%skeleton "lalr1.cc"
|
[[%skeleton "lalr1.cc"
|
||||||
%defines // FIXME: Mandated in 2.6.
|
%defines // FIXME: Mandated in 2.6.
|
||||||
%debug
|
%debug
|
||||||
|
%error-verbose
|
||||||
|
|
||||||
%code requires
|
%code requires
|
||||||
{
|
{
|
||||||
|
#include <cassert>
|
||||||
#include <cstdlib> // size_t and getenv.
|
#include <cstdlib> // size_t and getenv.
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
int debug = 0;
|
bool debug = false;
|
||||||
|
|
||||||
/// A class that counts its number of instances.
|
/// A class that counts its number of instances.
|
||||||
struct Object
|
struct Object
|
||||||
{
|
{
|
||||||
static size_t counter;
|
typedef std::list<const Object*> objects;
|
||||||
int val;
|
static objects instances;
|
||||||
|
char val;
|
||||||
|
|
||||||
Object (int v)
|
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 << " }" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object (char v)
|
||||||
: val (v)
|
: val (v)
|
||||||
{
|
{
|
||||||
++counter;
|
instances.push_back(this);
|
||||||
if (debug)
|
log (this, "Object::Object");
|
||||||
std::cerr << "Object::Object() => counter == " << counter << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Object ()
|
~Object ()
|
||||||
{
|
{
|
||||||
--counter;
|
instances.remove(this);
|
||||||
if (debug)
|
log (this, "Object::~Object");
|
||||||
std::cerr << "Object::~Object() => counter == " << counter << std::endl;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -232,13 +261,13 @@ AT_DATA_GRAMMAR([[input.yy]],
|
|||||||
#include <cstring> // strchr
|
#include <cstring> // strchr
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
int yylex (yy::parser::semantic_type *);
|
int yylex (yy::parser::semantic_type *);
|
||||||
size_t Object::counter = 0;
|
Object::objects Object::instances;
|
||||||
static char const *input;
|
static char const *input;
|
||||||
}
|
}
|
||||||
|
|
||||||
%union
|
%union
|
||||||
{
|
{
|
||||||
Object* obj;
|
Object *obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
%initial-action
|
%initial-action
|
||||||
@@ -250,12 +279,12 @@ AT_DATA_GRAMMAR([[input.yy]],
|
|||||||
%destructor { delete $$; } <obj>;
|
%destructor { delete $$; } <obj>;
|
||||||
%printer
|
%printer
|
||||||
{
|
{
|
||||||
yyo << "counter == " << $$->counter;
|
yyo << $$ << " '" << $$->val << '\'';
|
||||||
if ($$->val == 'p')
|
if ($$->val == 'p')
|
||||||
throw std::runtime_error ("printer");
|
throw std::runtime_error ("printer");
|
||||||
} <obj>;
|
} <obj>;
|
||||||
|
|
||||||
%token <obj> 'a' 'p' 's'
|
%token <obj> 'a' 'E' 'e' 'p' 'R' 's' 'T'
|
||||||
%type <obj> list item
|
%type <obj> list item
|
||||||
|
|
||||||
%%
|
%%
|
||||||
@@ -268,23 +297,30 @@ list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
item:
|
item:
|
||||||
'a' { std::swap ($$, $1); }
|
'a' { $$ = $1; }
|
||||||
| 'p' { std::swap ($$, $1); }
|
| 'e' { YYUSE ($$); YYUSE($1); error (location_type(), "syntax error"); }
|
||||||
| 's'
|
// Not just 'E', otherwise we reduce when 'E' is the lookahead, and
|
||||||
{
|
// then the stack is emptied, defeating the point of the test.
|
||||||
std::swap ($$, $1);
|
| 'E' 'a' { YYUSE($1); $$ = $2; }
|
||||||
throw std::runtime_error ("reduction");
|
| 'R' { $$ = YY_NULL; delete $1; YYERROR; }
|
||||||
}
|
| 'p' { $$ = $1; }
|
||||||
|
| 's' { $$ = $1; throw std::runtime_error ("reduction"); }
|
||||||
|
| 'T' { $$ = YY_NULL; delete $1; YYABORT; }
|
||||||
|
| error { $$ = YY_NULL; yyerrok; }
|
||||||
|
;
|
||||||
%%
|
%%
|
||||||
|
|
||||||
int
|
int
|
||||||
yylex (yy::parser::semantic_type *lvalp)
|
yylex (yy::parser::semantic_type *lvalp)
|
||||||
{
|
{
|
||||||
// 'a': no error.
|
// 'a': no error.
|
||||||
|
// 'e': user action calls error.
|
||||||
|
// 'E': syntax error, with yyerror that throws.
|
||||||
// 'i': initial action throws.
|
// 'i': initial action throws.
|
||||||
// 'l': yylex throws.
|
// 'l': yylex throws.
|
||||||
|
// 'R': call YYERROR in the action
|
||||||
// 's': reduction throws.
|
// 's': reduction throws.
|
||||||
|
// 'T': call YYABORT in the action
|
||||||
switch (int res = *input++)
|
switch (int res = *input++)
|
||||||
{
|
{
|
||||||
case 'l':
|
case 'l':
|
||||||
@@ -297,7 +333,13 @@ yylex (yy::parser::semantic_type *lvalp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]AT_YYERROR_DEFINE[
|
/* A C++ error reporting function. */
|
||||||
|
void
|
||||||
|
yy::parser::error (const location_type& l, const std::string& m)
|
||||||
|
{
|
||||||
|
YYUSE (l);
|
||||||
|
throw std::runtime_error (m);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, const char *argv[])
|
main (int argc, const char *argv[])
|
||||||
@@ -332,11 +374,12 @@ main (int argc, const char *argv[])
|
|||||||
{
|
{
|
||||||
std::cerr << "unknown exception caught" << std::endl;
|
std::cerr << "unknown exception caught" << std::endl;
|
||||||
}
|
}
|
||||||
assert (Object::counter == 0);
|
Object::log (YY_NULL, "end");
|
||||||
|
assert (Object::empty());
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
]])
|
]])
|
||||||
AT_BISON_CHECK([[-o input.cc input.yy]])
|
AT_BISON_CHECK([[-o input.cc --report=all input.yy]])
|
||||||
AT_COMPILE_CXX([[input]])
|
AT_COMPILE_CXX([[input]])
|
||||||
|
|
||||||
AT_PARSER_CHECK([[./input aaaas]], [[2]], [[]],
|
AT_PARSER_CHECK([[./input aaaas]], [[2]], [[]],
|
||||||
@@ -356,6 +399,19 @@ AT_PARSER_CHECK([[./input aaaap]])
|
|||||||
AT_PARSER_CHECK([[./input --debug aaaap]], [[2]], [[]], [[stderr]])
|
AT_PARSER_CHECK([[./input --debug aaaap]], [[2]], [[]], [[stderr]])
|
||||||
AT_PARSER_CHECK([[grep '^exception caught: printer$' stderr]], [], [ignore])
|
AT_PARSER_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]])
|
||||||
|
|
||||||
|
# There is error-recovery, so exit success.
|
||||||
|
AT_PARSER_CHECK([[./input aaaaR]], [[0]])
|
||||||
|
|
||||||
AT_BISON_OPTION_POPDEFS
|
AT_BISON_OPTION_POPDEFS
|
||||||
|
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|||||||
Reference in New Issue
Block a user