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