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