Skip to content

Commit a750724

Browse files
committed
glr2.cc: custom error messages
Reported by Tom Shields <[email protected]>. <https://lists.gnu.org/r/bug-bison/2021-08/msg00003.html> * data/skeletons/glr2.cc (context): New. Use it to generate the error messages. Add support for custom error messages. * tests/calc.at: Check support for custom error messages.
1 parent 7c47598 commit a750724

File tree

2 files changed

+172
-96
lines changed

2 files changed

+172
-96
lines changed

data/skeletons/glr2.cc

Lines changed: 167 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,25 @@ class glr_state;
283283
static std::string symbol_name (symbol_kind_type yysymbol);]])[
284284
285285
]b4_token_constructor_define[
286+
]b4_parse_error_bmatch([custom\|detailed\|verbose], [[
287+
class context
288+
{
289+
public:
290+
context (glr_stack& yystack, const symbol_type& yyla);
291+
const symbol_type& lookahead () const YY_NOEXCEPT { return yyla_; }
292+
symbol_kind_type token () const YY_NOEXCEPT { return yyla_.kind (); }]b4_locations_if([[
293+
const location_type& location () const YY_NOEXCEPT { return yyla_.location; }
294+
]])[
295+
/// Put in YYARG at most YYARGN of the expected tokens, and return the
296+
/// number of tokens stored in YYARG. If YYARG is null, return the
297+
/// number of expected tokens (guaranteed to be less than YYNTOKENS).
298+
int expected_tokens (symbol_kind_type yyarg[], int yyargn) const;
299+
300+
private:
301+
glr_stack& yystack_;
302+
const symbol_type& yyla_;
303+
};
304+
]])[
286305
# if ]b4_api_PREFIX[DEBUG
287306
public:
288307
/// \brief Report a symbol value on the debug stream.
@@ -304,7 +323,22 @@ class glr_state;
304323
std::ostream* yycdebug_;
305324
#endif
306325
307-
public: // FIXME: Private
326+
]b4_parse_error_bmatch(
327+
[custom], [[
328+
private:
329+
/// Report a syntax error
330+
/// \param yyctx the context in which the error occurred.
331+
void report_syntax_error (const context& yyctx) const;]],
332+
[detailed\|verbose], [[
333+
private:
334+
/// The arguments of the error message.
335+
int yy_syntax_error_arguments_ (const context& yyctx,
336+
symbol_kind_type yyarg[], int yyargn) const;
337+
338+
/// Generate an error message.
339+
/// \param yyctx the context in which the error occurred.
340+
virtual std::string yysyntax_error_ (const context& yyctx) const;]])[
341+
308342
/// Convert a scanner token kind \a t to a symbol kind.
309343
/// In theory \a t should be a token_kind_type, but character literals
310344
/// are valid, yet not members of the token_kind_type enum.
@@ -2020,6 +2054,12 @@ class glr_stack
20202054
typedef parser_type::value_type value_type;]b4_locations_if([[
20212055
typedef parser_type::location_type location_type;]])[
20222056

2057+
]b4_parse_error_bmatch([custom\|detailed\|verbose], [[
2058+
typedef parser_type::context context;
2059+
// Needs access to yypact_value_is_default, etc.
2060+
friend context;
2061+
]])[
2062+
20232063
glr_stack (size_t yysize, ]b4_namespace_ref[::]b4_parser_class[& yyparser_yyarg]m4_ifset([b4_parse_param], [, b4_parse_param_decl])[)
20242064
: yyerrState (0)
20252065
, yystateStack (yysize)
@@ -2293,109 +2333,26 @@ b4_dollar_popdef])[]dnl
22932333
yystateStack.dumpStack();
22942334
}
22952335
#endif
2336+
22962337
void
22972338
yyreportSyntaxError ()
22982339
{
22992340
if (yyerrState != 0)
23002341
return;
2301-
]b4_parse_error_bmatch(
2302-
[simple],
2303-
[[ yyparser.error (]b4_locations_if([this->yyla.location, ])[YY_("syntax error"));]],
2304-
[[ {
2305-
enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
2306-
/* Arguments of yyformat. */
2307-
yysymbol_kind_t yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]
2308-
= { ]b4_symbol(empty, kind)[ };
2309-
/* Number of reported tokens (one for the "unexpected", one per
2310-
"expected"). */
2311-
int yycount = 0;
2312-
2313-
/* There are many possibilities here to consider:
2314-
- If this state is a consistent state with a default action, then
2315-
the only way this function was invoked is if the default action
2316-
is an error action. In that case, don't check for expected
2317-
tokens because there are none.
2318-
- The only way there can be no lookahead present (in yytoken) is if
2319-
this state is a consistent state with a default action. Thus,
2320-
detecting the absence of a lookahead is sufficient to determine
2321-
that there is no unexpected or expected token to report. In that
2322-
case, just report a simple "syntax error".
2323-
- Don't assume there isn't a lookahead just because this state is a
2324-
consistent state with a default action. There might have been a
2325-
previous inconsistent state, consistent state with a non-default
2326-
action, or user semantic action that manipulated yytoken.
2327-
- Of course, the expected token list depends on states to have
2328-
correct lookahead information, and it depends on the parser not
2329-
to perform extra reductions after fetching a lookahead from the
2330-
scanner and before detecting a syntax error. Thus, state merging
2331-
(from LALR or IELR) and default reductions corrupt the expected
2332-
token list. However, the list is correct for canonical LR with
2333-
one exception: it will still contain any token that will not be
2334-
accepted due to an error action in a later state.
2335-
*/
2336-
if (!yyla.empty ())
2337-
{
2338-
const int yyn = yypact[firstTopState()->yylrState];
2339-
yyarg[yycount++] = yyla.kind ();
2340-
if (!yypact_value_is_default (yyn))
2341-
{
2342-
/* Start YYX at -YYN if negative to avoid negative indexes in
2343-
YYCHECK. In other words, skip the first -YYN actions for this
2344-
state because they are default actions. */
2345-
const int yyxbegin = yyn < 0 ? -yyn : 0;
2346-
/* Stay within bounds of both yycheck and yytname. */
2347-
const int yychecklim = YYLAST - yyn + 1;
2348-
const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
2349-
for (int yyx = yyxbegin; yyx < yyxend; ++yyx)
2350-
if (yycheck[yyx + yyn] == yyx && yyx != ]b4_symbol(error, kind)[
2351-
&& !yytable_value_is_error (yytable[yyx + yyn]))
2352-
{
2353-
if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
2354-
{
2355-
yycount = 1;
2356-
break;
2357-
}
2358-
yyarg[yycount++] = static_cast<yysymbol_kind_t>(yyx);
2359-
}
2360-
}
2361-
}
2362-
2363-
/* Internationalized format string. */
2364-
const char *yyformat = YY_NULLPTR;
2365-
switch (yycount)
2366-
{
2367-
#define YYCASE_(N, S) \
2368-
case N: \
2369-
yyformat = S; \
2370-
break
2371-
default: /* Avoid compiler warnings. */
2372-
YYCASE_(0, YY_("syntax error"));
2373-
YYCASE_(1, YY_("syntax error, unexpected %s"));
2374-
YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
2375-
YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
2376-
YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
2377-
YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
2378-
#undef YYCASE_
2379-
}
2380-
2381-
std::string yymsg;
2382-
// Argument number.
2383-
std::ptrdiff_t yyi = 0;
2384-
for (char const* yyp = yyformat; *yyp; ++yyp)
2385-
if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount)
2386-
{
2387-
yymsg += ]b4_namespace_ref[::]b4_parser_class[::symbol_name (yyarg[yyi++]);
2388-
++yyp;
2389-
}
2390-
else
2391-
yymsg += *yyp;
2392-
yyparser.error (]b4_locations_if([[yyla.location, ]])[yymsg);
2393-
}
2394-
]])[
2342+
]b4_parse_error_case(
2343+
[simple], [[
2344+
std::string msg = YY_("syntax error");
2345+
yyparser.error (]b4_join(b4_locations_if([yyla.location]), [[YY_MOVE (msg)]])[);]],
2346+
[custom], [[
2347+
context yyctx (*this, yyla);
2348+
yyparser.report_syntax_error (yyctx);]],
2349+
[[
2350+
context yyctx (*this, yyla);
2351+
std::string msg = yyparser.yysyntax_error_ (yyctx);
2352+
yyparser.error (]b4_join(b4_locations_if([yyla.location]), [[YY_MOVE (msg)]])[);]])[
23952353
yyerrcnt += 1;
23962354
}
23972355

