xref: /386bsd/usr/src/usr.bin/bc/bc.y (revision a2142627)
1 %{
2 /* bc.y: The grammar for a POSIX compatable bc processor with some
3          extensions to the language. */
4 
5 /*  This file is part of bc written for MINIX.
6     Copyright (C) 1991, 1992 Free Software Foundation, Inc.
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License , or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; see the file COPYING.  If not, write to
20     the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22     You may contact the author by:
23        e-mail:  phil@cs.wwu.edu
24       us-mail:  Philip A. Nelson
25                 Computer Science Department, 9062
26                 Western Washington University
27                 Bellingham, WA 98226-9062
28 
29 *************************************************************************/
30 
31 #include "bcdefs.h"
32 #include "global.h"
33 #include "proto.h"
34 %}
35 
36 %start program
37 
38 %union {
39 	char	 *s_value;
40 	char	  c_value;
41 	int	  i_value;
42 	arg_list *a_value;
43        }
44 
45 /* Extensions over POSIX bc.
46    a) NAME was LETTER.  This grammer allows longer names.
47       Single letter names will still work.
48    b) Relational_expression allowed only one comparison.
49       This grammar has added boolean expressions with
50       && (and) || (or) and ! (not) and allowed all of them in
51       full expressions.
52    c) Added an else to the if.
53    d) Call by variable array parameters
54    e) read() procedure that reads a number under program control from stdin.
55    f) halt statement that halts the the program under program control.  It
56       is an executed statement.
57    g) continue statement for for loops.
58    h) optional expressions in the for loop.
59    i) print statement to print multiple numbers per line.
60    j) warranty statement to print an extended warranty notice.
61    j) limits statement to print the processor's limits.
62 */
63 
64 %token <i_value> NEWLINE AND OR NOT
65 %token <s_value> STRING NAME NUMBER
66 /*     '-', '+' are tokens themselves		*/
67 %token <c_value> MUL_OP
68 /*     '*', '/', '%' 				*/
69 %token <c_value> ASSIGN_OP
70 /*     '=', '+=',  '-=', '*=', '/=', '%=', '^=' */
71 %token <s_value> REL_OP
72 /*     '==', '<=', '>=', '!=', '<', '>' 	*/
73 %token <c_value> INCR_DECR
74 /*     '++', '--' 				*/
75 %token <i_value> Define    Break    Quit    Length
76 /*     'define', 'break', 'quit', 'length' 	*/
77 %token <i_value> Return    For    If    While    Sqrt   Else
78 /*     'return', 'for', 'if', 'while', 'sqrt', 'else' 	*/
79 %token <i_value> Scale    Ibase    Obase    Auto  Read
80 /*     'scale', 'ibase', 'obase', 'auto', 'read' 	*/
81 %token <i_value> Warranty, Halt, Last, Continue, Print, Limits
82 /*     'warranty', 'halt', 'last', 'continue', 'print', 'limits'   */
83 
84 /* Types of all other things. */
85 %type <i_value> expression return_expression named_expression opt_expression
86 %type <c_value> '+' '-'
87 %type <a_value> opt_parameter_list opt_auto_define_list define_list
88 %type <a_value> opt_argument_list argument_list
89 %type <i_value> program input_item semicolon_list statement_list
90 %type <i_value> statement function   statement_or_error
91 
92 /* precedence */
93 %left OR
94 %left AND
95 %nonassoc NOT
96 %left REL_OP
97 %right ASSIGN_OP
98 %left '+' '-'
99 %left MUL_OP
100 %right '^'
101 %nonassoc UNARY_MINUS
102 %nonassoc INCR_DECR
103 
104 %%
105 program			: /* empty */
106 			    {
107 			      $$ = 0;
108 			      if (interactive)
109 				{
110 				  printf ("%s\n", BC_VERSION);
111 				  welcome ();
112 				}
113 			    }
114 			| program input_item
115 			;
116 input_item		: semicolon_list NEWLINE
117 			    { run_code (); }
118 			| function
119 			    { run_code (); }
120 			| error NEWLINE
121 			    {
122 			      yyerrok;
123 			      init_gen ();
124 			    }
125 			;
126 semicolon_list		: /* empty */
127 			    { $$ = 0; }
128 			| statement_or_error
129 			| semicolon_list ';' statement_or_error
130 			| semicolon_list ';'
131 			;
132 statement_list		: /* empty */
133 			    { $$ = 0; }
134 			| statement_or_error
135 			| statement_list NEWLINE
136 			| statement_list NEWLINE statement_or_error
137 			| statement_list ';'
138 			| statement_list ';' statement
139 			;
140 statement_or_error	: statement
141   			| error statement
142 			    { $$ = $2; }
143 			;
144 statement 		: Warranty
145 			    { warranty (""); }
146 			| Limits
147 			    { limits (); }
148 			| expression
149 			    {
150 			      if ($1 & 2)
151 				warn ("comparison in expression");
152 			      if ($1 & 1)
153 				generate ("W");
154 			      else
155 				generate ("p");
156 			    }
157 			| STRING
158 			    {
159 			      $$ = 0;
160 			      generate ("w");
161 			      generate ($1);
162 			      free ($1);
163 			    }
164 			| Break
165 			    {
166 			      if (break_label == 0)
167 				yyerror ("Break outside a for/while");
168 			      else
169 				{
170 				  sprintf (genstr, "J%1d:", break_label);
171 				  generate (genstr);
172 				}
173 			    }
174 			| Continue
175 			    {
176 			      warn ("Continue statement");
177 			      if (continue_label == 0)
178 				yyerror ("Continue outside a for");
179 			      else
180 				{
181 				  sprintf (genstr, "J%1d:", continue_label);
182 				  generate (genstr);
183 				}
184 			    }
185 			| Quit
186 			    { exit (0); }
187 			| Halt
188 			    { generate ("h"); }
189 			| Return
190 			    { generate ("0R"); }
191 			| Return '(' return_expression ')'
192 			    { generate ("R"); }
193 			| For
194 			    {
195 			      $1 = break_label;
196 			      break_label = next_label++;
197 			    }
198 			  '(' opt_expression ';'
199 			    {
200 			      if ($4 > 1)
201 				warn ("Comparison in first for expression");
202 			      $4 = next_label++;
203 			      if ($4 < 0)
204 				sprintf (genstr, "N%1d:", $4);
205 			      else
206 				sprintf (genstr, "pN%1d:", $4);
207 			      generate (genstr);
208 			    }
209 			  opt_expression ';'
210 			    {
211 			      if ($7 < 0) generate ("1");
212 			      $7 = next_label++;
213 			      sprintf (genstr, "B%1d:J%1d:", $7, break_label);
214 			      generate (genstr);
215 			      $<i_value>$ = continue_label;
216 			      continue_label = next_label++;
217 			      sprintf (genstr, "N%1d:", continue_label);
218 			      generate (genstr);
219 			    }
220 			  opt_expression ')'
221 			    {
222 			      if ($10 > 1)
223 				warn ("Comparison in third for expression");
224 			      if ($10 < 0)
225 				sprintf (genstr, "J%1d:N%1d:", $4, $7);
226 			      else
227 				sprintf (genstr, "pJ%1d:N%1d:", $4, $7);
228 			      generate (genstr);
229 			    }
230 			  statement
231 			    {
232 			      sprintf (genstr, "J%1d:N%1d:",
233 				       continue_label, break_label);
234 			      generate (genstr);
235 			      break_label = $1;
236 			      continue_label = $<i_value>9;
237 			    }
238 			| If '(' expression ')'
239 			    {
240 			      $3 = if_label;
241 			      if_label = next_label++;
242 			      sprintf (genstr, "Z%1d:", if_label);
243 			      generate (genstr);
244 			    }
245 			  statement  opt_else
246 			    {
247 			      sprintf (genstr, "N%1d:", if_label);
248 			      generate (genstr);
249 			      if_label = $3;
250 			    }
251 			| While
252 			    {
253 			      $1 = next_label++;
254 			      sprintf (genstr, "N%1d:", $1);
255 			      generate (genstr);
256 			    }
257 			'(' expression
258 			    {
259 			      $4 = break_label;
260 			      break_label = next_label++;
261 			      sprintf (genstr, "Z%1d:", break_label);
262 			      generate (genstr);
263 			    }
264 			')' statement
265 			    {
266 			      sprintf (genstr, "J%1d:N%1d:", $1, break_label);
267 			      generate (genstr);
268 			      break_label = $4;
269 			    }
270 			| '{' statement_list '}'
271 			    { $$ = 0; }
272 			| Print
273 			    {  warn ("print statement"); }
274 			  print_list
275 			;
276 print_list		: print_element
277  			| print_element ',' print_list
278 			;
279 print_element		: STRING
280 			    {
281 			      generate ("O");
282 			      generate ($1);
283 			      free ($1);
284 			    }
285 			| expression
286 			    { generate ("P"); }
287  			;
288 opt_else		: /* nothing */
289 			| Else
290 			    {
291 			      warn ("else clause in if statement");
292 			      $1 = next_label++;
293 			      sprintf (genstr, "J%d:N%1d:", $1, if_label);
294 			      generate (genstr);
295 			      if_label = $1;
296 			    }
297 			  statement
298 function 		: Define NAME '(' opt_parameter_list ')' '{'
299 			  NEWLINE opt_auto_define_list
300 			    {
301 			      /* Check auto list against parameter list? */
302 			      check_params ($4,$8);
303 			      sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT),
304 				       arg_str ($4,TRUE), arg_str ($8,TRUE));
305 			      generate (genstr);
306 			      free_args ($4);
307 			      free_args ($8);
308 			      $1 = next_label;
309 			      next_label = 0;
310 			    }
311 			  statement_list NEWLINE '}'
312 			    {
313 			      generate ("0R]");
314 			      next_label = $1;
315 			    }
316 			;
317 opt_parameter_list	: /* empty */
318 			    { $$ = NULL; }
319 			| define_list
320 			;
321 opt_auto_define_list 	: /* empty */
322 			    { $$ = NULL; }
323 			| Auto define_list NEWLINE
324 			    { $$ = $2; }
325 			| Auto define_list ';'
326 			    { $$ = $2; }
327 			;
328 define_list 		: NAME
329 			    { $$ = nextarg (NULL, lookup ($1,SIMPLE)); }
330 			| NAME '[' ']'
331 			    { $$ = nextarg (NULL, lookup ($1,ARRAY)); }
332 			| define_list ',' NAME
333 			    { $$ = nextarg ($1, lookup ($3,SIMPLE)); }
334 			| define_list ',' NAME '[' ']'
335 			    { $$ = nextarg ($1, lookup ($3,ARRAY)); }
336 			;
337 opt_argument_list	: /* empty */
338 			    { $$ = NULL; }
339 			| argument_list
340 			;
341 argument_list 		: expression
342 			    {
343 			      if ($1 > 1) warn ("comparison in argument");
344 			      $$ = nextarg (NULL,0);
345 			    }
346 			| NAME '[' ']'
347 			    {
348 			      sprintf (genstr, "K%d:", -lookup ($1,ARRAY));
349 			      generate (genstr);
350 			      $$ = nextarg (NULL,1);
351 			    }
352 			| argument_list ',' expression
353 			    {
354 			      if ($3 > 1) warn ("comparison in argument");
355 			      $$ = nextarg ($1,0);
356 			    }
357 			| argument_list ',' NAME '[' ']'
358 			    {
359 			      sprintf (genstr, "K%d:", -lookup ($3,ARRAY));
360 			      generate (genstr);
361 			      $$ = nextarg ($1,1);
362 			    }
363 			;
364 opt_expression 		: /* empty */
365 			    {
366 			      $$ = -1;
367 			      warn ("Missing expression in for statement");
368 			    }
369 			| expression
370 			;
371 return_expression	: /* empty */
372 			    {
373 			      $$ = 0;
374 			      generate ("0");
375 			    }
376 			| expression
377 			    {
378 			      if ($1 > 1)
379 				warn ("comparison in return expresion");
380 			    }
381 			;
382 expression		:  named_expression ASSIGN_OP
383 			    {
384 			      if ($2 != '=')
385 				{
386 				  if ($1 < 0)
387 				    sprintf (genstr, "DL%d:", -$1);
388 				  else
389 				    sprintf (genstr, "l%d:", $1);
390 				  generate (genstr);
391 				}
392 			    }
393 			  expression
394 			    {
395 			      if ($4 > 1) warn("comparison in assignment");
396 			      if ($2 != '=')
397 				{
398 				  sprintf (genstr, "%c", $2);
399 				  generate (genstr);
400 				}
401 			      if ($1 < 0)
402 				sprintf (genstr, "S%d:", -$1);
403 			      else
404 				sprintf (genstr, "s%d:", $1);
405 			      generate (genstr);
406 			      $$ = 0;
407 			    }
408 			;
409 			| expression AND
410 			    {
411 			      warn("&& operator");
412 			      $2 = next_label++;
413 			      sprintf (genstr, "DZ%d:p", $2);
414 			      generate (genstr);
415 			    }
416 			  expression
417 			    {
418 			      sprintf (genstr, "DZ%d:p1N%d:", $2, $2);
419 			      generate (genstr);
420 			      $$ = $1 | $4;
421 			    }
422 			| expression OR
423 			    {
424 			      warn("|| operator");
425 			      $2 = next_label++;
426 			      sprintf (genstr, "B%d:", $2);
427 			      generate (genstr);
428 			    }
429 			  expression
430  			    {
431 			      int tmplab;
432 			      tmplab = next_label++;
433 			      sprintf (genstr, "B%d:0J%d:N%d:1N%d:",
434 				       $2, tmplab, $2, tmplab);
435 			      generate (genstr);
436 			      $$ = $1 | $4;
437 			    }
438 			| NOT expression
439 			    {
440 			      $$ = $2;
441 			      warn("! operator");
442 			      generate ("!");
443 			    }
444 			| expression REL_OP expression
445 			    {
446 			      $$ = 3;
447 			      switch (*($2))
448 				{
449 				case '=':
450 				  generate ("=");
451 				  break;
452 
453 				case '!':
454 				  generate ("#");
455 				  break;
456 
457 				case '<':
458 				  if ($2[1] == '=')
459 				    generate ("{");
460 				  else
461 				    generate ("<");
462 				  break;
463 
464 				case '>':
465 				  if ($2[1] == '=')
466 				    generate ("}");
467 				  else
468 				    generate (">");
469 				  break;
470 				}
471 			    }
472 			| expression '+' expression
473 			    {
474 			      generate ("+");
475 			      $$ = $1 | $3;
476 			    }
477 			| expression '-' expression
478 			    {
479 			      generate ("-");
480 			      $$ = $1 | $3;
481 			    }
482 			| expression MUL_OP expression
483 			    {
484 			      genstr[0] = $2;
485 			      genstr[1] = 0;
486 			      generate (genstr);
487 			      $$ = $1 | $3;
488 			    }
489 			| expression '^' expression
490 			    {
491 			      generate ("^");
492 			      $$ = $1 | $3;
493 			    }
494 			| '-' expression  %prec UNARY_MINUS
495 			    {
496 			      generate ("n");
497 			      $$ = $2;
498 			    }
499 			| named_expression
500 			    {
501 			      $$ = 1;
502 			      if ($1 < 0)
503 				sprintf (genstr, "L%d:", -$1);
504 			      else
505 				sprintf (genstr, "l%d:", $1);
506 			      generate (genstr);
507 			    }
508 			| NUMBER
509 			    {
510 			      int len = strlen($1);
511 			      $$ = 1;
512 			      if (len == 1 && *$1 == '0')
513 				generate ("0");
514 			      else if (len == 1 && *$1 == '1')
515 				generate ("1");
516 			      else
517 				{
518 				  generate ("K");
519 				  generate ($1);
520 				  generate (":");
521 				}
522 			      free ($1);
523 			    }
524 			| '(' expression ')'
525 			    { $$ = $2 | 1; }
526 			| NAME '(' opt_argument_list ')'
527 			    {
528 			      $$ = 1;
529 			      if ($3 != NULL)
530 				{
531 				  sprintf (genstr, "C%d,%s:",
532 					   lookup ($1,FUNCT),
533 					   arg_str ($3,FALSE));
534 				  free_args ($3);
535 				}
536 			      else
537 				{
538 				  sprintf (genstr, "C%d:", lookup ($1,FUNCT));
539 				}
540 			      generate (genstr);
541 			    }
542 			| INCR_DECR named_expression
543 			    {
544 			      $$ = 1;
545 			      if ($2 < 0)
546 				{
547 				  if ($1 == '+')
548 				    sprintf (genstr, "DA%d:L%d:", -$2, -$2);
549 				  else
550 				    sprintf (genstr, "DM%d:L%d:", -$2, -$2);
551 				}
552 			      else
553 				{
554 				  if ($1 == '+')
555 				    sprintf (genstr, "i%d:l%d:", $2, $2);
556 				  else
557 				    sprintf (genstr, "d%d:l%d:", $2, $2);
558 				}
559 			      generate (genstr);
560 			    }
561 			| named_expression INCR_DECR
562 			    {
563 			      $$ = 1;
564 			      if ($1 < 0)
565 				{
566 				  sprintf (genstr, "DL%d:x", -$1);
567 				  generate (genstr);
568 				  if ($2 == '+')
569 				    sprintf (genstr, "A%d:", -$1);
570 				  else
571 				      sprintf (genstr, "M%d:", -$1);
572 				}
573 			      else
574 				{
575 				  sprintf (genstr, "l%d:", $1);
576 				  generate (genstr);
577 				  if ($2 == '+')
578 				    sprintf (genstr, "i%d:", $1);
579 				  else
580 				    sprintf (genstr, "d%d:", $1);
581 				}
582 			      generate (genstr);
583 			    }
584 			| Length '(' expression ')'
585 			    { generate ("cL"); $$ = 1;}
586 			| Sqrt '(' expression ')'
587 			    { generate ("cR"); $$ = 1;}
588 			| Scale '(' expression ')'
589 			    { generate ("cS"); $$ = 1;}
590 			| Read '(' ')'
591 			    {
592 			      warn ("read function");
593 			      generate ("cI"); $$ = 1;
594 			    }
595 			;
596 named_expression	: NAME
597 			    { $$ = lookup($1,SIMPLE); }
598 			| NAME '[' expression ']'
599 			    {
600 			      if ($3 > 1) warn("comparison in subscript");
601 			      $$ = lookup($1,ARRAY);
602 			    }
603 			| Ibase
604 			    { $$ = 0; }
605 			| Obase
606 			    { $$ = 1; }
607 			| Scale
608 			    { $$ = 2; }
609 			| Last
610 			    { $$ = 3; }
611 			;
612 %%
613