]> git.saurik.com Git - bison.git/blob - tests/calc.at
ecd4c67380249880ad98ff1e73ba537b40eb977f
[bison.git] / tests / calc.at
1 # Checking the output filenames. -*- Autotest -*-
2 # Copyright 2000, 2001 Free Software Foundation, Inc.
3
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2, or (at your option)
7 # any later version.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 # 02111-1307, USA.
18
19 AT_BANNER([[Simple Calculator.]])
20
21 ## ---------------------------------------------------- ##
22 ## Compile the grammar described in the documentation. ##
23 ## ---------------------------------------------------- ##
24
25
26 # ------------------------- #
27 # Helping Autotest macros. #
28 # ------------------------- #
29
30
31 # _AT_DATA_CALC_Y($1, $2, $3, [CPP-DIRECTIVES])
32 # ---------------------------------------------
33 # Produce `calc.y'. Don't call this macro directly, because it contains
34 # some occurrences of `$1' etc. which will be interpreted by m4. So
35 # you should call it with $1, $2, and $3 as arguments, which is what
36 # AT_DATA_CALC_Y does.
37 m4_define([_AT_DATA_CALC_Y],
38 [m4_if([$1$2$3], $[1]$[2]$[3], [],
39 [m4_fatal([$0: Invalid arguments: $@])])dnl
40 AT_DATA([calc.y],
41 [[/* Infix notation calculator--calc */
42
43 %{
44 #include <config.h>
45 /* We don't need a perfect malloc for these tests. */
46 #undef malloc
47 #include <stdio.h>
48
49 #if STDC_HEADERS
50 # include <stdlib.h>
51 # include <string.h>
52 #else
53 char *strcat(char *dest, const char *src);
54 #endif
55 #include <ctype.h>
56
57 static int power (int base, int exponent);
58 static void yyerror (const char *s);
59 static int yylex (void);
60 static int yygetc (void);
61 static void yyungetc (int c);
62
63 extern void perror (const char *s);
64
65 /* Exercise pre-prologue dependency to %union. */
66 typedef int value_t;
67
68 %}
69
70 /* Also exercise %union. */
71 %union
72 {
73 value_t ival; /* A comment to exercise an old bug. */
74 };
75
76 /* Exercise post-prologue dependency to %union. */
77 %{
78 static void id (YYSTYPE *lval);
79 %}
80
81 /* Bison Declarations */
82 %token CALC_EOF 0 "end of file"
83 %token <ival> NUM "number"
84 %type <ival> exp
85
86 %nonassoc '=' /* comparison */
87 %left '-' '+'
88 %left '*' '/'
89 %left NEG /* negation--unary minus */
90 %right '^' /* exponentiation */
91
92 ]$4[
93
94 /* Grammar follows */
95 %%
96 input:
97 line
98 | input line
99 ;
100
101 line:
102 '\n'
103 | exp '\n' {}
104 ;
105
106 exp:
107 NUM { $$ = $1; }
108 | exp '=' exp
109 {
110 if ($1 != $3)
111 fprintf (stderr, "calc: error: %d != %d\n", $1, $3);
112 $$ = $1 == $3;
113 }
114 | exp '+' exp { $$ = $1 + $3; }
115 | exp '-' exp { $$ = $1 - $3; }
116 | exp '*' exp { $$ = $1 * $3; }
117 | exp '/' exp { $$ = $1 / $3; }
118 | '-' exp %prec NEG { $$ = -$2; }
119 | exp '^' exp { $$ = power ($1, $3); }
120 | '(' exp ')' { $$ = $2; }
121 | '(' error ')' { $$ = 0; }
122 ;
123 %%
124 /* The input. */
125 FILE *yyin;
126
127 static void
128 yyerror (const char *s)
129 {
130 #if YYLSP_NEEDED
131 fprintf (stderr, "%d.%d-%d.%d: ",
132 yylloc.first_line, yylloc.first_column,
133 yylloc.last_line, yylloc.last_column);
134 #endif
135 fprintf (stderr, "%s\n", s);
136 }
137
138
139 #if YYLSP_NEEDED
140 static YYLTYPE last_yylloc;
141 #endif
142 static int
143 yygetc (void)
144 {
145 int res = getc (yyin);
146 #if YYLSP_NEEDED
147 last_yylloc = yylloc;
148 if (res == '\n')
149 {
150 yylloc.last_line++;
151 yylloc.last_column = 1;
152 }
153 else
154 yylloc.last_column++;
155 #endif
156 return res;
157 }
158
159
160 static void
161 yyungetc (int c)
162 {
163 #if YYLSP_NEEDED
164 /* Wrong when C == `\n'. */
165 yylloc = last_yylloc;
166 #endif
167 ungetc (c, yyin);
168 }
169
170 static int
171 read_signed_integer (void)
172 {
173 int c = yygetc ();
174 int sign = 1;
175 int n = 0;
176
177 if (c == '-')
178 {
179 c = yygetc ();
180 sign = -1;
181 }
182
183 while (isdigit (c))
184 {
185 n = 10 * n + (c - '0');
186 c = yygetc ();
187 }
188
189 yyungetc (c);
190
191 return sign * n;
192 }
193
194
195
196 /*---------------------------------------------------------------.
197 | Lexical analyzer returns an integer on the stack and the token |
198 | NUM, or the ASCII character read if not a number. Skips all |
199 | blanks and tabs, returns 0 for EOF. |
200 `---------------------------------------------------------------*/
201
202 static int
203 yylex (void)
204 {
205 int c;
206
207 #if YYLSP_NEEDED
208 yylloc.first_column = yylloc.last_column;
209 yylloc.first_line = yylloc.last_line;
210 #endif
211
212 /* Skip white space. */
213 while ((c = yygetc ()) == ' ' || c == '\t')
214 {
215 #if YYLSP_NEEDED
216 yylloc.first_column = yylloc.last_column;
217 yylloc.first_line = yylloc.last_line;
218 #endif
219 }
220
221 /* process numbers */
222 if (c == '.' || isdigit (c))
223 {
224 yyungetc (c);
225 yylval.ival = read_signed_integer ();
226 return NUM;
227 }
228
229 /* Return end-of-file. */
230 if (c == EOF)
231 return CALC_EOF;
232
233 /* Return single chars. */
234 return c;
235 }
236
237 static int
238 power (int base, int exponent)
239 {
240 int res = 1;
241 if (exponent < 0)
242 exit (1);
243 for (/* Niente */; exponent; --exponent)
244 res *= base;
245 return res;
246 }
247
248 void
249 id (YYSTYPE* lval)
250 {
251 }
252
253 int
254 main (int argc, const char **argv)
255 {
256 yyin = NULL;
257
258 if (argc == 2)
259 yyin = fopen (argv[1], "r");
260 else
261 yyin = stdin;
262
263 if (!yyin)
264 {
265 perror (argv[1]);
266 exit (1);
267 }
268
269 #if YYDEBUG
270 yydebug = 1;
271 #endif
272 #if YYLSP_NEEDED
273 yylloc.last_column = 1;
274 yylloc.last_line = 1;
275 #endif
276 yyparse ();
277 return 0;
278 }
279 ]])
280 ])# _AT_DATA_CALC_Y
281
282
283 # AT_DATA_CALC_Y([BISON-OPTIONS])
284 # -------------------------------
285 # Produce `calc.y'.
286 m4_define([AT_DATA_CALC_Y],
287 [_AT_DATA_CALC_Y($[1], $[2], $[3],
288 [m4_bmatch([$1], [--yyerror-verbose],
289 [[%error-verbose]])])])
290
291
292
293 # _AT_CHECK_CALC(BISON-OPTIONS, INPUT, [NUM-STDERR-LINES = 0])
294 # ------------------------------------------------------------
295 # Run `calc' on INPUT and expect no STDOUT nor STDERR.
296 #
297 # If BISON-OPTIONS contains `--debug', then NUM-STDERR-LINES is the number
298 # of expected lines on stderr.
299 m4_define([_AT_CHECK_CALC],
300 [AT_DATA([[input]],
301 [[$2
302 ]])
303 AT_CHECK([./calc input], 0, [], [stderr])dnl
304 AT_CHECK([wc -l <stderr | sed 's/[[^0-9]]//g'], 0,
305 [m4_bmatch([$1], [--debug],
306 [$3], [0])
307 ])
308 ])
309
310
311 # _AT_CHECK_CALC_ERROR(BISON-OPTIONS, INPUT, [NUM-DEBUG-LINES],
312 # [ERROR-LOCATION], [IF-YYERROR-VERBOSE])
313 # ------------------------------------------------------------
314 # Run `calc' on INPUT, and expect a `parse error' message.
315 #
316 # If INPUT starts with a slash, it is used as absolute input file name,
317 # otherwise as contents.
318 #
319 # If BISON-OPTIONS contains `--location', then make sure the ERROR-LOCATION
320 # is correctly output on stderr.
321 #
322 # If BISON-OPTIONS contains `--yyerror-verbose', then make sure the
323 # IF-YYERROR-VERBOSE message is properly output after `parse error, '
324 # on STDERR.
325 #
326 # If BISON-OPTIONS contains `--debug', then NUM-STDERR-LINES is the number
327 # of expected lines on stderr.
328 m4_define([_AT_CHECK_CALC_ERROR],
329 [m4_bmatch([$2], [^/],
330 [AT_CHECK([./calc $2], 0, [], [stderr])],
331 [AT_DATA([[input]],
332 [[$2
333 ]])
334 AT_CHECK([./calc input], 0, [], [stderr])])
335
336 m4_bmatch([$1], [--debug],
337 [AT_CHECK([wc -l <stderr | sed 's/[[^0-9]]//g'], 0, [$3
338 ])])
339
340 # Normalize the observed and expected error messages, depending upon the
341 # options.
342 # 1. Remove the traces from observed.
343 egrep -v '^((Start|Enter|Read|Reduc|Shift)ing|state|Error:|Next|Discarding) ' stderr >at-stderr
344 mv at-stderr stderr
345 # 2. Create the reference error message.
346 AT_DATA([[expout]],
347 [$4
348 ])
349 # 3. If locations are not used, remove them.
350 m4_bmatch([$1], [--location], [],
351 [[sed 's/^[-0-9.]*: //' expout >at-expout
352 mv at-expout expout]])
353 # 4. If error-verbose is not used, strip the`, unexpected....' part.
354 m4_bmatch([$1], [--yyerror-verbose], [],
355 [[sed 's/parse error, .*$/parse error/' expout >at-expout
356 mv at-expout expout]])
357 # 5. Check
358 AT_CHECK([cat stderr], 0, [expout])
359 ])
360
361
362 # AT_CHECK_CALC([BISON-OPTIONS], [PARSER-EXPECTED-STDERR])
363 # --------------------------------------------------------
364 # Start a testing chunk which compiles `calc' grammar with
365 # BISON-OPTIONS, and performs several tests over the parser.
366 m4_define([AT_CHECK_CALC],
367 [# We use integers to avoid dependencies upon the precision of doubles.
368 AT_SETUP([Calculator $1])
369
370 AT_DATA_CALC_Y([$1])
371
372 # Specify the output files to avoid problems on different file systems.
373 AT_CHECK([bison calc.y -o calc.c m4_bpatsubst([$1], [--yyerror-verbose])],
374 [0], [], [])
375
376 AT_CHECK([$CC $CFLAGS $CPPFLAGS calc.c -o calc], 0, [], [ignore])
377
378 # Test the priorities.
379 _AT_CHECK_CALC([$1],
380 [1 + 2 * 3 = 7
381 1 + 2 * -3 = -5
382
383 -1^2 = -1
384 (-1)^2 = 1
385
386 ---1 = -1
387
388 1 - 2 - 3 = -4
389 1 - (2 - 3) = 2
390
391 2^2^3 = 256
392 (2^2)^3 = 64], [486])
393
394 # Some parse errors.
395 _AT_CHECK_CALC_ERROR([$1], [0 0], [10],
396 [1.3-1.4: parse error, unexpected "number"])
397 _AT_CHECK_CALC_ERROR([$1], [1//2], [13],
398 [1.3-1.4: parse error, unexpected '/', expecting "number" or '-' or '('])
399 _AT_CHECK_CALC_ERROR([$1], [error], [4],
400 [1.1-1.2: parse error, unexpected $undefined., expecting "number" or '-' or '\n' or '('])
401 _AT_CHECK_CALC_ERROR([$1], [1 = 2 = 3], [19],
402 [1.7-1.8: parse error, unexpected '='])
403 _AT_CHECK_CALC_ERROR([$1],
404 [
405 +1],
406 [13],
407 [2.1-2.2: parse error, unexpected '+'])
408 # Exercise error messages with EOF: work on an empty file.
409 _AT_CHECK_CALC_ERROR([$1],
410 [/dev/null],
411 [4],
412 [1.1-1.2: parse error, unexpected "end of file", expecting "number" or '-' or '\n' or '('])
413
414 # Exercise the error token: without it, we die at the first error,
415 # hence be sure i. to have several errors, ii. to test the action
416 # associated to `error'.
417 _AT_CHECK_CALC_ERROR([$1],
418 [(1 ++ 2) + (0 0) = 1],
419 [76],
420 [1.5-1.6: parse error, unexpected '+', expecting "number" or '-' or '('
421 1.15-1.16: parse error, unexpected "number"
422 calc: error: 0 != 1])
423
424 # Add a studid example demonstrating that Bison can further improve the
425 # error message. FIXME: Fix this ridiculous message.
426 _AT_CHECK_CALC_ERROR([$1],
427 [()],
428 [21],
429 [1.2-1.3: parse error, unexpected ')', expecting error or "number" or '-' or '('])
430
431 AT_CLEANUP
432 ])# AT_CHECK_CALC
433
434
435
436
437 # ------------------ #
438 # Test the parsers. #
439 # ------------------ #
440
441 AT_CHECK_CALC()
442
443 AT_CHECK_CALC([--defines])
444 AT_CHECK_CALC([--locations])
445 AT_CHECK_CALC([--name-prefix=calc])
446 AT_CHECK_CALC([--verbose])
447 AT_CHECK_CALC([--yacc])
448 AT_CHECK_CALC([--yyerror-verbose])
449
450 AT_CHECK_CALC([--locations --yyerror-verbose])
451
452 AT_CHECK_CALC([--defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose])
453
454 AT_CHECK_CALC([--debug])
455 AT_CHECK_CALC([--debug --defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose])