X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/ed8e1f68e9743c391f4aba148968ea1e5e960973..24d96dd3ebb49d003c29904fc5152ee5ec220e86:/tests/torture.at diff --git a/tests/torture.at b/tests/torture.at index 8fe00f7c..705e131e 100644 --- a/tests/torture.at +++ b/tests/torture.at @@ -1,78 +1,460 @@ # 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-2012 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++; +} +int +main (void) +{ + yydebug = !!getenv ("YYDEBUG"); + return yyparse (); +} +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; +} + +int +main (void) +{ + yydebug = !!getenv ("YYDEBUG"); + return yyparse (); +} +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++; +} + +int +main (void) +{ + yydebug = !!getenv ("YYDEBUG"); + return yyparse (); +} +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], [[%{ +#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; + 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 +464,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 +510,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_PARSER_CHECK([./input 10000], 2, [], [ignore], + [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) -AT_SETUP([GNU AWK Grammar]) - -# 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