xref: /netbsd-src/external/bsd/bc/dist/bc.y (revision f92961fef281136ac03eddf12414300888d10da1)
1 /*	$NetBSD: bc.y,v 1.2 2017/04/10 15:13:04 christos Exp $ */
2 
3 /*
4  * Copyright (C) 1991-1994, 1997, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
5  * Copyright (C) 2016-2017 Philip A. Nelson.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The names Philip A. Nelson and Free Software Foundation may not be
18  *    used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY PHILIP A. NELSON ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL PHILIP A. NELSON OR THE FREE SOFTWARE FOUNDATION BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /* bc.y: The grammar for a POSIX compatable bc processor with some
35          extensions to the language. */
36 
37 %{
38 
39 #include "bcdefs.h"
40 #include "global.h"
41 #include "proto.h"
42 
43 /* current function number. */
44 int cur_func = -1;
45 
46 /* Expression encoded information -- See comment at expression rules. */
47 #define EX_ASSGN 0
48 #define EX_REG   1
49 #define EX_COMP  2
50 #define EX_PAREN 4
51 #define EX_VOID  8
52 #define EX_EMPTY 16
53 
54 %}
55 
56 %start program
57 
58 %union {
59 	char	 *s_value;
60 	char	  c_value;
61 	int	  i_value;
62 	arg_list *a_value;
63        }
64 
65 /* Extensions over POSIX bc.
66    a) NAME was LETTER.  This grammar allows longer names.
67       Single letter names will still work.
68    b) Relational_expression allowed only one comparison.
69       This grammar has added boolean expressions with
70       && (and) || (or) and ! (not) and allowed all of them in
71       full expressions.
72    c) Added an else to the if.
73    d) Call by variable array parameters
74    e) read() procedure that reads a number under program control from stdin.
75    f) halt statement that halts the the program under program control.  It
76       is an executed statement.
77    g) continue statement for for loops.
78    h) optional expressions in the for loop.
79    i) print statement to print multiple numbers per line.
80    j) warranty statement to print an extended warranty notice.
81    k) limits statement to print the processor's limits.
82    l) void functions.
83 */
84 
85 %token <i_value> ENDOFLINE AND OR NOT
86 %token <s_value> STRING NAME NUMBER
87 /*     '-', '+' are tokens themselves		*/
88 /*     '=', '+=',  '-=', '*=', '/=', '%=', '^=' */
89 %token <c_value> ASSIGN_OP
90 /*     '==', '<=', '>=', '!=', '<', '>' 	*/
91 %token <s_value> REL_OP
92 /*     '++', '--' 				*/
93 %token <c_value> INCR_DECR
94 /*     'define', 'break', 'quit', 'length' 	*/
95 %token <i_value> Define    Break    Quit    Length
96 /*     'return', 'for', 'if', 'while', 'sqrt', 'else' 	*/
97 %token <i_value> Return    For    If    While    Sqrt   Else
98 /*     'scale', 'ibase', 'obase', 'auto', 'read', 'random' 	*/
99 %token <i_value> Scale Ibase Obase Auto    Read    Random
100 /*     'warranty', 'halt', 'last', 'continue', 'print', 'limits'   */
101 %token <i_value> Warranty  Halt  Last  Continue  Print  Limits
102 /*     'history', 'void' */
103 %token <i_value> UNARY_MINUS HistoryVar Void
104 
105 
106 /* Types of all other things. */
107 %type <i_value> expression return_expression named_expression opt_expression
108 %type <c_value> '+' '-' '*' '/' '%'
109 %type <a_value> opt_parameter_list opt_auto_define_list define_list
110 %type <a_value> opt_argument_list argument_list
111 %type <i_value> program input_item semicolon_list statement_list
112 %type <i_value> statement function statement_or_error required_eol
113 %type <i_value> opt_void
114 
115 /* precedence */
116 %left OR
117 %left AND
118 %nonassoc NOT
119 %left REL_OP
120 %right ASSIGN_OP
121 %left '+' '-'
122 %left '*' '/' '%'
123 %right '^'
124 %nonassoc UNARY_MINUS
125 %nonassoc INCR_DECR
126 
127 %%
128 program			: /* empty */
129 			    {
130 			      $$ = 0;
131 			      if (interactive && !quiet)
132 				{
133 				  show_bc_version ();
134 				  welcome ();
135 				}
136 			    }
137 			| program input_item
138 			;
139 input_item		: semicolon_list ENDOFLINE
140 			    { run_code (); }
141 			| function
142 			    { run_code (); }
143 			| error ENDOFLINE
144 			    {
145 			      yyerrok;
146 			      init_gen ();
147 			    }
148 			;
149 opt_newline		: /* empty */
150 			| ENDOFLINE
151 			    { ct_warn ("newline not allowed"); }
152 			;
153 semicolon_list		: /* empty */
154 			    { $$ = 0; }
155 			| statement_or_error
156 			| semicolon_list ';' statement_or_error
157 			| semicolon_list ';'
158 			;
159 statement_list		: /* empty */
160 			    { $$ = 0; }
161 			| statement_or_error
162 			| statement_list ENDOFLINE
163 			| statement_list ENDOFLINE statement_or_error
164 			| statement_list ';'
165 			| statement_list ';' statement
166 			;
167 statement_or_error	: statement
168   			| error statement
169 			    { $$ = $2; }
170 			;
171 statement 		: Warranty
172 			    { warranty (""); }
173 			| Limits
174 			    { limits (); }
175 			| expression
176 			    {
177 			      if ($1 & EX_COMP)
178 				ct_warn ("comparison in expression");
179 			      if ($1 & EX_REG)
180 				generate ("W");
181 			      else
182 				generate ("p");
183 			    }
184 			| STRING
185 			    {
186 			      $$ = 0;
187 			      generate ("w");
188 			      generate ($1);
189 			      free ($1);
190 			    }
191 			| Break
192 			    {
193 			      if (break_label == 0)
194 				yyerror ("Break outside a for/while");
195 			      else
196 				{
197 				  snprintf (genstr, genlen, "J%1d:",
198 				  	    break_label);
199 				  generate (genstr);
200 				}
201 			    }
202 			| Continue
203 			    {
204 			      ct_warn ("Continue statement");
205 			      if (continue_label == 0)
206 				yyerror ("Continue outside a for");
207 			      else
208 				{
209 				  snprintf (genstr, genlen, "J%1d:",
210 					    continue_label);
211 				  generate (genstr);
212 				}
213 			    }
214 			| Quit
215 			    { bc_exit (0); }
216 			| Halt
217 			    { generate ("h"); }
218 			| Return return_expression
219 			    { generate ("R"); }
220 			| For
221 			    {
222 			      $1 = break_label;
223 			      break_label = next_label++;
224 			    }
225 			  '(' opt_expression ';'
226 			    {
227 			      if ($4 & EX_COMP)
228 				ct_warn ("Comparison in first for expression");
229 			      if ($4 & EX_VOID)
230 				yyerror ("first expression is void");
231 			      if (!($4 & EX_EMPTY))
232 				generate ("p");
233 			      $4 = next_label++;
234 			      snprintf (genstr, genlen, "N%1d:", $4);
235 			      generate (genstr);
236 			    }
237 			  opt_expression ';'
238 			    {
239 			      if ($7 & EX_VOID)
240 				yyerror ("second expression is void");
241 			      if ($7 & EX_EMPTY ) generate ("1");
242 			      $7 = next_label++;
243 			      snprintf (genstr, genlen, "B%1d:J%1d:", $7,
244 			      		break_label);
245 			      generate (genstr);
246 			      $<i_value>$ = continue_label;
247 			      continue_label = next_label++;
248 			      snprintf (genstr, genlen, "N%1d:",
249 			      		continue_label);
250 			      generate (genstr);
251 			    }
252 			  opt_expression ')'
253 			    {
254 			      if ($10 & EX_COMP)
255 				ct_warn ("Comparison in third for expression");
256 			      if ($10 & EX_VOID)
257 				yyerror ("third expression is void");
258 			      if ($10 & EX_EMPTY)
259 				snprintf (genstr, genlen, "J%1d:N%1d:", $4, $7);
260 			      else
261 				snprintf (genstr, genlen, "pJ%1d:N%1d:", $4, $7);
262 			      generate (genstr);
263 			    }
264 			  opt_newline statement
265 			    {
266 			      snprintf (genstr, genlen, "J%1d:N%1d:",
267 				       continue_label, break_label);
268 			      generate (genstr);
269 			      break_label = $1;
270 			      continue_label = $<i_value>9;
271 			    }
272 			| If '(' expression ')'
273 			    {
274 			      if ($3 & EX_VOID)
275 				yyerror ("void expression");
276 			      $3 = if_label;
277 			      if_label = next_label++;
278 			      snprintf (genstr, genlen, "Z%1d:", if_label);
279 			      generate (genstr);
280 			    }
281 			  opt_newline statement  opt_else
282 			    {
283 			      snprintf (genstr, genlen, "N%1d:", if_label);
284 			      generate (genstr);
285 			      if_label = $3;
286 			    }
287 			| While
288 			    {
289 			      $1 = continue_label;
290 			      continue_label = next_label++;
291 			      snprintf (genstr, genlen, "N%1d:",
292 					continue_label);
293 			      generate (genstr);
294 			    }
295 			'(' expression
296 			    {
297 			      if ($4 & EX_VOID)
298 				yyerror ("void expression");
299 			      $4 = break_label;
300 			      break_label = next_label++;
301 			      snprintf (genstr, genlen, "Z%1d:", break_label);
302 			      generate (genstr);
303 			    }
304 			')' opt_newline statement
305 			    {
306 			      snprintf (genstr, genlen, "J%1d:N%1d:",
307 					continue_label, break_label);
308 			      generate (genstr);
309 			      break_label = $4;
310 			      continue_label = $1;
311 			    }
312 			| '{' statement_list '}'
313 			    { $$ = 0; }
314 			| Print
315 			    {  ct_warn ("print statement"); }
316 			  print_list
317 			;
318 print_list		: print_element
319  			| print_element ',' print_list
320 			;
321 print_element		: STRING
322 			    {
323 			      generate ("O");
324 			      generate ($1);
325 			      free ($1);
326 			    }
327 			| expression
328 			    {
329 			      if ($1 & EX_VOID)
330 				yyerror ("void expression in print");
331 			      generate ("P");
332 			    }
333  			;
334 opt_else		: /* nothing */
335 			| Else
336 			    {
337 			      ct_warn ("else clause in if statement");
338 			      $1 = next_label++;
339 			      snprintf (genstr, genlen, "J%d:N%1d:", $1,
340 					if_label);
341 			      generate (genstr);
342 			      if_label = $1;
343 			    }
344 			  opt_newline statement
345 			;
346 function 		: Define opt_void NAME '(' opt_parameter_list ')' opt_newline
347      			  '{' required_eol opt_auto_define_list
348 			    { char *params, *autos;
349 			      /* Check auto list against parameter list? */
350 			      check_params ($5,$10);
351 			      params = arg_str ($5);
352 			      autos  = arg_str ($10);
353 			      set_genstr_size (30 + strlen (params)
354 					       + strlen (autos));
355 			      cur_func = lookup($3,FUNCTDEF);
356 			      snprintf (genstr, genlen, "F%d,%s.%s[", cur_func,
357 					params, autos);
358 			      generate (genstr);
359 			      functions[cur_func].f_void = $2;
360 			      free_args ($5);
361 			      free_args ($10);
362 			      $1 = next_label;
363 			      next_label = 1;
364 			    }
365 			  statement_list /* ENDOFLINE */ '}'
366 			    {
367 			      generate ("0R]");
368 			      next_label = $1;
369 			      cur_func = -1;
370 			    }
371 			;
372 opt_void		: /* empty */
373 			    { $$ = 0; }
374 			| Void
375 			    {
376 			      $$ = 1;
377 			      ct_warn ("void functions");
378 			    }
379 			;
380 opt_parameter_list	: /* empty */
381 			    { $$ = NULL; }
382 			| define_list
383 			;
384 opt_auto_define_list 	: /* empty */
385 			    { $$ = NULL; }
386 			| Auto define_list ENDOFLINE
387 			    { $$ = $2; }
388 			| Auto define_list ';'
389 			    { $$ = $2; }
390 			;
391 define_list 		: NAME
392 			    { $$ = nextarg (NULL, lookup ($1,SIMPLE), FALSE);}
393 			| NAME '[' ']'
394 			    { $$ = nextarg (NULL, lookup ($1,ARRAY), FALSE); }
395 			| '*' NAME '[' ']'
396 			    { $$ = nextarg (NULL, lookup ($2,ARRAY), TRUE);
397 			      ct_warn ("Call by variable arrays");
398 			    }
399 			| '&' NAME '[' ']'
400 			    { $$ = nextarg (NULL, lookup ($2,ARRAY), TRUE);
401 			      ct_warn ("Call by variable arrays");
402 			    }
403 			| define_list ',' NAME
404 			    { $$ = nextarg ($1, lookup ($3,SIMPLE), FALSE); }
405 			| define_list ',' NAME '[' ']'
406 			    { $$ = nextarg ($1, lookup ($3,ARRAY), FALSE); }
407 			| define_list ',' '*' NAME '[' ']'
408 			    { $$ = nextarg ($1, lookup ($4,ARRAY), TRUE);
409 			      ct_warn ("Call by variable arrays");
410 			    }
411 			| define_list ',' '&' NAME '[' ']'
412 			    { $$ = nextarg ($1, lookup ($4,ARRAY), TRUE);
413 			      ct_warn ("Call by variable arrays");
414 			    }
415 			;
416 opt_argument_list	: /* empty */
417 			    { $$ = NULL; }
418 			| argument_list
419 			;
420 argument_list 		: expression
421 			    {
422 			      if ($1 & EX_COMP)
423 				ct_warn ("comparison in argument");
424 			      if ($1 & EX_VOID)
425 				yyerror ("void argument");
426 			      $$ = nextarg (NULL,0,FALSE);
427 			    }
428 			| NAME '[' ']'
429 			    {
430 			      snprintf (genstr, genlen, "K%d:",
431 					-lookup ($1,ARRAY));
432 			      generate (genstr);
433 			      $$ = nextarg (NULL,1,FALSE);
434 			    }
435 			| argument_list ',' expression
436 			    {
437 			      if ($3 & EX_COMP)
438 				ct_warn ("comparison in argument");
439 			      if ($3 & EX_VOID)
440 				yyerror ("void argument");
441 			      $$ = nextarg ($1,0,FALSE);
442 			    }
443 			| argument_list ',' NAME '[' ']'
444 			    {
445 			      snprintf (genstr, genlen, "K%d:",
446 					-lookup ($3,ARRAY));
447 			      generate (genstr);
448 			      $$ = nextarg ($1,1,FALSE);
449 			    }
450 			;
451 
452 /* Expression lval meanings!  (Bits mean something!)  (See defines above)
453  *  0 => Top op is assignment.
454  *  1 => Top op is not assignment.
455  *  2 => Comparison is somewhere in expression.
456  *  4 => Expression is in parenthesis.
457  *  8 => Expression is void!
458  * 16 => Empty optional expression.
459  */
460 
461 opt_expression 		: /* empty */
462 			    {
463 			      $$ = EX_EMPTY;
464 			      ct_warn ("Missing expression in for statement");
465 			    }
466 			| expression
467 			;
468 return_expression	: /* empty */
469 			    {
470 			      $$ = 0;
471 			      generate ("0");
472 			      if (cur_func == -1)
473 				yyerror("Return outside of a function.");
474 			    }
475 			| expression
476 			    {
477 			      if ($1 & EX_COMP)
478 				ct_warn ("comparison in return expresion");
479 			      if (!($1 & EX_PAREN))
480 				ct_warn ("return expression requires parenthesis");
481 			      if ($1 & EX_VOID)
482 				yyerror("return requires non-void expression");
483 			      if (cur_func == -1)
484 				yyerror("Return outside of a function.");
485 			      else if (functions[cur_func].f_void)
486 				yyerror("Return expression in a void function.");
487 			    }
488 			;
489 expression		:  named_expression ASSIGN_OP
490 			    {
491 			      if ($2 != '=')
492 				{
493 				  if ($1 < 0)
494 				    snprintf (genstr, genlen, "DL%d:", -$1);
495 				  else
496 				    snprintf (genstr, genlen, "l%d:", $1);
497 				  generate (genstr);
498 				}
499 			    }
500 			  expression
501 			    {
502 			      if ($4 & EX_ASSGN)
503 				ct_warn("comparison in assignment");
504 			      if ($4 & EX_VOID)
505 				yyerror("Assignment of a void expression");
506 			      if ($2 != '=')
507 				{
508 				  snprintf (genstr, genlen, "%c", $2);
509 				  generate (genstr);
510 				}
511 			      if ($1 < 0)
512 				snprintf (genstr, genlen, "S%d:", -$1);
513 			      else
514 				snprintf (genstr, genlen, "s%d:", $1);
515 			      generate (genstr);
516 			      $$ = EX_ASSGN;
517 			    }
518 			| expression AND
519 			    {
520 			      ct_warn("&& operator");
521 			      $2 = next_label++;
522 			      snprintf (genstr, genlen, "DZ%d:p", $2);
523 			      generate (genstr);
524 			    }
525 			  expression
526 			    {
527 			      if (($1 & EX_VOID) || ($4 & EX_VOID))
528 				yyerror ("void expression with &&");
529 			      snprintf (genstr, genlen, "DZ%d:p1N%d:", $2, $2);
530 			      generate (genstr);
531 			      $$ = ($1 | $4) & ~EX_PAREN;
532 			    }
533 			| expression OR
534 			    {
535 			      ct_warn("|| operator");
536 			      $2 = next_label++;
537 			      snprintf (genstr, genlen, "B%d:", $2);
538 			      generate (genstr);
539 			    }
540 			  expression
541  			    {
542 			      int tmplab;
543 			      if (($1 & EX_VOID) || ($4 & EX_VOID))
544 				yyerror ("void expression with ||");
545 			      tmplab = next_label++;
546 			      snprintf (genstr, genlen, "B%d:0J%d:N%d:1N%d:",
547 				       $2, tmplab, $2, tmplab);
548 			      generate (genstr);
549 			      $$ = ($1 | $4) & ~EX_PAREN;
550 			    }
551 			| NOT expression
552 			    {
553 			      if ($2 & EX_VOID)
554 				yyerror ("void expression with !");
555 			      $$ = $2 & ~EX_PAREN;
556 			      ct_warn("! operator");
557 			      generate ("!");
558 			    }
559 			| expression REL_OP expression
560 			    {
561 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
562 				yyerror ("void expression with comparison");
563 			      $$ = EX_REG | EX_COMP;
564 			      switch (*($2))
565 				{
566 				case '=':
567 				  generate ("=");
568 				  break;
569 
570 				case '!':
571 				  generate ("#");
572 				  break;
573 
574 				case '<':
575 				  if ($2[1] == '=')
576 				    generate ("{");
577 				  else
578 				    generate ("<");
579 				  break;
580 
581 				case '>':
582 				  if ($2[1] == '=')
583 				    generate ("}");
584 				  else
585 				    generate (">");
586 				  break;
587 				}
588                               free($2);
589 			    }
590 			| expression '+' expression
591 			    {
592 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
593 				yyerror ("void expression with +");
594 			      generate ("+");
595 			      $$ = ($1 | $3) & ~EX_PAREN;
596 			    }
597 			| expression '-' expression
598 			    {
599 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
600 				yyerror ("void expression with -");
601 			      generate ("-");
602 			      $$ = ($1 | $3) & ~EX_PAREN;
603 			    }
604 			| expression '*' expression
605 			    {
606 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
607 				yyerror ("void expression with *");
608 			      generate ("*");
609 			      $$ = ($1 | $3) & ~EX_PAREN;
610 			    }
611 			| expression '/' expression
612 			    {
613 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
614 				yyerror ("void expression with /");
615 			      generate ("/");
616 			      $$ = ($1 | $3) & ~EX_PAREN;
617 			    }
618 			| expression '%' expression
619 			    {
620 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
621 				yyerror ("void expression with %%");
622 			      generate ("%");
623 			      $$ = ($1 | $3) & ~EX_PAREN;
624 			    }
625 			| expression '^' expression
626 			    {
627 			      if (($1 & EX_VOID) || ($3 & EX_VOID))
628 				yyerror ("void expression with ^");
629 			      generate ("^");
630 			      $$ = ($1 | $3) & ~EX_PAREN;
631 			    }
632 			| '-' expression  %prec UNARY_MINUS
633 			    {
634 			      if ($2 & EX_VOID)
635 				yyerror ("void expression with unary -");
636 			      generate ("n");
637 			      $$ = $2 & ~EX_PAREN;
638 			    }
639 			| named_expression
640 			    {
641 			      $$ = EX_REG;
642 			      if ($1 < 0)
643 				snprintf (genstr, genlen, "L%d:", -$1);
644 			      else
645 				snprintf (genstr, genlen, "l%d:", $1);
646 			      generate (genstr);
647 			    }
648 			| NUMBER
649 			    {
650 			      int len = strlen($1);
651 			      $$ = EX_REG;
652 			      if (len == 1 && *$1 == '0')
653 				generate ("0");
654 			      else if (len == 1 && *$1 == '1')
655 				generate ("1");
656 			      else
657 				{
658 				  generate ("K");
659 				  generate ($1);
660 				  generate (":");
661 				}
662 			      free ($1);
663 			    }
664 			| '(' expression ')'
665 			    {
666 			      if ($2 & EX_VOID)
667 				yyerror ("void expression in parenthesis");
668 			      $$ = $2 | EX_REG | EX_PAREN;
669 			    }
670 			| NAME '(' opt_argument_list ')'
671 			    { int fn;
672 			      fn = lookup ($1,FUNCT);
673 			      if (functions[fn].f_void)
674 				$$ = EX_VOID;
675 			      else
676 				$$ = EX_REG;
677 			      if ($3 != NULL)
678 				{ char *params = call_str ($3);
679 				  set_genstr_size (20 + strlen (params));
680 				  snprintf (genstr, genlen, "C%d,%s:", fn,
681 				  	    params);
682 				  free_args ($3);
683 				}
684 			      else
685 				{
686 				  snprintf (genstr, genlen, "C%d:", fn);
687 				}
688 			      generate (genstr);
689 			    }
690 			| INCR_DECR named_expression
691 			    {
692 			      $$ = EX_REG;
693 			      if ($2 < 0)
694 				{
695 				  if ($1 == '+')
696 				    snprintf (genstr, genlen, "DA%d:L%d:", -$2, -$2);
697 				  else
698 				    snprintf (genstr, genlen, "DM%d:L%d:", -$2, -$2);
699 				}
700 			      else
701 				{
702 				  if ($1 == '+')
703 				    snprintf (genstr, genlen, "i%d:l%d:", $2, $2);
704 				  else
705 				    snprintf (genstr, genlen, "d%d:l%d:", $2, $2);
706 				}
707 			      generate (genstr);
708 			    }
709 			| named_expression INCR_DECR
710 			    {
711 			      $$ = EX_REG;
712 			      if ($1 < 0)
713 				{
714 				  snprintf (genstr, genlen, "DL%d:x", -$1);
715 				  generate (genstr);
716 				  if ($2 == '+')
717 				    snprintf (genstr, genlen, "A%d:", -$1);
718 				  else
719 				      snprintf (genstr, genlen, "M%d:", -$1);
720 				}
721 			      else
722 				{
723 				  snprintf (genstr, genlen, "l%d:", $1);
724 				  generate (genstr);
725 				  if ($2 == '+')
726 				    snprintf (genstr, genlen, "i%d:", $1);
727 				  else
728 				    snprintf (genstr, genlen, "d%d:", $1);
729 				}
730 			      generate (genstr);
731 			    }
732 			| Length '(' expression ')'
733 			    {
734 			      if ($3 & EX_VOID)
735 				yyerror ("void expression in length()");
736 			      generate ("cL");
737 			      $$ = EX_REG;
738 			    }
739 			| Sqrt '(' expression ')'
740 			    {
741 			      if ($3 & EX_VOID)
742 				yyerror ("void expression in sqrt()");
743 			      generate ("cR");
744 			      $$ = EX_REG;
745 			    }
746 			| Scale '(' expression ')'
747 			    {
748 			      if ($3 & EX_VOID)
749 				yyerror ("void expression in scale()");
750 			      generate ("cS");
751 			      $$ = EX_REG;
752 			    }
753 			| Read '(' ')'
754 			    {
755 			      ct_warn ("read function");
756 			      generate ("cI");
757 			      $$ = EX_REG;
758 			    }
759 			| Random '(' ')'
760 			    {
761 			      ct_warn ("random function");
762 			      generate ("cX");
763 			      $$ = EX_REG;
764 			    }
765 			;
766 named_expression	: NAME
767 			    { $$ = lookup($1,SIMPLE); }
768 			| NAME '[' expression ']'
769 			    {
770 			      if ($3 & EX_VOID)
771 				yyerror("void expression as subscript");
772 			      if ($3 & EX_COMP)
773 				ct_warn("comparison in subscript");
774 			      $$ = lookup($1,ARRAY);
775 			    }
776 			| Ibase
777 			    { $$ = 0; }
778 			| Obase
779 			    { $$ = 1; }
780 			| Scale
781 			    { $$ = 2; }
782 			| HistoryVar
783 			    { $$ = 3;
784 			      ct_warn ("History variable");
785 			    }
786 			| Last
787 			    { $$ = 4;
788 			      ct_warn ("Last variable");
789 			    }
790 			;
791 
792 
793 required_eol		: { ct_warn ("End of line required"); }
794 			| ENDOFLINE
795 			| required_eol ENDOFLINE
796 			  { ct_warn ("Too many end of lines"); }
797 			;
798 
799 %%
800