]>
Commit | Line | Data |
---|---|---|
1 | # -*- Autoconf -*- | |
2 | ||
3 | cat <<EOF | |
4 | ||
5 | Simple Calculator. | |
6 | ||
7 | EOF | |
8 | ||
9 | ||
10 | ## ---------------------------------------------------- ## | |
11 | ## Compile the grammar described in the documentation. ## | |
12 | ## ---------------------------------------------------- ## | |
13 | ||
14 | ||
15 | # ------------------------- # | |
16 | # Helping Autotest macros. # | |
17 | # ------------------------- # | |
18 | ||
19 | ||
20 | # _AT_DATA_CALC_Y($1, $2, $3, [CPP-DIRECTIVES]) | |
21 | # --------------------------------------------- | |
22 | # Produce `calc.y'. Don't call this macro directly, because it contains | |
23 | # some occurrences of `$1' etc. which will be interpreted by m4. So | |
24 | # you should call it with $1, $2, and $3 as arguments, which is what | |
25 | # AT_DATA_CALC_Y does. | |
26 | AT_DEFINE([_AT_DATA_CALC_Y], | |
27 | [ifelse([$1$2$3], | |
28 | $[1]$[2]$[3], [], | |
29 | [errprint([$0: Invalid arguments: $@ | |
30 | ])m4exit(1)])dnl | |
31 | AT_DATA([calc.y], | |
32 | [[/* Infix notation calculator--calc */ | |
33 | ||
34 | %{ | |
35 | #include <config.h> | |
36 | #include <stdio.h> | |
37 | ||
38 | #if STDC_HEADERS | |
39 | # include <stdlib.h> | |
40 | # include <string.h> | |
41 | #else | |
42 | char *strcat(char *dest, const char *src); | |
43 | #endif | |
44 | #include <ctype.h> | |
45 | ]$4[ | |
46 | ||
47 | static int power (int base, int exponent); | |
48 | static void yyerror (const char *s); | |
49 | static int yylex (void); | |
50 | static int yygetc (void); | |
51 | static void yyungetc (int c); | |
52 | ||
53 | extern void perror (const char *s); | |
54 | %} | |
55 | ||
56 | /* BISON Declarations */ | |
57 | %token NUM | |
58 | ||
59 | %nonassoc '=' /* comparison */ | |
60 | %left '-' '+' | |
61 | %left '*' '/' | |
62 | %left NEG /* negation--unary minus */ | |
63 | %right '^' /* exponentiation */ | |
64 | ||
65 | /* Grammar follows */ | |
66 | %% | |
67 | input: | |
68 | /* empty string */ | |
69 | | input line | |
70 | ; | |
71 | ||
72 | line: | |
73 | '\n' | |
74 | | exp '\n' | |
75 | ; | |
76 | ||
77 | exp: | |
78 | NUM { $$ = $1; } | |
79 | | exp '=' exp | |
80 | { | |
81 | if ($1 != $3) | |
82 | printf ("calc: error: %d != %d\n", $1, $3); | |
83 | $$ = $1 == $3; | |
84 | } | |
85 | | exp '+' exp { $$ = $1 + $3; } | |
86 | | exp '-' exp { $$ = $1 - $3; } | |
87 | | exp '*' exp { $$ = $1 * $3; } | |
88 | | exp '/' exp { $$ = $1 / $3; } | |
89 | | '-' exp %prec NEG { $$ = -$2; } | |
90 | | exp '^' exp { $$ = power ($1, $3); } | |
91 | | '(' exp ')' { $$ = $2; } | |
92 | ; | |
93 | %% | |
94 | /* The input. */ | |
95 | FILE *yyin; | |
96 | ||
97 | static void | |
98 | yyerror (const char *s) | |
99 | { | |
100 | #if YYLSP_NEEDED | |
101 | fprintf (stderr, "%d.%d:%d.%d: ", | |
102 | yylloc.first_line, yylloc.first_column, | |
103 | yylloc.last_line, yylloc.last_column); | |
104 | #endif | |
105 | fprintf (stderr, "%s\n", s); | |
106 | } | |
107 | ||
108 | static int | |
109 | yygetc (void) | |
110 | { | |
111 | int res = getc (yyin); | |
112 | #if YYLSP_NEEDED | |
113 | if (res == '\n') | |
114 | { | |
115 | yylloc.last_line++; | |
116 | yylloc.last_column = 0; | |
117 | } | |
118 | else | |
119 | yylloc.last_column++; | |
120 | #endif | |
121 | return res; | |
122 | } | |
123 | ||
124 | ||
125 | static void | |
126 | yyungetc (int c) | |
127 | { | |
128 | #if YYLSP_NEEDED | |
129 | /* Wrong when C == `\n'. */ | |
130 | yylloc.last_column--; | |
131 | #endif | |
132 | ungetc (c, yyin); | |
133 | } | |
134 | ||
135 | static int | |
136 | read_signed_integer (void) | |
137 | { | |
138 | int c = yygetc (); | |
139 | int sign = 1; | |
140 | int n = 0; | |
141 | ||
142 | if (c == '-') | |
143 | { | |
144 | c = yygetc (); | |
145 | sign = -1; | |
146 | } | |
147 | ||
148 | while (isdigit (c)) | |
149 | { | |
150 | n = 10 * n + (c - '0'); | |
151 | c = yygetc (); | |
152 | } | |
153 | ||
154 | yyungetc (c); | |
155 | ||
156 | return sign * n; | |
157 | } | |
158 | ||
159 | ||
160 | ||
161 | /*---------------------------------------------------------------. | |
162 | | Lexical analyzer returns an integer on the stack and the token | | |
163 | | NUM, or the ASCII character read if not a number. Skips all | | |
164 | | blanks and tabs, returns 0 for EOF. | | |
165 | `---------------------------------------------------------------*/ | |
166 | ||
167 | static int | |
168 | yylex (void) | |
169 | { | |
170 | int c; | |
171 | ||
172 | #if YYLSP_NEEDED | |
173 | yylloc.first_column = yylloc.last_column; | |
174 | yylloc.first_line = yylloc.last_line; | |
175 | #endif | |
176 | ||
177 | /* Skip white space. */ | |
178 | while ((c = yygetc ()) == ' ' || c == '\t') | |
179 | { | |
180 | #if YYLSP_NEEDED | |
181 | yylloc.first_column = yylloc.last_column; | |
182 | yylloc.first_line = yylloc.last_line; | |
183 | #endif | |
184 | } | |
185 | ||
186 | /* process numbers */ | |
187 | if (c == '.' || isdigit (c)) | |
188 | { | |
189 | yyungetc (c); | |
190 | yylval = read_signed_integer (); | |
191 | return NUM; | |
192 | } | |
193 | ||
194 | /* Return end-of-file. */ | |
195 | if (c == EOF) | |
196 | return 0; | |
197 | ||
198 | /* Return single chars. */ | |
199 | return c; | |
200 | } | |
201 | ||
202 | static int | |
203 | power (int base, int exponent) | |
204 | { | |
205 | int res = 1; | |
206 | if (exponent < 0) | |
207 | exit (1); | |
208 | for (/* Niente */; exponent; --exponent) | |
209 | res *= base; | |
210 | return res; | |
211 | } | |
212 | ||
213 | int | |
214 | main (int argn, const char **argv) | |
215 | { | |
216 | if (argn == 2) | |
217 | yyin = fopen (argv[1], "r"); | |
218 | else | |
219 | yyin = stdin; | |
220 | ||
221 | if (!stdin) | |
222 | { | |
223 | perror (argv[1]); | |
224 | exit (1); | |
225 | } | |
226 | ||
227 | #if YYDEBUG | |
228 | yydebug = 1; | |
229 | #endif | |
230 | #if YYLSP_NEEDED | |
231 | yylloc.last_column = 0; | |
232 | yylloc.last_line = 1; | |
233 | #endif | |
234 | yyparse (); | |
235 | return 0; | |
236 | } | |
237 | ]]) | |
238 | ])# _AT_DATA_CALC_Y | |
239 | ||
240 | ||
241 | # AT_DATA_CALC_Y([BISON-OPTIONS]) | |
242 | # ------------------------------- | |
243 | # Produce `calc.y'. | |
244 | AT_DEFINE([AT_DATA_CALC_Y], | |
245 | [_AT_DATA_CALC_Y($[1], $[2], $[3], | |
246 | [ifelse(regexp([$1], [--yyerror-verbose]), | |
247 | [-1], [], | |
248 | [[#define YYERROR_VERBOSE 1]])])]) | |
249 | ||
250 | ||
251 | ||
252 | # _AT_CHECK_CALC(BISON-OPTIONS, INPUT) | |
253 | # ------------------------------------ | |
254 | # Run `calc' on INPUT and expect no STDOUT nor STDERR. | |
255 | # If `--debug' is passed to bison, discard all the debugging traces | |
256 | # preserving only the `parse errors'. Note that since there should be | |
257 | # none, the `grep' will fail with exit status 1. | |
258 | AT_DEFINE([_AT_CHECK_CALC], | |
259 | [ifelse(regexp([$1], [--debug]), | |
260 | [-1], | |
261 | [AT_CHECK([echo "$2" | calc], | |
262 | [0], [], [])], | |
263 | [AT_CHECK([echo "$2" | calc 2>&1 >/dev/null | grep 'parse error' >&2], | |
264 | [1], [], [])])]) | |
265 | ||
266 | ||
267 | # _AT_CHECK_CALC_ERROR(BISON-OPTIONS, INPUT, | |
268 | # [ERROR-LOCATION], [IF-YYERROR-VERBOSE]) | |
269 | # ------------------------------------------------------------ | |
270 | # Run `calc' on INPUT, and expect STDERR. | |
271 | AT_DEFINE([_AT_CHECK_CALC_ERROR], | |
272 | [AT_CHECK([echo "$2" | calc 2>&1 >/dev/null | grep 'parse error' >&2], 0, | |
273 | [], | |
274 | [ifelse(regexp([$1], [--location]), | |
275 | [-1], [], [$3: ])[]dnl | |
276 | parse error[]dnl | |
277 | ifelse(regexp([$1], [--yyerror-verbose]), | |
278 | [-1], [], [$4])[]dnl | |
279 | ||
280 | ])]) | |
281 | ||
282 | ||
283 | # AT_CHECK_CALC([BISON-OPTIONS], [PARSER-EXPECTED-STDERR]) | |
284 | # -------------------------------------------------------- | |
285 | # Start a testing chunk which compiles `calc' grammar with | |
286 | # BISON-OPTIONS, and performs several tests over the parser. | |
287 | AT_DEFINE([AT_CHECK_CALC], | |
288 | [# We use integers to avoid dependencies upon the precision of doubles. | |
289 | AT_SETUP([Calculator $1]) | |
290 | ||
291 | AT_DATA_CALC_Y([$1]) | |
292 | ||
293 | # Specify the output files to avoid problems on different file systems. | |
294 | AT_CHECK([bison calc.y -o calc.c patsubst([$1], [--yyerror-verbose])], | |
295 | [0], [], []) | |
296 | AT_CHECK([$CC $CFLAGS $CPPFLAGS calc.c -o calc], 0, [], []) | |
297 | ||
298 | # Test the priorities. | |
299 | _AT_CHECK_CALC([$1], | |
300 | [1 + 2 * 3 = 7 | |
301 | 1 + 2 * -3 = -5 | |
302 | ||
303 | -1^2 = -1 | |
304 | (-1)^2 = 1 | |
305 | ||
306 | ---1 = -1 | |
307 | ||
308 | 1 - 2 - 3 = -4 | |
309 | 1 - (2 - 3) = 2 | |
310 | ||
311 | 2^2^3 = 256 | |
312 | (2^2)^3 = 64], [$2]) | |
313 | ||
314 | # Some parse errors. | |
315 | _AT_CHECK_CALC_ERROR([$1], [+1], | |
316 | [1.0:1.1], | |
317 | [, unexpected `'+'']) | |
318 | _AT_CHECK_CALC_ERROR([$1], [1//2], | |
319 | [1.2:1.3], | |
320 | [, unexpected `'/'', expecting `NUM' or `'-'' or `'('']) | |
321 | _AT_CHECK_CALC_ERROR([$1], [error], | |
322 | [1.0:1.1], | |
323 | [, unexpected `$undefined.']) | |
324 | _AT_CHECK_CALC_ERROR([$1], [1 = 2 = 3], | |
325 | [1.6:1.7], | |
326 | [, unexpected `'='']) | |
327 | _AT_CHECK_CALC_ERROR([$1], | |
328 | [ | |
329 | +1], | |
330 | [2.0:2.1], | |
331 | [, unexpected `'+'']) | |
332 | ||
333 | AT_CLEANUP(calc calc.c calc.h calc.output) | |
334 | ])# AT_CHECK_CALC | |
335 | ||
336 | ||
337 | ||
338 | ||
339 | # ------------------ # | |
340 | # Test the parsers. # | |
341 | # ------------------ # | |
342 | ||
343 | AT_CHECK_CALC() | |
344 | ||
345 | AT_CHECK_CALC([--defines]) | |
346 | AT_CHECK_CALC([--locations]) | |
347 | AT_CHECK_CALC([--name-prefix=calc]) | |
348 | AT_CHECK_CALC([--verbose]) | |
349 | AT_CHECK_CALC([--yacc]) | |
350 | AT_CHECK_CALC([--yyerror-verbose]) | |
351 | ||
352 | AT_CHECK_CALC([--locations --yyerror-verbose]) | |
353 | ||
354 | AT_CHECK_CALC([--defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose]) | |
355 | ||
356 | AT_CHECK_CALC([--debug]) | |
357 | AT_CHECK_CALC([--debug --defines --locations --name-prefix=calc --verbose --yacc --yyerror-verbose]) |