]>
Commit | Line | Data |
---|---|---|
0d533154 AD |
1 | # -*- Autoconf -*- |
2 | ||
3 | cat <<EOF | |
4 | ||
5 | Simple Calculator. | |
6 | ||
7 | EOF | |
8 | ||
db5b3a89 | 9 | |
0d533154 AD |
10 | ## ---------------------------------------------------- ## |
11 | ## Compile the grammar described in the documentation. ## | |
12 | ## ---------------------------------------------------- ## | |
13 | ||
0d533154 | 14 | |
db5b3a89 AD |
15 | # ------------------------- # |
16 | # Helping Autotest macros. # | |
17 | # ------------------------- # | |
18 | ||
19 | ||
d6c2cba0 AD |
20 | # _AT_DATA_CALC_Y($1, $2, $3, [CPP-DIRECTIVES]) |
21 | # --------------------------------------------- | |
db5b3a89 AD |
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], | |
d6c2cba0 AD |
27 | [ifelse([$1$2$3], |
28 | $[1]$[2]$[3], [], | |
29 | [errprint([$0: Invalid arguments: $@ | |
30 | ])m4exit(1)])dnl | |
31 | AT_DATA([calc.y], | |
0d533154 AD |
32 | [[/* Infix notation calculator--calc */ |
33 | ||
34 | %{ | |
35 | #include <stdio.h> | |
36 | #include <stdlib.h> | |
37 | #include <ctype.h> | |
d6c2cba0 | 38 | ]$4[ |
0d533154 AD |
39 | |
40 | static int power (int base, int exponent); | |
41 | static int read_signed_integer (FILE *stream); | |
ceed8467 | 42 | static void yyerror (const char *s); |
5a35a6cb | 43 | static int yylex (void); |
0d533154 AD |
44 | extern void perror (const char *s); |
45 | %} | |
46 | ||
47 | /* BISON Declarations */ | |
48 | %token NUM | |
d6c2cba0 AD |
49 | |
50 | %nonassoc '=' /* comparison */ | |
0d533154 AD |
51 | %left '-' '+' |
52 | %left '*' '/' | |
53 | %left NEG /* negation--unary minus */ | |
54 | %right '^' /* exponentiation */ | |
55 | ||
56 | /* Grammar follows */ | |
57 | %% | |
d6c2cba0 AD |
58 | input: |
59 | /* empty string */ | |
60 | | input line | |
0d533154 AD |
61 | ; |
62 | ||
d6c2cba0 AD |
63 | line: |
64 | '\n' | |
65 | | exp '\n' | |
0d533154 AD |
66 | ; |
67 | ||
d6c2cba0 AD |
68 | exp: |
69 | NUM { $$ = $1; } | |
70 | | exp '=' exp | |
71 | { | |
72 | if ($1 != $3) | |
73 | printf ("calc: error: %d != %d\n", $1, $3); | |
74 | $$ = $1 == $3; | |
75 | } | |
76 | | exp '+' exp { $$ = $1 + $3; } | |
77 | | exp '-' exp { $$ = $1 - $3; } | |
78 | | exp '*' exp { $$ = $1 * $3; } | |
79 | | exp '/' exp { $$ = $1 / $3; } | |
80 | | '-' exp %prec NEG { $$ = -$2; } | |
81 | | exp '^' exp { $$ = power ($1, $3); } | |
82 | | '(' exp ')' { $$ = $2; } | |
0d533154 AD |
83 | ; |
84 | %% | |
ceed8467 AD |
85 | /* The input. */ |
86 | FILE *yyin; | |
05a1d24b | 87 | |
ceed8467 | 88 | static void |
0d533154 AD |
89 | yyerror (const char *s) |
90 | { | |
91 | fprintf (stderr, "%s\n", s); | |
92 | } | |
93 | ||
94 | static int | |
95 | read_signed_integer (FILE *stream) | |
96 | { | |
97 | int c = getc (stream); | |
98 | int sign = 1; | |
99 | int n = 0; | |
100 | ||
101 | if (c == '-') | |
102 | { | |
103 | c = getc (stream); | |
104 | sign = -1; | |
105 | } | |
106 | ||
107 | while (isdigit (c)) | |
108 | { | |
109 | n = 10 * n + (c - '0'); | |
110 | c = getc (stream); | |
111 | } | |
112 | ||
113 | ungetc (c, stream); | |
114 | ||
115 | return sign * n; | |
116 | } | |
117 | ||
118 | /*---------------------------------------------------------------. | |
119 | | Lexical analyzer returns an integer on the stack and the token | | |
120 | | NUM, or the ASCII character read if not a number. Skips all | | |
121 | | blanks and tabs, returns 0 for EOF. | | |
122 | `---------------------------------------------------------------*/ | |
123 | ||
5a35a6cb AD |
124 | static int |
125 | yylex (void) | |
0d533154 AD |
126 | { |
127 | int c; | |
128 | ||
129 | /* Skip white space. */ | |
05a1d24b | 130 | while ((c = getc (yyin)) == ' ' || c == '\t') |
0d533154 AD |
131 | ; |
132 | /* process numbers */ | |
133 | if (c == '.' || isdigit (c)) | |
134 | { | |
05a1d24b AD |
135 | ungetc (c, yyin); |
136 | yylval = read_signed_integer (yyin); | |
0d533154 AD |
137 | return NUM; |
138 | } | |
139 | /* Return end-of-file. */ | |
140 | if (c == EOF) | |
141 | return 0; | |
142 | /* Return single chars. */ | |
143 | return c; | |
144 | } | |
145 | ||
146 | static int | |
147 | power (int base, int exponent) | |
148 | { | |
149 | int res = 1; | |
150 | if (exponent < 0) | |
151 | exit (1); | |
152 | for (/* Niente */; exponent; --exponent) | |
153 | res *= base; | |
154 | return res; | |
155 | } | |
ceed8467 AD |
156 | |
157 | int | |
158 | main (int argn, const char **argv) | |
159 | { | |
160 | if (argn == 2) | |
161 | yyin = fopen (argv[1], "r"); | |
162 | else | |
163 | yyin = stdin; | |
164 | ||
165 | if (!stdin) | |
166 | { | |
167 | perror (argv[1]); | |
168 | exit (1); | |
169 | } | |
db5b3a89 AD |
170 | |
171 | #if YYDEBUG | |
172 | yydebug = 1; | |
173 | #endif | |
ceed8467 AD |
174 | yyparse (); |
175 | return 0; | |
176 | } | |
0d533154 | 177 | ]]) |
db5b3a89 | 178 | ])# _AT_DATA_CALC_Y |
0d533154 | 179 | |
0d533154 | 180 | |
d6c2cba0 AD |
181 | # AT_DATA_CALC_Y([BISON-OPTIONS]) |
182 | # ------------------------------- | |
db5b3a89 AD |
183 | # Produce `calc.y'. |
184 | AT_DEFINE([AT_DATA_CALC_Y], | |
d6c2cba0 AD |
185 | [_AT_DATA_CALC_Y($[1], $[2], $[3], |
186 | [ifelse(regexp([$1], [--yyerror-verbose]), | |
187 | [-1], [], | |
188 | [[#define YYERROR_VERBOSE]])])]) | |
189 | ||
db5b3a89 AD |
190 | |
191 | ||
d6c2cba0 AD |
192 | # _AT_CHECK_CALC(BISON-OPTIONS, INPUT) |
193 | # ------------------------------------ | |
194 | # Run `calc' on INPUT and expect no STDOUT nor STDERR. | |
195 | # If `--debug' is passed to bison, discard all the debugging traces | |
196 | # preserving only the `parse errors'. Note that since there should be | |
197 | # none, the `grep' will fail with exit status 1. | |
db5b3a89 | 198 | AT_DEFINE([_AT_CHECK_CALC], |
d6c2cba0 AD |
199 | [ifelse(regexp([$1], [--debug]), |
200 | [-1], | |
201 | [AT_CHECK([echo "$2" | calc], | |
202 | [0], [], [])], | |
203 | [AT_CHECK([echo "$2" | calc 2>&1 >/dev/null | grep 'parse error' >&2], | |
204 | [1], [], [])])]) | |
205 | ||
206 | ||
207 | # _AT_CHECK_CALC_ERROR(BISON-OPTIONS, INPUT, [IF-YYERROR-VERBOSE]) | |
208 | # ---------------------------------------------------------------- | |
209 | # Run `calc' on INPUT, and expect STDERR. | |
210 | AT_DEFINE([_AT_CHECK_CALC_ERROR], | |
211 | [AT_CHECK([echo "$2" | calc 2>&1 >/dev/null | grep 'parse error' >&2], 0, | |
212 | [], | |
213 | [parse error[]ifelse(regexp([$1], [--yyerror-verbose]), | |
214 | [-1], [], [$3]) | |
215 | ])]) | |
db5b3a89 AD |
216 | |
217 | ||
5a35a6cb AD |
218 | # AT_CHECK_CALC([BISON-OPTIONS], [PARSER-EXPECTED-STDERR]) |
219 | # -------------------------------------------------------- | |
220 | # Start a testing chunk which compiles `calc' grammar with | |
db5b3a89 | 221 | # BISON-OPTIONS, and performs several tests over the parser. |
0d533154 | 222 | AT_DEFINE([AT_CHECK_CALC], |
db5b3a89 | 223 | [# We use integers to avoid dependencies upon the precision of doubles. |
5a35a6cb | 224 | AT_SETUP([Calculator $1]) |
db5b3a89 | 225 | |
d6c2cba0 | 226 | AT_DATA_CALC_Y([$1]) |
db5b3a89 AD |
227 | |
228 | # Specify the output files to avoid problems on different file systems. | |
d6c2cba0 AD |
229 | AT_CHECK([bison calc.y -o calc.c patsubst([$1], [--yyerror-verbose])], |
230 | [0], [], []) | |
db5b3a89 | 231 | AT_CHECK([$CC $CFLAGS calc.c -o calc], 0, [], []) |
0d533154 AD |
232 | |
233 | # Test the priorities. | |
d6c2cba0 AD |
234 | _AT_CHECK_CALC([$1], |
235 | [1 + 2 * 3 = 7 | |
236 | 1 + 2 * -3 = -5 | |
237 | ||
238 | -1^2 = -1 | |
239 | (-1)^2 = 1 | |
0d533154 | 240 | |
d6c2cba0 | 241 | ---1 = -1 |
0d533154 | 242 | |
d6c2cba0 AD |
243 | 1 - 2 - 3 = -4 |
244 | 1 - (2 - 3) = 2 | |
0d533154 | 245 | |
d6c2cba0 AD |
246 | 2^2^3 = 256 |
247 | (2^2)^3 = 64], [$2]) | |
0d533154 | 248 | |
d6c2cba0 AD |
249 | # Some parse errors. |
250 | _AT_CHECK_CALC_ERROR([$1], [+1], | |
251 | [, unexpected `'+'']) | |
252 | _AT_CHECK_CALC_ERROR([$1], [1//2], | |
253 | [, unexpected `'/'', expecting `NUM' or `'-'' or `'('']) | |
254 | _AT_CHECK_CALC_ERROR([$1], [error], | |
255 | [, unexpected `$undefined.']) | |
256 | _AT_CHECK_CALC_ERROR([$1], [1 = 2 = 3], | |
257 | [, unexpected `'='']) | |
0d533154 | 258 | |
5a35a6cb | 259 | AT_CLEANUP(calc calc.c calc.h calc.output) |
db5b3a89 AD |
260 | ])# AT_CHECK_CALC |
261 | ||
262 | ||
db5b3a89 AD |
263 | |
264 | ||
d6c2cba0 AD |
265 | # ------------------ # |
266 | # Test the parsers. # | |
267 | # ------------------ # | |
268 | ||
5a35a6cb AD |
269 | AT_CHECK_CALC() |
270 | # This one is very suspicious. The test fails, but it might be normal. | |
271 | AT_CHECK_CALC([--raw]) | |
db5b3a89 | 272 | |
5a35a6cb AD |
273 | AT_CHECK_CALC([--defines]) |
274 | AT_CHECK_CALC([--name-prefix=calc]) | |
275 | AT_CHECK_CALC([--verbose]) | |
276 | AT_CHECK_CALC([--yacc]) | |
d6c2cba0 AD |
277 | AT_CHECK_CALC([--yyerror-verbose]) |
278 | AT_CHECK_CALC([--defines --name-prefix=calc --verbose --yacc --yyerror-verbose]) | |
db5b3a89 | 279 | |
5a35a6cb | 280 | # When --debug, a lot of data is sent to STDERR, we can't test it. |
d6c2cba0 AD |
281 | AT_CHECK_CALC([--debug]) |
282 | AT_CHECK_CALC([--debug --defines --name-prefix=calc --verbose --yacc --yyerror-verbose]) |