Hi! > Le 17 août 2018 à 09:13, Brooks Moses <[email protected]> a écrit : > > Akim, > > Thanks for the comments! And no worries on the delay; I've certainly been > on the "maintainer" side of this sort of delay many times. > > Your proposed patch seems reasonable to me, and we could add "magic" for > other compilers (and it also allows people to set YY_EXCEPTIONS directly if > it doesn't do the magic they want). I can confirm it will work for Clang > as well as GCC.
Yep, I used #ifndef so that people can spell out what they want, yet I don’t want to document this just yet. This is not really mature, I want to be able to change this if it is not satisfactory. > I'd be happy with either this or a new Bison variable. However, I think > this way may be a bit better -- it's entirely possible that someone may > write a parser that chooses to either throw an exception or abort depending > on whether exceptions are enabled, so this can be a choice of the person > compiling the parser, as well as a choice of the person writing the parser. So the Bison variable should have three values I guess :) If we introduce it. Here is a more mature version of the patch. commit d835f484e04eef07ede1844c152bfff52f50896d Author: Akim Demaille <[email protected]> Date: Fri Aug 17 19:28:50 2018 +0200 lalr1.cc: support compilation with disabled support for exceptions Reported by Brooks Moses <[email protected]> http://lists.gnu.org/archive/html/bison-patches/2018-02/msg00000.html * data/lalr1.cc (YY_EXCEPTIONS): New. Use it to disable try/catch clauses. * doc/bison.texi (C++ Parser Interface): Document it. * configure.ac (CXXFLAGS_NO_EXCEPTIONS): New. * tests/atlocal.in: Receive it. * tests/local.at (AT_FULL_COMPILE, AT_LANG_COMPILE): Accept a new argument, extra compiler flags. * tests/calc.at: Run the C++ calculator with exception support disabled. diff --git a/NEWS b/NEWS index 9135184a..a47bbc09 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,13 @@ GNU Bison NEWS input: '0' | exp ^^^ +*** C++: Generated parsers can be compiled with -fno-exceptions (lalr1.cc) + + When compiled with exceptions disabled, the generated parsers no longer + uses try/catch clauses. + + Currently only GCC and Clang are supported. + ** Bug fixes *** GLR: Predicates support broken by #line directives diff --git a/THANKS b/THANKS index 9d37dc84..d42504a2 100644 --- a/THANKS +++ b/THANKS @@ -25,6 +25,7 @@ Bert Deknuydt [email protected] Bill Allombert [email protected] Bob Rossi [email protected] Brandon Lucia [email protected] +Brooks Moses [email protected] Bruce Lilly [email protected] Bruno Haible [email protected] Charles-Henri de Boysson [email protected] diff --git a/configure.ac b/configure.ac index 99f6ad43..1a9dcb00 100644 --- a/configure.ac +++ b/configure.ac @@ -153,6 +153,7 @@ if test "$enable_gcc_warnings" = yes; then # ... possiby in std=c++11 mode. gl_WARN_ADD([-Wno-zero-as-null-pointer-constant], [FLEX_SCANNER_CXXFLAGS]) CXXFLAGS=$save_CXXFLAGS + gl_WARN_ADD([-fno-exceptions], [CXXFLAGS_NO_EXCEPTIONS]) AC_LANG_POP([C++]) fi diff --git a/data/lalr1.cc b/data/lalr1.cc index 053875de..637a92cf 100644 --- a/data/lalr1.cc +++ b/data/lalr1.cc @@ -424,6 +424,15 @@ m4_if(b4_prefix, [yy], [], # endif #endif +// Whether we are compiled with exception support. +#ifndef YY_EXCEPTIONS +# if defined __GNUC__ && !defined __EXCEPTIONS +# define YY_EXCEPTIONS 0 +# else +# define YY_EXCEPTIONS 1 +# endif +#endif + ]b4_locations_if([dnl [#define YYRHSLOC(Rhs, K) ((Rhs)[K].location) ]b4_yylloc_default_define])[ @@ -717,9 +726,11 @@ m4_if(b4_prefix, [yy], [], /// The return value of parse (). int yyresult; +#if YY_EXCEPTIONS // FIXME: This shoud be completely indented. It is not yet to // avoid gratuitous conflicts when merging into the master branch. try +#endif // YY_EXCEPTIONS { YYCDEBUG << "Starting parse\n"; @@ -758,17 +769,21 @@ b4_dollar_popdef])[]dnl if (yyla.empty ()) { YYCDEBUG << "Reading a token: "; +#if YY_EXCEPTIONS try +#endif // YY_EXCEPTIONS {]b4_token_ctor_if([[ symbol_type yylookahead (]b4_lex[); yyla.move (yylookahead);]], [[ yyla.type = yytranslate_ (]b4_lex[);]])[ } +#if YY_EXCEPTIONS catch (const syntax_error& yyexc) { error (yyexc); goto yyerrlab1; } +#endif // YY_EXCEPTIONS } YY_SYMBOL_PRINT ("Next token is", yyla); @@ -838,7 +853,9 @@ b4_dollar_popdef])[]dnl // Perform the reduction. YY_REDUCE_PRINT (yyn); +#if YY_EXCEPTIONS try +#endif // YY_EXCEPTIONS { switch (yyn) { @@ -847,11 +864,13 @@ b4_dollar_popdef])[]dnl break; } } +#if YY_EXCEPTIONS catch (const syntax_error& yyexc) { error (yyexc); YYERROR; } +#endif // YY_EXCEPTIONS YY_SYMBOL_PRINT ("-> $$ =", yylhs); yypop_ (yylen); yylen = 0; @@ -976,6 +995,7 @@ b4_dollar_popdef])[]dnl return yyresult; } +#if YY_EXCEPTIONS catch (...) { YYCDEBUG << "Exception caught: cleaning lookahead and stack\n"; @@ -991,6 +1011,7 @@ b4_dollar_popdef])[]dnl } throw; } +#endif // YY_EXCEPTIONS } void diff --git a/doc/bison.texi b/doc/bison.texi index 1a7a495b..f56320b2 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -10888,13 +10888,12 @@ use @code{yy::parser::token::FOO}. The scanner can use @defcv {Type} {parser} {syntax_error} This class derives from @code{std::runtime_error}. Throw instances of it -from the scanner or from the user actions to raise parse errors. This is -equivalent with first -invoking @code{error} to report the location and message of the syntax -error, and then to invoke @code{YYERROR} to enter the error-recovery mode. -But contrary to @code{YYERROR} which can only be invoked from user actions -(i.e., written in the action itself), the exception can be thrown from -function invoked from the user action. +from the scanner or from the actions to raise parse errors. This is +equivalent with first invoking @code{error} to report the location and +message of the syntax error, and then to invoke @code{YYERROR} to enter the +error-recovery mode. But contrary to @code{YYERROR} which can only be +invoked from user actions (i.e., written in the action itself), the +exception can be thrown from function invoked from the user action. @end defcv @deftypemethod {parser} {} parser (@var{type1} @var{arg1}, ...) @@ -10914,6 +10913,10 @@ Run the syntactic analysis, and return 0 on success, 1 otherwise. The whole function is wrapped in a @code{try}/@code{catch} block, so that when an exception is thrown, the @code{%destructor}s are called to release the lookahead symbol, and the symbols pushed on the stack. + +Exception related code in the generated parser is protected by CPP guards +(@code{#if}) and disabled when exception support is disabled (i.e., passing +@code{-fno-exceptions} to the C++ compiler). @end deftypemethod @deftypemethod {parser} {std::ostream&} debug_stream () diff --git a/tests/atlocal.in b/tests/atlocal.in index 5db32669..93ba7613 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -50,6 +50,9 @@ CXXFLAGS="$NO_WERROR_CXXFLAGS @WERROR_CXXFLAGS@" # If 'exit 77'; skip all C++ tests; otherwise ':'. BISON_CXX_WORKS='@BISON_CXX_WORKS@' +# Compiler flags to disable exception support. +CXXFLAGS_NO_EXCEPTIONS='@CXXFLAGS_NO_EXCEPTIONS@' + # Be sure that the C++ compiler is not broken because of gnulib. This # cannot be checked in configure (gnulib is not parameterized yet), # and checking this in every C++ test in AC_COMPILE_CXX is too costly. diff --git a/tests/calc.at b/tests/calc.at index 3d83ba72..acc27da2 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -481,20 +481,20 @@ m4_define([AT_CHECK_SPACES], ]) -# AT_CHECK_CALC([BISON-OPTIONS]) -# ------------------------------ +# AT_CHECK_CALC([BISON-OPTIONS], [COMPILER-OPTIONS]) +# -------------------------------------------------- # Start a testing chunk which compiles 'calc' grammar with # BISON-OPTIONS, and performs several tests over the parser. m4_define([AT_CHECK_CALC], -[m4_ifval([$2], [m4_fatal([$0: expected a single argument])]) +[m4_ifval([$3], [m4_fatal([$0: expected at most two arguments])]) # We use integers to avoid dependencies upon the precision of doubles. -AT_SETUP([Calculator $1]) +AT_SETUP([Calculator $1 $2]) AT_BISON_OPTION_PUSHDEFS([$1]) AT_DATA_CALC_Y([$1]) -AT_FULL_COMPILE([calc], AT_DEFINES_IF([[lex], [main]])) +AT_FULL_COMPILE([calc], AT_DEFINES_IF([[lex], [main]]), [], [$2]) AT_CHECK_SPACES(m4_join([ ], [calc.AT_SKEL_CC_IF([cc], [c])], [AT_DEFINES_IF([calc.AT_SKEL_CC_IF([hh], [h])])])) @@ -677,10 +677,11 @@ AT_CHECK_CALC([%skeleton "lalr1.cc" %defines]) # Start a testing chunk which compiles 'calc' grammar with # the C++ skeleton, and performs several tests over the parser. m4_define([AT_CHECK_CALC_LALR1_CC], -[AT_CHECK_CALC([%language "C++"] $@)]) +[AT_CHECK_CALC([%language "C++" $1], [$2])]) AT_CHECK_CALC_LALR1_CC([]) AT_CHECK_CALC_LALR1_CC([%locations]) +AT_CHECK_CALC_LALR1_CC([%locations], [$CXXFLAGS_NO_EXCEPTIONS]) AT_CHECK_CALC_LALR1_CC([%locations %define api.location.type {Span}]) AT_CHECK_CALC_LALR1_CC([%defines %locations %define parse.error verbose %name-prefix "calc" %verbose %yacc]) diff --git a/tests/local.at b/tests/local.at index b1bdb4da..5341549e 100644 --- a/tests/local.at +++ b/tests/local.at @@ -740,8 +740,8 @@ m4_define([AT_QUELL_VALGRIND], ## ------------------------ ## -# AT_COMPILE(OUTPUT, [SOURCES = OUTPUT.c]) -# ---------------------------------------- +# AT_COMPILE(OUTPUT, [SOURCES = OUTPUT.c], [EXTRA-COMPILER-FLAGS]) +# ---------------------------------------------------------------- # Compile SOURCES into OUTPUT. # # If OUTPUT does not contain '.', assume that we are linking too, @@ -758,8 +758,8 @@ AT_CHECK(m4_join([ ], 0, [ignore], [ignore])]) -# AT_COMPILE_CXX(OUTPUT, [SOURCES = OUTPUT.cc]) -# --------------------------------------------- +# AT_COMPILE_CXX(OUTPUT, [SOURCES = OUTPUT.cc], [EXTRA-COMPILER-FLAGS]) +# --------------------------------------------------------------------- # Compile SOURCES into OUTPUT. If the C++ compiler does not work, # ignore the test. # @@ -770,7 +770,7 @@ m4_define([AT_COMPILE_CXX], [AT_KEYWORDS(c++) AT_CHECK([$BISON_CXX_WORKS], 0, ignore, ignore) AT_CHECK(m4_join([ ], - [$CXX $CXXFLAGS $CPPFLAGS], + [$CXX $CXXFLAGS $CPPFLAGS $3], [m4_bmatch([$1], [[.]], [-c], [$LDFLAGS])], [-o $1], [m4_default([$2], [m4_bpatsubst([$1], [\.o$]).cc])], @@ -790,21 +790,21 @@ AT_CHECK([[$SHELL ../../../javacomp.sh ]$1], [[0]], [ignore], [ignore])]) -# AT_LANG_COMPILE(OUTPUT, [SOURCES = OUTPUT.c] -# -------------------------------------------- +# AT_LANG_COMPILE(OUTPUT, [SOURCES = OUTPUT.c], [EXTRA-COMPILER-FLAGS]) +# --------------------------------------------------------------------- # Compile SOURCES into OUTPUT. Skip if compiler does not work. # # If OUTPUT does not contain '.', assume that we are linking too, # otherwise pass "-c"; this is a hack. The default SOURCES is OUTPUT # with trailing .o removed, and ".c"/".cc" appended. m4_define([AT_LANG_COMPILE], [AT_LANG_DISPATCH([$0], $@)]) -m4_define([AT_LANG_COMPILE(c)], [AT_COMPILE([$1], [$2])]) -m4_define([AT_LANG_COMPILE(c++)], [AT_COMPILE_CXX([$1], [$2])]) -m4_define([AT_LANG_COMPILE(java)], [AT_JAVA_COMPILE([$1.java], [$2])]) +m4_define([AT_LANG_COMPILE(c)], [AT_COMPILE([$1], [$2], [$3])]) +m4_define([AT_LANG_COMPILE(c++)], [AT_COMPILE_CXX([$1], [$2], [$3])]) +m4_define([AT_LANG_COMPILE(java)], [AT_JAVA_COMPILE([$1.java], [$2], [$3])]) -# AT_FULL_COMPILE(OUTPUT, [OTHER1], [OTHER2]) -# ------------------------------------------- +# AT_FULL_COMPILE(OUTPUT, [OTHER1], [OTHER2], [EXTRA-COMPILER-FLAGS) +# ------------------------------------------------------------------ # Compile OUTPUT.y to OUTPUT.c, OUTPUT.cc, or OUTPUT.java, and then # compile it to OUTPUT or OUTPUT.class. If OTHER is specified, compile # OUTPUT-OTHER.c, OUTPUT-OTHER.cc, or OUTPUT-OTHER.java to OUTPUT or @@ -817,7 +817,8 @@ m4_define([AT_FULL_COMPILE(c)], m4_join([ ], [$1.c], m4_ifval($2, [[$1-$2.c]]), - m4_ifval($3, [[$1-$3.c]])))]) + m4_ifval($3, [[$1-$3.c]])), + [$4])]) m4_define([AT_FULL_COMPILE(c++)], [AT_BISON_CHECK([-o $1.cc $1.y]) @@ -825,7 +826,8 @@ m4_define([AT_FULL_COMPILE(c++)], m4_join([ ], [$1.cc], m4_ifval($2, [[$1-$2.cc]]), - m4_ifval($3, [[$1-$3.cc]])))]) + m4_ifval($3, [[$1-$3.cc]])), + [$4])]) m4_define([AT_FULL_COMPILE(java)], [AT_BISON_CHECK([-o $1.java $1.y]) @@ -833,7 +835,8 @@ m4_define([AT_FULL_COMPILE(java)], m4_join([ ], [$1.java], m4_ifval($2, [[$1-$2.java]]), - m4_ifval($3, [[$1-$3.java]])))]) + m4_ifval($3, [[$1-$3.java]])), + [$4])])
