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