2398-
23992356
/* Recover from a syntax error on this, assuming that yytoken,
24002357
yylval, and yylloc are the syntactic category, semantic value, and location
24012358
of the lookahead. */
@@ -3365,6 +3322,120 @@ static void yypdumpstack (const glr_stack& yystack)
33653322
#endif
33663323
]])[
33673324

3325+
]b4_parse_error_bmatch([custom\|detailed\|verbose], [[
3326+
// ]b4_parser_class[::context.
3327+
]b4_parser_class[::context::context (glr_stack& yystack, const symbol_type& yyla)
3328+
: yystack_ (yystack)
3329+
, yyla_ (yyla)
3330+
{}
3331+
3332+
int
3333+
]b4_parser_class[::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const
3334+
{
3335+
// Actual number of expected tokens
3336+
int yycount = 0;
3337+
const int yyn = yypact[yystack_.firstTopState()->yylrState];
3338+
if (!yystack_.yypact_value_is_default (yyn))
3339+
{
3340+
/* Start YYX at -YYN if negative to avoid negative indexes in
3341+
YYCHECK. In other words, skip the first -YYN actions for this
3342+
state because they are default actions. */
3343+
const int yyxbegin = yyn < 0 ? -yyn : 0;
3344+
/* Stay within bounds of both yycheck and yytname. */
3345+
const int yychecklim = YYLAST - yyn + 1;
3346+
const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
3347+
for (int yyx = yyxbegin; yyx < yyxend; ++yyx)
3348+
if (yycheck[yyx + yyn] == yyx && yyx != ]b4_symbol(error, kind)[
3349+
&& !yystack_.yytable_value_is_error (yytable[yyx + yyn]))
3350+
{
3351+
if (!yyarg)
3352+
++yycount;
3353+
else if (yycount == yyargn)
3354+
return 0;
3355+
else
3356+
yyarg[yycount++] = YY_CAST (symbol_kind_type, yyx);
3357+
}
3358+
}
3359+
if (yyarg && yycount == 0 && 0 < yyargn)
3360+
yyarg[0] = ]b4_symbol(empty, kind)[;
3361+
return yycount;
3362+
}
3363+
3364+
]])[
3365+
3366+
]b4_parse_error_bmatch([detailed\|verbose], [[
3367+
int
3368+
]b4_parser_class[::yy_syntax_error_arguments_ (const context& yyctx,
3369+
symbol_kind_type yyarg[], int yyargn) const
3370+
{
3371+
/* There are many possibilities here to consider:
3372+
- If this state is a consistent state with a default action, then
3373+
the only way this function was invoked is if the default action
3374+
is an error action. In that case, don't check for expected
3375+
tokens because there are none.
3376+
- The only way there can be no lookahead present (in yyla) is
3377+
if this state is a consistent state with a default action.
3378+
Thus, detecting the absence of a lookahead is sufficient to
3379+
determine that there is no unexpected or expected token to
3380+
report. In that case, just report a simple "syntax error".
3381+
- Don't assume there isn't a lookahead just because this state is
3382+
a consistent state with a default action. There might have
3383+
been a previous inconsistent state, consistent state with a
3384+
non-default action, or user semantic action that manipulated
3385+
yyla. (However, yyla is currently not documented for users.)
3386+
*/
3387+
3388+
if (!yyctx.lookahead ().empty ())
3389+
{
3390+
if (yyarg)
3391+
yyarg[0] = yyctx.token ();
3392+
int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1);
3393+
return yyn + 1;
3394+
}
3395+
return 0;
3396+
}
3397+
3398+
// Generate an error message.
3399+
std::string
3400+
]b4_parser_class[::yysyntax_error_ (const context& yyctx) const
3401+
{
3402+
// Its maximum.
3403+
enum { YYARGS_MAX = 5 };
3404+
// Arguments of yyformat.
3405+
symbol_kind_type yyarg[YYARGS_MAX];
3406+
int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX);
3407+
3408+
char const* yyformat = YY_NULLPTR;
3409+
switch (yycount)
3410+
{
3411+
#define YYCASE_(N, S) \
3412+
case N: \
3413+
yyformat = S; \
3414+
break
3415+
default: // Avoid compiler warnings.
3416+
YYCASE_ (0, YY_("syntax error"));
3417+
YYCASE_ (1, YY_("syntax error, unexpected %s"));
3418+
YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s"));
3419+
YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s"));
3420+
YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
3421+
YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
3422+
#undef YYCASE_
3423+
}
3424+
3425+
std::string yyres;
3426+
// Argument number.
3427+
std::ptrdiff_t yyi = 0;
3428+
for (char const* yyp = yyformat; *yyp; ++yyp)
3429+
if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount)
3430+
{
3431+
yyres += symbol_name (yyarg[yyi++]);
3432+
++yyp;
3433+
}
3434+
else
3435+
yyres += *yyp;
3436+
return yyres;
3437+
}]])[
3438+
33683439
void
33693440
]b4_parser_class[::yy_destroy_ (const char* yymsg, symbol_kind_type yykind,
33703441
value_type& yyval]b4_locations_if([[,

tests/calc.at

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,7 @@ AT_CHECK_CALC_GLR([%define api.pure %locations])
13981398
AT_CHECK_CALC_GLR([%define parse.error verbose %locations])
13991399

14001400
AT_CHECK_CALC_GLR([%define parse.error custom %locations %header %name-prefix "calc" %verbose])
1401+
AT_CHECK_CALC_GLR([%define parse.error custom %locations %header %name-prefix "calc" %verbose %define api.pure])
14011402
AT_CHECK_CALC_GLR([%define parse.error detailed %locations %header %name-prefix "calc" %verbose])
14021403
AT_CHECK_CALC_GLR([%define parse.error verbose %locations %header %name-prefix "calc" %verbose])
14031404

@@ -1480,7 +1481,11 @@ AT_CHECK_CALC_GLR_CC([%define parse.error verbose %define api.prefix {calc} %ver
14801481

14811482
AT_CHECK_CALC_GLR_CC([%debug])
14821483

1484+
# parse.error.
1485+
AT_CHECK_CALC_GLR_CC([%define parse.error detailed %debug %name-prefix "calc" %verbose])
14831486
AT_CHECK_CALC_GLR_CC([%define parse.error verbose %debug %name-prefix "calc" %verbose])
1487+
AT_CHECK_CALC([%skeleton "glr2.cc" %define parse.error custom %debug %name-prefix "calc" %verbose]) # Only glr2.cc.
1488+
14841489
AT_CHECK_CALC_GLR_CC([%define parse.error verbose %debug %name-prefix "calc" %define api.token.prefix {TOK_} %verbose])
14851490

14861491
AT_CHECK_CALC_GLR_CC([%locations %header %define parse.error verbose %debug %name-prefix "calc" %verbose %parse-param {semantic_value *result}{int *count}{int *nerrs}])

0 commit comments

Comments
 (0)