X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/ed8e1f68e9743c391f4aba148968ea1e5e960973..57597927ef1b9cc751df4314764a6edd575ffc8b:/tests/torture.at
diff --git a/tests/torture.at b/tests/torture.at
index 8fe00f7c..5489e919 100644
--- a/tests/torture.at
+++ b/tests/torture.at
@@ -1,78 +1,445 @@
# Torturing Bison. -*- Autotest -*-
-# Copyright 2001 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 2, or (at your option)
-# any later version.
+# Copyright (C) 2001-2002, 2004-2007, 2009-2013 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, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-# 02111-1307, USA.
+# along with this program. If not, see .
AT_BANNER([[Torture Tests.]])
-# AT_DATA_STACK_TORTURE(C-PROLOGUE)
-# ---------------------------------
+# AT_INCREASE_DATA_SIZE(SIZE)
+# ---------------------------
+# Try to increase the data size to SIZE KiB if possible.
+m4_define([AT_INCREASE_DATA_SIZE],
+[data_limit=`(ulimit -S -d) 2>/dev/null`
+case $data_limit in
+[[0-9]]*)
+ if test "$data_limit" -lt $1; then
+ AT_CHECK([ulimit -S -d $1 || exit 77])
+ ulimit -S -d $1
+ fi
+esac])
+
+
+## ------------------------------------- ##
+## Creating a large artificial grammar. ##
+## ------------------------------------- ##
+
+# AT_DATA_TRIANGULAR_GRAMMAR(FILE-NAME, SIZE)
+# -------------------------------------------
+# Create FILE-NAME, containing a self checking parser for a huge
+# triangular grammar.
+m4_define([AT_DATA_TRIANGULAR_GRAMMAR],
+[AT_BISON_OPTION_PUSHDEFS
+AT_DATA([[gengram.pl]],
+[[#! /usr/bin/perl -w
+
+use strict;
+my $max = $ARGV[0] || 10;
+
+print <
+#include
+#include
+#define MAX $max
+]AT_YYLEX_DECLARE[
+]AT_YYERROR_DECLARE[
+%}
+%union
+{
+ int val;
+};
+
+%token END "end"
+%type exp input
+EOF
+
+for my $size (1 .. $max)
+ {
+ print "%token t$size $size \"$size\"\n";
+ };
+
+print < MAX)
+ return 0;
+ else if (inner > outer)
+ {
+ inner = 1;
+ ++outer;
+ return END;
+ }
+ return inner++;
+}
+]AT_MAIN_DEFINE[
+EOF
+]])
+AT_BISON_OPTION_POPDEFS
+
+AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout])
+mv stdout $1
+])
+
+
+## -------------- ##
+## Big triangle. ##
+## -------------- ##
+
+AT_SETUP([Big triangle])
+
+# I have been able to go up to 2000 on my machine.
+# I tried 3000, a 29Mb grammar file, but then my system killed bison.
+# With 500 and the new parser, which consume far too much memory,
+# it gets killed too. Of course the parser is to be cleaned.
+AT_DATA_TRIANGULAR_GRAMMAR([input.y], [200])
+AT_BISON_CHECK_NO_XML([-v -o input.c input.y])
+AT_COMPILE([input])
+AT_PARSER_CHECK([./input])
+
+AT_CLEANUP
+
+
+
+# AT_DATA_HORIZONTAL_GRAMMAR(FILE-NAME, SIZE)
+# -------------------------------------------
+# Create FILE-NAME, containing a self checking parser for a huge
+# horizontal grammar.
+m4_define([AT_DATA_HORIZONTAL_GRAMMAR],
+[AT_BISON_OPTION_PUSHDEFS
+AT_DATA([[gengram.pl]],
+[[#! /usr/bin/perl -w
+
+use strict;
+my $max = $ARGV[0] || 10;
+
+print <
+#include
+#define MAX $max
+]AT_YYLEX_DECLARE[
+]AT_YYERROR_DECLARE[
+%}
+
+%token
+EOF
+for my $size (1 .. $max)
+ {
+ print " t$size $size \"$size\"\n";
+ };
+
+print <
+]AT_YYERROR_DEFINE[
+static int
+yylex (void)
+{
+ static int counter = 1;
+ if (counter <= MAX)
+ return counter++;
+ assert (counter++ == MAX + 1);
+ return 0;
+}
+]AT_MAIN_DEFINE[
+EOF
+]])
+
+AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout])
+mv stdout $1
+AT_BISON_OPTION_POPDEFS
+])
+
+
+## ---------------- ##
+## Big horizontal. ##
+## ---------------- ##
+
+AT_SETUP([Big horizontal])
+
+# I have been able to go up to 10000 on my machine, but I had to
+# increase the maximum stack size (* 100). It gave:
+#
+# input.y 263k
+# input.tab.c 1.3M
+# input 453k
+#
+# gengram.pl 10000 0.70s user 0.01s sys 99% cpu 0.711 total
+# bison input.y 730.56s user 0.53s sys 99% cpu 12:12.34 total
+# gcc -Wall input.tab.c -o input 5.81s user 0.20s sys 100% cpu 6.01 total
+# ./input 0.00s user 0.01s sys 108% cpu 0.01 total
+#
+AT_DATA_HORIZONTAL_GRAMMAR([input.y], [1000])
+
+# GNU m4 requires about 70 MiB for this test on a 32-bit host.
+# Ask for 200 MiB, which should be plenty even on a 64-bit host.
+AT_INCREASE_DATA_SIZE(204000)
+
+AT_BISON_CHECK_NO_XML([-v -o input.c input.y])
+AT_COMPILE([input])
+AT_PARSER_CHECK([./input])
+
+AT_CLEANUP
+
+
+
+# AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR(FILE-NAME, SIZE)
+# --------------------------------------------------
+# Create FILE-NAME, containing a self checking parser for a grammar
+# requiring SIZE lookahead tokens.
+m4_define([AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR],
+[AT_BISON_OPTION_PUSHDEFS
+AT_DATA([[gengram.pl]],
+[[#! /usr/bin/perl -w
+
+use strict;
+use Text::Wrap;
+my $max = $ARGV[0] || 10;
+
+print <
+# include
+# include
+# define MAX $max
+]AT_YYLEX_DECLARE[
+]AT_YYERROR_DECLARE[
+%}
+%union
+{
+ int val;
+};
+
+%type input exp
+%token token
+EOF
+
+print
+ wrap ("%type ",
+ " ",
+ map { "n$_" } (1 .. $max)),
+ "\n";
+
+print "%token\n";
+for my $count (1 .. $max)
+ {
+ print " t$count $count \"$count\"\n";
+ };
+
+print < MAX)
+ {
+ assert (counter++ == MAX + 1);
+ return 0;
+ }
+ if (return_token)
+ {
+ return_token = 0;
+ return token;
+ }
+ return_token = 1;
+ return counter++;
+}
+
+]AT_MAIN_DEFINE[
+EOF
+]])
+
+AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout])
+mv stdout $1
+AT_BISON_OPTION_POPDEFS
+])
+
+
+## ------------------------ ##
+## Many lookahead tokens. ##
+## ------------------------ ##
+
+AT_SETUP([Many lookahead tokens])
+
+AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR([input.y], [1000])
+
+# GNU m4 requires about 70 MiB for this test on a 32-bit host.
+# Ask for 200 MiB, which should be plenty even on a 64-bit host.
+AT_INCREASE_DATA_SIZE(204000)
+
+AT_BISON_CHECK([-v -o input.c input.y])
+AT_COMPILE([input])
+AT_PARSER_CHECK([./input])
+
+AT_CLEANUP
+
+
+
+# AT_DATA_STACK_TORTURE(C-PROLOGUE, [BISON-DECLS])
+# ------------------------------------------------
# A parser specialized in torturing the stack size.
m4_define([AT_DATA_STACK_TORTURE],
-[# A grammar of parens growing the stack thanks to right recursion.
+[AT_BISON_OPTION_PUSHDEFS([$2])
+# A grammar of parens growing the stack thanks to right recursion.
# exp:
-AT_DATA([input.y],
+AT_DATA_GRAMMAR([input.y],
[[%{
+#include
+#include
#include
#include
-#include
]$1[
- static int yylex (void);
- static void yyerror (const char *msg);
-#define YYPRINT(File, Type, Value) \
- fprintf (File, " (%d, stack size = %d, max = %d)", \
- Value, yyssp - yyss + 1, yystacksize);
+ ]AT_YYLEX_DECLARE[
+ ]AT_YYERROR_DECLARE[
%}
+]$2[
%error-verbose
%debug
%token WAIT_FOR_EOF
%%
exp: WAIT_FOR_EOF exp | ;
%%
-static void
-yyerror (const char *msg)
-{
- fprintf (stderr, "%s\n", msg);
- exit (1);
-}
-
-/* There are YYLVAL_MAX of WAIT_FOR_EOFs. */
-unsigned int yylval_max;
-
+]AT_YYERROR_DEFINE[
+#include
static int
yylex (void)
{
+ assert (0 <= yylval);
if (yylval--)
return WAIT_FOR_EOF;
else
return EOF;
}
+/* Return argv[1] as an int. */
+static int
+get_args (int argc, const char **argv)
+{
+ int res;
+ char *endp;
+ assert (argc == 2);
+ res = strtol (argv[1], &endp, 10);
+ assert (argv[1] != endp);
+ assert (0 <= res);
+ assert (res <= INT_MAX);
+ assert (errno != ERANGE);
+ return res;
+}
+
int
main (int argc, const char **argv)
{
- assert (argc == 2);
- yylval = atoi (argv[1]);
- yydebug = 1;
- return yyparse ();
+ YYSTYPE yylval_init = get_args (argc, argv);
+ int status = 0;
+ int count;
+]m4_bmatch([$2], [api.push-pull both],
+[[ yypstate *ps = yypstate_new ();
+]])[ yydebug = 1;
+ for (count = 0; count < 2; ++count)
+ {
+ int new_status;
+ yylval = yylval_init;
+ new_status = ]m4_bmatch([$2], [api.push-pull both],
+ [[yypull_parse (ps)]],
+ [[yyparse ()]])[;
+ if (count == 0)
+ status = new_status;
+ else
+ assert (new_status == status);
+ }]m4_bmatch([$2], [api.push-pull both],[[
+ yypstate_delete (ps);]])[
+ return status;
}
]])
-AT_CHECK([bison input.y -o input.c])
-AT_CHECK([$CC $CFLAGS $CPPFLAGS input.c -o input], 0, [], [ignore])
+AT_BISON_OPTION_POPDEFS([$2])
+AT_BISON_CHECK([-o input.c input.y])
+AT_COMPILE([input])
])
@@ -82,15 +449,40 @@ AT_CHECK([$CC $CFLAGS $CPPFLAGS input.c -o input], 0, [], [ignore])
AT_SETUP([Exploding the Stack Size with Alloca])
-AT_DATA_STACK_TORTURE
+m4_pushdef([AT_USE_ALLOCA], [[
+#if (defined __GNUC__ || defined __BUILTIN_VA_ARG_INCR \
+ || defined _AIX || defined _MSC_VER || defined _ALLOCA_H)
+# define YYSTACK_USE_ALLOCA 1
+#endif
+]])
+
+AT_DATA_STACK_TORTURE([AT_USE_ALLOCA])
# Below the limit of 200.
-AT_CHECK([input 20], 0, [], [ignore])
+AT_PARSER_CHECK([./input 20], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
# Two enlargements: 2 * 2 * 200.
-AT_CHECK([input 900], 0, [], [ignore])
+AT_PARSER_CHECK([./input 900], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
# Fails: beyond the limit of 10,000 (which we don't reach anyway since we
# multiply by two starting at 200 => 5120 is the last possible).
-AT_CHECK([input 10000], 1, [], [ignore])
+AT_PARSER_CHECK([./input 10000], 2, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+
+# The push parser can't use alloca since the stacks can't be locals. This test
+# just helps guarantee we don't let the YYSTACK_USE_ALLOCA feature affect
+# push parsers.
+AT_DATA_STACK_TORTURE([AT_USE_ALLOCA],
+[[%define api.push-pull both
+]])
+AT_PARSER_CHECK([./input 20], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+AT_PARSER_CHECK([./input 900], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+AT_PARSER_CHECK([./input 10000], 2, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+
+m4_popdef([AT_USE_ALLOCA])
AT_CLEANUP
@@ -103,354 +495,31 @@ AT_CLEANUP
AT_SETUP([Exploding the Stack Size with Malloc])
-AT_DATA_STACK_TORTURE([[#define YYSTACK_USE_ALLOCA 0]])
+m4_pushdef([AT_USE_ALLOCA], [[#define YYSTACK_USE_ALLOCA 0]])
+
+AT_DATA_STACK_TORTURE([AT_USE_ALLOCA])
# Below the limit of 200.
-AT_CHECK([input 20], 0, [], [ignore])
+AT_PARSER_CHECK([./input 20], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
# Two enlargements: 2 * 2 * 200.
-AT_CHECK([input 900], 0, [], [ignore])
+AT_PARSER_CHECK([./input 900], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
# Fails: beyond the limit of 10,000 (which we don't reach anyway since we
# multiply by two starting at 200 => 5120 is the possible).
-AT_CHECK([input 10000], 1, [], [ignore])
-
-AT_CLEANUP
-
-
-## ----------------- ##
-## GNU AWK Grammar. ##
-## ----------------- ##
-
-AT_SETUP([GNU AWK Grammar])
+AT_PARSER_CHECK([./input 10000], 2, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
-# We have been careful to strip all the actions excepts the
-# mid-rule actions. We rely on %expect to check that there are
-# indeed 65 SR conflicts.
-#
-# Bison was once wrong, due to an incorrect computation of nullable.
-# It reported 485 SR conflicts!
-
-AT_DATA([[input.y]],
-[[%expect 65
-
-%token FUNC_CALL NAME REGEXP
-%token ERROR
-%token YNUMBER YSTRING
-%token RELOP APPEND_OP
-%token ASSIGNOP MATCHOP NEWLINE CONCAT_OP
-%token LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
-%token LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
-%token LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
-%token LEX_GETLINE LEX_NEXTFILE
-%token LEX_IN
-%token LEX_AND LEX_OR INCREMENT DECREMENT
-%token LEX_BUILTIN LEX_LENGTH
-
-/* Lowest to highest */
-%right ASSIGNOP
-%right '?' ':'
-%left LEX_OR
-%left LEX_AND
-%left LEX_GETLINE
-%nonassoc LEX_IN
-%left FUNC_CALL LEX_BUILTIN LEX_LENGTH
-%nonassoc ','
-%nonassoc MATCHOP
-%nonassoc RELOP '<' '>' '|' APPEND_OP TWOWAYIO
-%left CONCAT_OP
-%left YSTRING YNUMBER
-%left '+' '-'
-%left '*' '/' '%'
-%right '!' UNARY
-%right '^'
-%left INCREMENT DECREMENT
-%left '$'
-%left '(' ')'
-%%
-
-start
- : opt_nls program opt_nls
- ;
-
-program
- : rule
- | program rule
- | error
- | program error
- | /* empty */
- ;
-
-rule
- : LEX_BEGIN {} action
- | LEX_END {} action
- | LEX_BEGIN statement_term
- | LEX_END statement_term
- | pattern action
- | action
- | pattern statement_term
- | function_prologue function_body
- ;
-
-func_name
- : NAME
- | FUNC_CALL
- | lex_builtin
- ;
-
-lex_builtin
- : LEX_BUILTIN
- | LEX_LENGTH
- ;
-
-function_prologue
- : LEX_FUNCTION {} func_name '(' opt_param_list r_paren opt_nls
- ;
-
-function_body
- : l_brace statements r_brace opt_semi opt_nls
- | l_brace r_brace opt_semi opt_nls
- ;
-
-
-pattern
- : exp
- | exp ',' exp
- ;
-
-regexp
- /*
- * In this rule, want_regexp tells yylex that the next thing
- * is a regexp so it should read up to the closing slash.
- */
- : '/' {} REGEXP '/'
- ;
-
-action
- : l_brace statements r_brace opt_semi opt_nls
- | l_brace r_brace opt_semi opt_nls
- ;
-
-statements
- : statement
- | statements statement
- | error
- | statements error
- ;
-
-statement_term
- : nls
- | semi opt_nls
- ;
-
-statement
- : semi opt_nls
- | l_brace r_brace
- | l_brace statements r_brace
- | if_statement
- | LEX_WHILE '(' exp r_paren opt_nls statement
- | LEX_DO opt_nls statement LEX_WHILE '(' exp r_paren opt_nls
- | LEX_FOR '(' NAME LEX_IN NAME r_paren opt_nls statement
- | LEX_FOR '(' opt_exp semi opt_nls exp semi opt_nls opt_exp r_paren opt_nls statement
- | LEX_FOR '(' opt_exp semi opt_nls semi opt_nls opt_exp r_paren opt_nls statement
- | LEX_BREAK statement_term
- | LEX_CONTINUE statement_term
- | print '(' expression_list r_paren output_redir statement_term
- | print opt_rexpression_list output_redir statement_term
- | LEX_NEXT statement_term
- | LEX_NEXTFILE statement_term
- | LEX_EXIT opt_exp statement_term
- | LEX_RETURN {} opt_exp statement_term
- | LEX_DELETE NAME '[' expression_list ']' statement_term
- | LEX_DELETE NAME statement_term
- | exp statement_term
- ;
-
-print
- : LEX_PRINT
- | LEX_PRINTF
- ;
-
-if_statement
- : LEX_IF '(' exp r_paren opt_nls statement
- | LEX_IF '(' exp r_paren opt_nls statement
- LEX_ELSE opt_nls statement
- ;
-
-nls
- : NEWLINE
- | nls NEWLINE
- ;
-
-opt_nls
- : /* empty */
- | nls
- ;
-
-input_redir
- : /* empty */
- | '<' simp_exp
- ;
-
-output_redir
- : /* empty */
- | '>' exp
- | APPEND_OP exp
- | '|' exp
- | TWOWAYIO exp
- ;
-
-opt_param_list
- : /* empty */
- | param_list
- ;
-
-param_list
- : NAME
- | param_list comma NAME
- | error
- | param_list error
- | param_list comma error
- ;
-
-/* optional expression, as in for loop */
-opt_exp
- : /* empty */
- | exp
- ;
-
-opt_rexpression_list
- : /* empty */
- | rexpression_list
- ;
-
-rexpression_list
- : rexp
- | rexpression_list comma rexp
- | error
- | rexpression_list error
- | rexpression_list error rexp
- | rexpression_list comma error
- ;
-
-opt_expression_list
- : /* empty */
- | expression_list
- ;
-
-expression_list
- : exp
- | expression_list comma exp
- | error
- | expression_list error
- | expression_list error exp
- | expression_list comma error
- ;
-
-/* Expressions, not including the comma operator. */
-exp : variable ASSIGNOP {} exp
- | '(' expression_list r_paren LEX_IN NAME
- | exp '|' LEX_GETLINE opt_variable
- | exp TWOWAYIO LEX_GETLINE opt_variable
- | LEX_GETLINE opt_variable input_redir
- | exp LEX_AND exp
- | exp LEX_OR exp
- | exp MATCHOP exp
- | regexp
- | '!' regexp %prec UNARY
- | exp LEX_IN NAME
- | exp RELOP exp
- | exp '<' exp
- | exp '>' exp
- | exp '?' exp ':' exp
- | simp_exp
- | exp simp_exp %prec CONCAT_OP
- ;
-
-rexp
- : variable ASSIGNOP {} rexp
- | rexp LEX_AND rexp
- | rexp LEX_OR rexp
- | LEX_GETLINE opt_variable input_redir
- | regexp
- | '!' regexp %prec UNARY
- | rexp MATCHOP rexp
- | rexp LEX_IN NAME
- | rexp RELOP rexp
- | rexp '?' rexp ':' rexp
- | simp_exp
- | rexp simp_exp %prec CONCAT_OP
- ;
-
-simp_exp
- : non_post_simp_exp
- /* Binary operators in order of decreasing precedence. */
- | simp_exp '^' simp_exp
- | simp_exp '*' simp_exp
- | simp_exp '/' simp_exp
- | simp_exp '%' simp_exp
- | simp_exp '+' simp_exp
- | simp_exp '-' simp_exp
- | variable INCREMENT
- | variable DECREMENT
- ;
-
-non_post_simp_exp
- : '!' simp_exp %prec UNARY
- | '(' exp r_paren
- | LEX_BUILTIN
- '(' opt_expression_list r_paren
- | LEX_LENGTH '(' opt_expression_list r_paren
- | LEX_LENGTH
- | FUNC_CALL '(' opt_expression_list r_paren
- | variable
- | INCREMENT variable
- | DECREMENT variable
- | YNUMBER
- | YSTRING
- | '-' simp_exp %prec UNARY
- | '+' simp_exp %prec UNARY
- ;
-
-opt_variable
- : /* empty */
- | variable
- ;
-
-variable
- : NAME
- | NAME '[' expression_list ']'
- | '$' non_post_simp_exp
- ;
-
-l_brace
- : '{' opt_nls
- ;
-
-r_brace
- : '}' opt_nls
- ;
-
-r_paren
- : ')'
- ;
-
-opt_semi
- : /* empty */
- | semi
- ;
-
-semi
- : ';'
- ;
-
-comma : ',' opt_nls
- ;
-
-%%
+AT_DATA_STACK_TORTURE([AT_USE_ALLOCA],
+[[%define api.push-pull both
]])
-
-# Pass plenty of options, to exercise plenty of code, even if we
-# don't actually check the output. But SEGV is watching us, and
-# so might do dmalloc.
-AT_CHECK([[bison --verbose --defines input.y]])
+AT_PARSER_CHECK([./input 20], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+AT_PARSER_CHECK([./input 900], 0, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+AT_PARSER_CHECK([./input 10000], 2, [], [ignore],
+ [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]])
+
+m4_popdef([AT_USE_ALLOCA])
AT_CLEANUP