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