1 /* $NetBSD: pic.y,v 1.2 2016/01/13 19:01:58 christos Exp $ */ 2 3 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 4 Free Software Foundation, Inc. 5 Written by James Clark (jjc@jclark.com) 6 7 This file is part of groff. 8 9 groff is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free 11 Software Foundation; either version 2, or (at your option) any later 12 version. 13 14 groff is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 for more details. 18 19 You should have received a copy of the GNU General Public License along 20 with groff; see the file COPYING. If not, write to the Free Software 21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22 %{ 23 #include "pic.h" 24 #include "ptable.h" 25 #include "object.h" 26 27 extern int delim_flag; 28 extern void copy_rest_thru(const char *, const char *); 29 extern void copy_file_thru(const char *, const char *, const char *); 30 extern void push_body(const char *); 31 extern void do_for(char *var, double from, double to, 32 int by_is_multiplicative, double by, char *body); 33 extern void do_lookahead(); 34 35 /* Maximum number of characters produced by printf("%g") */ 36 #define GDIGITS 14 37 38 int yylex(); 39 void yyerror(const char *); 40 41 void reset(const char *nm); 42 void reset_all(); 43 44 place *lookup_label(const char *); 45 void define_label(const char *label, const place *pl); 46 47 direction current_direction; 48 position current_position; 49 50 implement_ptable(place) 51 52 PTABLE(place) top_table; 53 54 PTABLE(place) *current_table = &top_table; 55 saved_state *current_saved_state = 0; 56 57 object_list olist; 58 59 const char *ordinal_postfix(int n); 60 const char *object_type_name(object_type type); 61 char *format_number(const char *form, double n); 62 char *do_sprintf(const char *form, const double *v, int nv); 63 64 %} 65 66 67 %union { 68 char *str; 69 int n; 70 double x; 71 struct { double x, y; } pair; 72 struct { double x; char *body; } if_data; 73 struct { char *str; const char *filename; int lineno; } lstr; 74 struct { double *v; int nv; int maxv; } dv; 75 struct { double val; int is_multiplicative; } by; 76 place pl; 77 object *obj; 78 corner crn; 79 path *pth; 80 object_spec *spec; 81 saved_state *pstate; 82 graphics_state state; 83 object_type obtype; 84 } 85 86 %token <str> LABEL 87 %token <str> VARIABLE 88 %token <x> NUMBER 89 %token <lstr> TEXT 90 %token <lstr> COMMAND_LINE 91 %token <str> DELIMITED 92 %token <n> ORDINAL 93 %token TH 94 %token LEFT_ARROW_HEAD 95 %token RIGHT_ARROW_HEAD 96 %token DOUBLE_ARROW_HEAD 97 %token LAST 98 %token UP 99 %token DOWN 100 %token LEFT 101 %token RIGHT 102 %token BOX 103 %token CIRCLE 104 %token ELLIPSE 105 %token ARC 106 %token LINE 107 %token ARROW 108 %token MOVE 109 %token SPLINE 110 %token HEIGHT 111 %token RADIUS 112 %token FIGNAME 113 %token WIDTH 114 %token DIAMETER 115 %token UP 116 %token DOWN 117 %token RIGHT 118 %token LEFT 119 %token FROM 120 %token TO 121 %token AT 122 %token WITH 123 %token BY 124 %token THEN 125 %token SOLID 126 %token DOTTED 127 %token DASHED 128 %token CHOP 129 %token SAME 130 %token INVISIBLE 131 %token LJUST 132 %token RJUST 133 %token ABOVE 134 %token BELOW 135 %token OF 136 %token THE 137 %token WAY 138 %token BETWEEN 139 %token AND 140 %token HERE 141 %token DOT_N 142 %token DOT_E 143 %token DOT_W 144 %token DOT_S 145 %token DOT_NE 146 %token DOT_SE 147 %token DOT_NW 148 %token DOT_SW 149 %token DOT_C 150 %token DOT_START 151 %token DOT_END 152 %token DOT_X 153 %token DOT_Y 154 %token DOT_HT 155 %token DOT_WID 156 %token DOT_RAD 157 %token SIN 158 %token COS 159 %token ATAN2 160 %token LOG 161 %token EXP 162 %token SQRT 163 %token K_MAX 164 %token K_MIN 165 %token INT 166 %token RAND 167 %token SRAND 168 %token COPY 169 %token THRU 170 %token TOP 171 %token BOTTOM 172 %token UPPER 173 %token LOWER 174 %token SH 175 %token PRINT 176 %token CW 177 %token CCW 178 %token FOR 179 %token DO 180 %token IF 181 %token ELSE 182 %token ANDAND 183 %token OROR 184 %token NOTEQUAL 185 %token EQUALEQUAL 186 %token LESSEQUAL 187 %token GREATEREQUAL 188 %token LEFT_CORNER 189 %token RIGHT_CORNER 190 %token NORTH 191 %token SOUTH 192 %token EAST 193 %token WEST 194 %token CENTER 195 %token END 196 %token START 197 %token RESET 198 %token UNTIL 199 %token PLOT 200 %token THICKNESS 201 %token FILL 202 %token COLORED 203 %token OUTLINED 204 %token SHADED 205 %token ALIGNED 206 %token SPRINTF 207 %token COMMAND 208 209 %token DEFINE 210 %token UNDEF 211 212 %left '.' 213 214 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */ 215 %left PLOT 216 %left TEXT SPRINTF 217 218 /* give text adjustments higher precedence than TEXT, so that 219 box "foo" above ljust == box ("foo" above ljust) 220 */ 221 222 %left LJUST RJUST ABOVE BELOW 223 224 %left LEFT RIGHT 225 /* Give attributes that take an optional expression a higher 226 precedence than left and right, so that eg `line chop left' 227 parses properly. */ 228 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED 229 %left LABEL 230 231 %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST 232 %left ORDINAL HERE '`' 233 234 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '[' 235 236 /* these need to be lower than '-' */ 237 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS 238 239 /* these must have higher precedence than CHOP so that `label %prec CHOP' 240 works */ 241 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C 242 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER 243 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END 244 245 %left ',' 246 %left OROR 247 %left ANDAND 248 %left EQUALEQUAL NOTEQUAL 249 %left '<' '>' LESSEQUAL GREATEREQUAL 250 251 %left BETWEEN OF 252 %left AND 253 254 %left '+' '-' 255 %left '*' '/' '%' 256 %right '!' 257 %right '^' 258 259 %type <x> expr any_expr text_expr 260 %type <by> optional_by 261 %type <pair> expr_pair position_not_place 262 %type <if_data> simple_if 263 %type <obj> nth_primitive 264 %type <crn> corner 265 %type <pth> path label_path relative_path 266 %type <pl> place label element element_list middle_element_list 267 %type <spec> object_spec 268 %type <pair> position 269 %type <obtype> object_type 270 %type <n> optional_ordinal_last ordinal 271 %type <str> macro_name until 272 %type <dv> sprintf_args 273 %type <lstr> text print_args print_arg 274 275 %% 276 277 top: 278 optional_separator 279 | element_list 280 { 281 if (olist.head) 282 print_picture(olist.head); 283 } 284 ; 285 286 287 element_list: 288 optional_separator middle_element_list optional_separator 289 { $$ = $2; } 290 ; 291 292 middle_element_list: 293 element 294 { $$ = $1; } 295 | middle_element_list separator element 296 { $$ = $1; } 297 ; 298 299 optional_separator: 300 /* empty */ 301 | separator 302 ; 303 304 separator: 305 ';' 306 | separator ';' 307 ; 308 309 placeless_element: 310 FIGNAME '=' macro_name 311 { 312 a_delete graphname; 313 graphname = new char[strlen($3) + 1]; 314 strcpy(graphname, $3); 315 a_delete $3; 316 } 317 | 318 VARIABLE '=' any_expr 319 { 320 define_variable($1, $3); 321 a_delete $1; 322 } 323 | VARIABLE ':' '=' any_expr 324 { 325 place *p = lookup_label($1); 326 if (!p) { 327 lex_error("variable `%1' not defined", $1); 328 YYABORT; 329 } 330 p->obj = 0; 331 p->x = $4; 332 p->y = 0.0; 333 a_delete $1; 334 } 335 | UP 336 { current_direction = UP_DIRECTION; } 337 | DOWN 338 { current_direction = DOWN_DIRECTION; } 339 | LEFT 340 { current_direction = LEFT_DIRECTION; } 341 | RIGHT 342 { current_direction = RIGHT_DIRECTION; } 343 | COMMAND_LINE 344 { 345 olist.append(make_command_object($1.str, $1.filename, 346 $1.lineno)); 347 } 348 | COMMAND print_args 349 { 350 olist.append(make_command_object($2.str, $2.filename, 351 $2.lineno)); 352 } 353 | PRINT print_args 354 { 355 fprintf(stderr, "%s\n", $2.str); 356 a_delete $2.str; 357 fflush(stderr); 358 } 359 | SH 360 { delim_flag = 1; } 361 DELIMITED 362 { 363 delim_flag = 0; 364 if (safer_flag) 365 lex_error("unsafe to run command `%1'", $3); 366 else 367 system($3); 368 a_delete $3; 369 } 370 | COPY TEXT 371 { 372 if (yychar < 0) 373 do_lookahead(); 374 do_copy($2.str); 375 // do not delete the filename 376 } 377 | COPY TEXT THRU 378 { delim_flag = 2; } 379 DELIMITED 380 { delim_flag = 0; } 381 until 382 { 383 if (yychar < 0) 384 do_lookahead(); 385 copy_file_thru($2.str, $5, $7); 386 // do not delete the filename 387 a_delete $5; 388 a_delete $7; 389 } 390 | COPY THRU 391 { delim_flag = 2; } 392 DELIMITED 393 { delim_flag = 0; } 394 until 395 { 396 if (yychar < 0) 397 do_lookahead(); 398 copy_rest_thru($4, $6); 399 a_delete $4; 400 a_delete $6; 401 } 402 | FOR VARIABLE '=' expr TO expr optional_by DO 403 { delim_flag = 1; } 404 DELIMITED 405 { 406 delim_flag = 0; 407 if (yychar < 0) 408 do_lookahead(); 409 do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10); 410 } 411 | simple_if 412 { 413 if (yychar < 0) 414 do_lookahead(); 415 if ($1.x != 0.0) 416 push_body($1.body); 417 a_delete $1.body; 418 } 419 | simple_if ELSE 420 { delim_flag = 1; } 421 DELIMITED 422 { 423 delim_flag = 0; 424 if (yychar < 0) 425 do_lookahead(); 426 if ($1.x != 0.0) 427 push_body($1.body); 428 else 429 push_body($4); 430 a_delete $1.body; 431 a_delete $4; 432 } 433 | reset_variables 434 | RESET 435 { define_variable("scale", 1.0); } 436 ; 437 438 macro_name: 439 VARIABLE 440 | LABEL 441 ; 442 443 reset_variables: 444 RESET VARIABLE 445 { 446 reset($2); 447 a_delete $2; 448 } 449 | reset_variables VARIABLE 450 { 451 reset($2); 452 a_delete $2; 453 } 454 | reset_variables ',' VARIABLE 455 { 456 reset($3); 457 a_delete $3; 458 } 459 ; 460 461 print_args: 462 print_arg 463 { $$ = $1; } 464 | print_args print_arg 465 { 466 $$.str = new char[strlen($1.str) + strlen($2.str) + 1]; 467 strcpy($$.str, $1.str); 468 strcat($$.str, $2.str); 469 a_delete $1.str; 470 a_delete $2.str; 471 if ($1.filename) { 472 $$.filename = $1.filename; 473 $$.lineno = $1.lineno; 474 } 475 else if ($2.filename) { 476 $$.filename = $2.filename; 477 $$.lineno = $2.lineno; 478 } 479 } 480 ; 481 482 print_arg: 483 expr %prec ',' 484 { 485 $$.str = new char[GDIGITS + 1]; 486 sprintf($$.str, "%g", $1); 487 $$.filename = 0; 488 $$.lineno = 0; 489 } 490 | text 491 { $$ = $1; } 492 | position %prec ',' 493 { 494 $$.str = new char[GDIGITS + 2 + GDIGITS + 1]; 495 sprintf($$.str, "%g, %g", $1.x, $1.y); 496 $$.filename = 0; 497 $$.lineno = 0; 498 } 499 ; 500 501 simple_if: 502 IF any_expr THEN 503 { delim_flag = 1; } 504 DELIMITED 505 { 506 delim_flag = 0; 507 $$.x = $2; 508 $$.body = $5; 509 } 510 ; 511 512 until: 513 /* empty */ 514 { $$ = 0; } 515 | UNTIL TEXT 516 { $$ = $2.str; } 517 ; 518 519 any_expr: 520 expr 521 { $$ = $1; } 522 | text_expr 523 { $$ = $1; } 524 ; 525 526 text_expr: 527 text EQUALEQUAL text 528 { 529 $$ = strcmp($1.str, $3.str) == 0; 530 a_delete $1.str; 531 a_delete $3.str; 532 } 533 | text NOTEQUAL text 534 { 535 $$ = strcmp($1.str, $3.str) != 0; 536 a_delete $1.str; 537 a_delete $3.str; 538 } 539 | text_expr ANDAND text_expr 540 { $$ = ($1 != 0.0 && $3 != 0.0); } 541 | text_expr ANDAND expr 542 { $$ = ($1 != 0.0 && $3 != 0.0); } 543 | expr ANDAND text_expr 544 { $$ = ($1 != 0.0 && $3 != 0.0); } 545 | text_expr OROR text_expr 546 { $$ = ($1 != 0.0 || $3 != 0.0); } 547 | text_expr OROR expr 548 { $$ = ($1 != 0.0 || $3 != 0.0); } 549 | expr OROR text_expr 550 { $$ = ($1 != 0.0 || $3 != 0.0); } 551 | '!' text_expr 552 { $$ = ($2 == 0.0); } 553 ; 554 555 556 optional_by: 557 /* empty */ 558 { 559 $$.val = 1.0; 560 $$.is_multiplicative = 0; 561 } 562 | BY expr 563 { 564 $$.val = $2; 565 $$.is_multiplicative = 0; 566 } 567 | BY '*' expr 568 { 569 $$.val = $3; 570 $$.is_multiplicative = 1; 571 } 572 ; 573 574 element: 575 object_spec 576 { 577 $$.obj = $1->make_object(¤t_position, 578 ¤t_direction); 579 if ($$.obj == 0) 580 YYABORT; 581 delete $1; 582 if ($$.obj) 583 olist.append($$.obj); 584 else { 585 $$.x = current_position.x; 586 $$.y = current_position.y; 587 } 588 } 589 | LABEL ':' optional_separator element 590 { 591 $$ = $4; 592 define_label($1, & $$); 593 a_delete $1; 594 } 595 | LABEL ':' optional_separator position_not_place 596 { 597 $$.obj = 0; 598 $$.x = $4.x; 599 $$.y = $4.y; 600 define_label($1, & $$); 601 a_delete $1; 602 } 603 | LABEL ':' optional_separator place 604 { 605 $$ = $4; 606 define_label($1, & $$); 607 a_delete $1; 608 } 609 | '{' 610 { 611 $<state>$.x = current_position.x; 612 $<state>$.y = current_position.y; 613 $<state>$.dir = current_direction; 614 } 615 element_list '}' 616 { 617 current_position.x = $<state>2.x; 618 current_position.y = $<state>2.y; 619 current_direction = $<state>2.dir; 620 } 621 optional_element 622 { 623 $$ = $3; 624 } 625 | placeless_element 626 { 627 $$.obj = 0; 628 $$.x = current_position.x; 629 $$.y = current_position.y; 630 } 631 ; 632 633 optional_element: 634 /* empty */ 635 {} 636 | element 637 {} 638 ; 639 640 object_spec: 641 BOX 642 { $$ = new object_spec(BOX_OBJECT); } 643 | CIRCLE 644 { $$ = new object_spec(CIRCLE_OBJECT); } 645 | ELLIPSE 646 { $$ = new object_spec(ELLIPSE_OBJECT); } 647 | ARC 648 { 649 $$ = new object_spec(ARC_OBJECT); 650 $$->dir = current_direction; 651 } 652 | LINE 653 { 654 $$ = new object_spec(LINE_OBJECT); 655 lookup_variable("lineht", & $$->segment_height); 656 lookup_variable("linewid", & $$->segment_width); 657 $$->dir = current_direction; 658 } 659 | ARROW 660 { 661 $$ = new object_spec(ARROW_OBJECT); 662 lookup_variable("lineht", & $$->segment_height); 663 lookup_variable("linewid", & $$->segment_width); 664 $$->dir = current_direction; 665 } 666 | MOVE 667 { 668 $$ = new object_spec(MOVE_OBJECT); 669 lookup_variable("moveht", & $$->segment_height); 670 lookup_variable("movewid", & $$->segment_width); 671 $$->dir = current_direction; 672 } 673 | SPLINE 674 { 675 $$ = new object_spec(SPLINE_OBJECT); 676 lookup_variable("lineht", & $$->segment_height); 677 lookup_variable("linewid", & $$->segment_width); 678 $$->dir = current_direction; 679 } 680 | text %prec TEXT 681 { 682 $$ = new object_spec(TEXT_OBJECT); 683 $$->text = new text_item($1.str, $1.filename, $1.lineno); 684 } 685 | PLOT expr 686 { 687 $$ = new object_spec(TEXT_OBJECT); 688 $$->text = new text_item(format_number(0, $2), 0, -1); 689 } 690 | PLOT expr text 691 { 692 $$ = new object_spec(TEXT_OBJECT); 693 $$->text = new text_item(format_number($3.str, $2), 694 $3.filename, $3.lineno); 695 a_delete $3.str; 696 } 697 | '[' 698 { 699 saved_state *p = new saved_state; 700 $<pstate>$ = p; 701 p->x = current_position.x; 702 p->y = current_position.y; 703 p->dir = current_direction; 704 p->tbl = current_table; 705 p->prev = current_saved_state; 706 current_position.x = 0.0; 707 current_position.y = 0.0; 708 current_table = new PTABLE(place); 709 current_saved_state = p; 710 olist.append(make_mark_object()); 711 } 712 element_list ']' 713 { 714 current_position.x = $<pstate>2->x; 715 current_position.y = $<pstate>2->y; 716 current_direction = $<pstate>2->dir; 717 $$ = new object_spec(BLOCK_OBJECT); 718 olist.wrap_up_block(& $$->oblist); 719 $$->tbl = current_table; 720 current_table = $<pstate>2->tbl; 721 current_saved_state = $<pstate>2->prev; 722 delete $<pstate>2; 723 } 724 | object_spec HEIGHT expr 725 { 726 $$ = $1; 727 $$->height = $3; 728 $$->flags |= HAS_HEIGHT; 729 } 730 | object_spec RADIUS expr 731 { 732 $$ = $1; 733 $$->radius = $3; 734 $$->flags |= HAS_RADIUS; 735 } 736 | object_spec WIDTH expr 737 { 738 $$ = $1; 739 $$->width = $3; 740 $$->flags |= HAS_WIDTH; 741 } 742 | object_spec DIAMETER expr 743 { 744 $$ = $1; 745 $$->radius = $3/2.0; 746 $$->flags |= HAS_RADIUS; 747 } 748 | object_spec expr %prec HEIGHT 749 { 750 $$ = $1; 751 $$->flags |= HAS_SEGMENT; 752 switch ($$->dir) { 753 case UP_DIRECTION: 754 $$->segment_pos.y += $2; 755 break; 756 case DOWN_DIRECTION: 757 $$->segment_pos.y -= $2; 758 break; 759 case RIGHT_DIRECTION: 760 $$->segment_pos.x += $2; 761 break; 762 case LEFT_DIRECTION: 763 $$->segment_pos.x -= $2; 764 break; 765 } 766 } 767 | object_spec UP 768 { 769 $$ = $1; 770 $$->dir = UP_DIRECTION; 771 $$->flags |= HAS_SEGMENT; 772 $$->segment_pos.y += $$->segment_height; 773 } 774 | object_spec UP expr 775 { 776 $$ = $1; 777 $$->dir = UP_DIRECTION; 778 $$->flags |= HAS_SEGMENT; 779 $$->segment_pos.y += $3; 780 } 781 | object_spec DOWN 782 { 783 $$ = $1; 784 $$->dir = DOWN_DIRECTION; 785 $$->flags |= HAS_SEGMENT; 786 $$->segment_pos.y -= $$->segment_height; 787 } 788 | object_spec DOWN expr 789 { 790 $$ = $1; 791 $$->dir = DOWN_DIRECTION; 792 $$->flags |= HAS_SEGMENT; 793 $$->segment_pos.y -= $3; 794 } 795 | object_spec RIGHT 796 { 797 $$ = $1; 798 $$->dir = RIGHT_DIRECTION; 799 $$->flags |= HAS_SEGMENT; 800 $$->segment_pos.x += $$->segment_width; 801 } 802 | object_spec RIGHT expr 803 { 804 $$ = $1; 805 $$->dir = RIGHT_DIRECTION; 806 $$->flags |= HAS_SEGMENT; 807 $$->segment_pos.x += $3; 808 } 809 | object_spec LEFT 810 { 811 $$ = $1; 812 $$->dir = LEFT_DIRECTION; 813 $$->flags |= HAS_SEGMENT; 814 $$->segment_pos.x -= $$->segment_width; 815 } 816 | object_spec LEFT expr 817 { 818 $$ = $1; 819 $$->dir = LEFT_DIRECTION; 820 $$->flags |= HAS_SEGMENT; 821 $$->segment_pos.x -= $3; 822 } 823 | object_spec FROM position 824 { 825 $$ = $1; 826 $$->flags |= HAS_FROM; 827 $$->from.x = $3.x; 828 $$->from.y = $3.y; 829 } 830 | object_spec TO position 831 { 832 $$ = $1; 833 if ($$->flags & HAS_SEGMENT) 834 $$->segment_list = new segment($$->segment_pos, 835 $$->segment_is_absolute, 836 $$->segment_list); 837 $$->flags |= HAS_SEGMENT; 838 $$->segment_pos.x = $3.x; 839 $$->segment_pos.y = $3.y; 840 $$->segment_is_absolute = 1; 841 $$->flags |= HAS_TO; 842 $$->to.x = $3.x; 843 $$->to.y = $3.y; 844 } 845 | object_spec AT position 846 { 847 $$ = $1; 848 $$->flags |= HAS_AT; 849 $$->at.x = $3.x; 850 $$->at.y = $3.y; 851 if ($$->type != ARC_OBJECT) { 852 $$->flags |= HAS_FROM; 853 $$->from.x = $3.x; 854 $$->from.y = $3.y; 855 } 856 } 857 | object_spec WITH path 858 { 859 $$ = $1; 860 $$->flags |= HAS_WITH; 861 $$->with = $3; 862 } 863 | object_spec WITH position %prec ',' 864 { 865 $$ = $1; 866 $$->flags |= HAS_WITH; 867 position pos; 868 pos.x = $3.x; 869 pos.y = $3.y; 870 $$->with = new path(pos); 871 } 872 | object_spec BY expr_pair 873 { 874 $$ = $1; 875 $$->flags |= HAS_SEGMENT; 876 $$->segment_pos.x += $3.x; 877 $$->segment_pos.y += $3.y; 878 } 879 | object_spec THEN 880 { 881 $$ = $1; 882 if ($$->flags & HAS_SEGMENT) { 883 $$->segment_list = new segment($$->segment_pos, 884 $$->segment_is_absolute, 885 $$->segment_list); 886 $$->flags &= ~HAS_SEGMENT; 887 $$->segment_pos.x = $$->segment_pos.y = 0.0; 888 $$->segment_is_absolute = 0; 889 } 890 } 891 | object_spec SOLID 892 { 893 $$ = $1; // nothing 894 } 895 | object_spec DOTTED 896 { 897 $$ = $1; 898 $$->flags |= IS_DOTTED; 899 lookup_variable("dashwid", & $$->dash_width); 900 } 901 | object_spec DOTTED expr 902 { 903 $$ = $1; 904 $$->flags |= IS_DOTTED; 905 $$->dash_width = $3; 906 } 907 | object_spec DASHED 908 { 909 $$ = $1; 910 $$->flags |= IS_DASHED; 911 lookup_variable("dashwid", & $$->dash_width); 912 } 913 | object_spec DASHED expr 914 { 915 $$ = $1; 916 $$->flags |= IS_DASHED; 917 $$->dash_width = $3; 918 } 919 | object_spec FILL 920 { 921 $$ = $1; 922 $$->flags |= IS_DEFAULT_FILLED; 923 } 924 | object_spec FILL expr 925 { 926 $$ = $1; 927 $$->flags |= IS_FILLED; 928 $$->fill = $3; 929 } 930 | object_spec SHADED text 931 { 932 $$ = $1; 933 $$->flags |= (IS_SHADED | IS_FILLED); 934 $$->shaded = new char[strlen($3.str)+1]; 935 strcpy($$->shaded, $3.str); 936 } 937 | object_spec COLORED text 938 { 939 $$ = $1; 940 $$->flags |= (IS_SHADED | IS_OUTLINED | IS_FILLED); 941 $$->shaded = new char[strlen($3.str)+1]; 942 strcpy($$->shaded, $3.str); 943 $$->outlined = new char[strlen($3.str)+1]; 944 strcpy($$->outlined, $3.str); 945 } 946 | object_spec OUTLINED text 947 { 948 $$ = $1; 949 $$->flags |= IS_OUTLINED; 950 $$->outlined = new char[strlen($3.str)+1]; 951 strcpy($$->outlined, $3.str); 952 } 953 | object_spec CHOP 954 { 955 $$ = $1; 956 // line chop chop means line chop 0 chop 0 957 if ($$->flags & IS_DEFAULT_CHOPPED) { 958 $$->flags |= IS_CHOPPED; 959 $$->flags &= ~IS_DEFAULT_CHOPPED; 960 $$->start_chop = $$->end_chop = 0.0; 961 } 962 else if ($$->flags & IS_CHOPPED) { 963 $$->end_chop = 0.0; 964 } 965 else { 966 $$->flags |= IS_DEFAULT_CHOPPED; 967 } 968 } 969 | object_spec CHOP expr 970 { 971 $$ = $1; 972 if ($$->flags & IS_DEFAULT_CHOPPED) { 973 $$->flags |= IS_CHOPPED; 974 $$->flags &= ~IS_DEFAULT_CHOPPED; 975 $$->start_chop = 0.0; 976 $$->end_chop = $3; 977 } 978 else if ($$->flags & IS_CHOPPED) { 979 $$->end_chop = $3; 980 } 981 else { 982 $$->start_chop = $$->end_chop = $3; 983 $$->flags |= IS_CHOPPED; 984 } 985 } 986 | object_spec SAME 987 { 988 $$ = $1; 989 $$->flags |= IS_SAME; 990 } 991 | object_spec INVISIBLE 992 { 993 $$ = $1; 994 $$->flags |= IS_INVISIBLE; 995 } 996 | object_spec LEFT_ARROW_HEAD 997 { 998 $$ = $1; 999 $$->flags |= HAS_LEFT_ARROW_HEAD; 1000 } 1001 | object_spec RIGHT_ARROW_HEAD 1002 { 1003 $$ = $1; 1004 $$->flags |= HAS_RIGHT_ARROW_HEAD; 1005 } 1006 | object_spec DOUBLE_ARROW_HEAD 1007 { 1008 $$ = $1; 1009 $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD); 1010 } 1011 | object_spec CW 1012 { 1013 $$ = $1; 1014 $$->flags |= IS_CLOCKWISE; 1015 } 1016 | object_spec CCW 1017 { 1018 $$ = $1; 1019 $$->flags &= ~IS_CLOCKWISE; 1020 } 1021 | object_spec text %prec TEXT 1022 { 1023 $$ = $1; 1024 text_item **p; 1025 for (p = & $$->text; *p; p = &(*p)->next) 1026 ; 1027 *p = new text_item($2.str, $2.filename, $2.lineno); 1028 } 1029 | object_spec LJUST 1030 { 1031 $$ = $1; 1032 if ($$->text) { 1033 text_item *p; 1034 for (p = $$->text; p->next; p = p->next) 1035 ; 1036 p->adj.h = LEFT_ADJUST; 1037 } 1038 } 1039 | object_spec RJUST 1040 { 1041 $$ = $1; 1042 if ($$->text) { 1043 text_item *p; 1044 for (p = $$->text; p->next; p = p->next) 1045 ; 1046 p->adj.h = RIGHT_ADJUST; 1047 } 1048 } 1049 | object_spec ABOVE 1050 { 1051 $$ = $1; 1052 if ($$->text) { 1053 text_item *p; 1054 for (p = $$->text; p->next; p = p->next) 1055 ; 1056 p->adj.v = ABOVE_ADJUST; 1057 } 1058 } 1059 | object_spec BELOW 1060 { 1061 $$ = $1; 1062 if ($$->text) { 1063 text_item *p; 1064 for (p = $$->text; p->next; p = p->next) 1065 ; 1066 p->adj.v = BELOW_ADJUST; 1067 } 1068 } 1069 | object_spec THICKNESS expr 1070 { 1071 $$ = $1; 1072 $$->flags |= HAS_THICKNESS; 1073 $$->thickness = $3; 1074 } 1075 | object_spec ALIGNED 1076 { 1077 $$ = $1; 1078 $$->flags |= IS_ALIGNED; 1079 } 1080 ; 1081 1082 text: 1083 TEXT 1084 { $$ = $1; } 1085 | SPRINTF '(' TEXT sprintf_args ')' 1086 { 1087 $$.filename = $3.filename; 1088 $$.lineno = $3.lineno; 1089 $$.str = do_sprintf($3.str, $4.v, $4.nv); 1090 a_delete $4.v; 1091 a_delete $3.str; 1092 } 1093 ; 1094 1095 sprintf_args: 1096 /* empty */ 1097 { 1098 $$.v = 0; 1099 $$.nv = 0; 1100 $$.maxv = 0; 1101 } 1102 | sprintf_args ',' expr 1103 { 1104 $$ = $1; 1105 if ($$.nv >= $$.maxv) { 1106 if ($$.nv == 0) { 1107 $$.v = new double[4]; 1108 $$.maxv = 4; 1109 } 1110 else { 1111 double *oldv = $$.v; 1112 $$.maxv *= 2; 1113 #if 0 1114 $$.v = new double[$$.maxv]; 1115 memcpy($$.v, oldv, $$.nv*sizeof(double)); 1116 #else 1117 // workaround for bug in Compaq C++ V6.5-033 1118 // for Compaq Tru64 UNIX V5.1A (Rev. 1885) 1119 double *foo = new double[$$.maxv]; 1120 memcpy(foo, oldv, $$.nv*sizeof(double)); 1121 $$.v = foo; 1122 #endif 1123 a_delete oldv; 1124 } 1125 } 1126 $$.v[$$.nv] = $3; 1127 $$.nv += 1; 1128 } 1129 ; 1130 1131 position: 1132 position_not_place 1133 { $$ = $1; } 1134 | place 1135 { 1136 position pos = $1; 1137 $$.x = pos.x; 1138 $$.y = pos.y; 1139 } 1140 | '(' place ')' 1141 { 1142 position pos = $2; 1143 $$.x = pos.x; 1144 $$.y = pos.y; 1145 } 1146 ; 1147 1148 position_not_place: 1149 expr_pair 1150 { $$ = $1; } 1151 | position '+' expr_pair 1152 { 1153 $$.x = $1.x + $3.x; 1154 $$.y = $1.y + $3.y; 1155 } 1156 | '(' position '+' expr_pair ')' 1157 { 1158 $$.x = $2.x + $4.x; 1159 $$.y = $2.y + $4.y; 1160 } 1161 | position '-' expr_pair 1162 { 1163 $$.x = $1.x - $3.x; 1164 $$.y = $1.y - $3.y; 1165 } 1166 | '(' position '-' expr_pair ')' 1167 { 1168 $$.x = $2.x - $4.x; 1169 $$.y = $2.y - $4.y; 1170 } 1171 | '(' position ',' position ')' 1172 { 1173 $$.x = $2.x; 1174 $$.y = $4.y; 1175 } 1176 | expr between position AND position 1177 { 1178 $$.x = (1.0 - $1)*$3.x + $1*$5.x; 1179 $$.y = (1.0 - $1)*$3.y + $1*$5.y; 1180 } 1181 | '(' expr between position AND position ')' 1182 { 1183 $$.x = (1.0 - $2)*$4.x + $2*$6.x; 1184 $$.y = (1.0 - $2)*$4.y + $2*$6.y; 1185 } 1186 | expr '<' position ',' position '>' 1187 { 1188 $$.x = (1.0 - $1)*$3.x + $1*$5.x; 1189 $$.y = (1.0 - $1)*$3.y + $1*$5.y; 1190 } 1191 | '(' expr '<' position ',' position '>' ')' 1192 { 1193 $$.x = (1.0 - $2)*$4.x + $2*$6.x; 1194 $$.y = (1.0 - $2)*$4.y + $2*$6.y; 1195 } 1196 ; 1197 1198 between: 1199 BETWEEN 1200 | OF THE WAY BETWEEN 1201 ; 1202 1203 expr_pair: 1204 expr ',' expr 1205 { 1206 $$.x = $1; 1207 $$.y = $3; 1208 } 1209 | '(' expr_pair ')' 1210 { $$ = $2; } 1211 ; 1212 1213 place: 1214 /* line at A left == line (at A) left */ 1215 label %prec CHOP 1216 { $$ = $1; } 1217 | label corner 1218 { 1219 path pth($2); 1220 if (!pth.follow($1, & $$)) 1221 YYABORT; 1222 } 1223 | corner label 1224 { 1225 path pth($1); 1226 if (!pth.follow($2, & $$)) 1227 YYABORT; 1228 } 1229 | corner OF label 1230 { 1231 path pth($1); 1232 if (!pth.follow($3, & $$)) 1233 YYABORT; 1234 } 1235 | HERE 1236 { 1237 $$.x = current_position.x; 1238 $$.y = current_position.y; 1239 $$.obj = 0; 1240 } 1241 ; 1242 1243 label: 1244 LABEL 1245 { 1246 place *p = lookup_label($1); 1247 if (!p) { 1248 lex_error("there is no place `%1'", $1); 1249 YYABORT; 1250 } 1251 $$ = *p; 1252 a_delete $1; 1253 } 1254 | nth_primitive 1255 { $$.obj = $1; } 1256 | label '.' LABEL 1257 { 1258 path pth($3); 1259 if (!pth.follow($1, & $$)) 1260 YYABORT; 1261 } 1262 ; 1263 1264 ordinal: 1265 ORDINAL 1266 { $$ = $1; } 1267 | '`' any_expr TH 1268 { 1269 // XXX Check for overflow (and non-integers?). 1270 $$ = (int)$2; 1271 } 1272 ; 1273 1274 optional_ordinal_last: 1275 LAST 1276 { $$ = 1; } 1277 | ordinal LAST 1278 { $$ = $1; } 1279 ; 1280 1281 nth_primitive: 1282 ordinal object_type 1283 { 1284 int count = 0; 1285 object *p; 1286 for (p = olist.head; p != 0; p = p->next) 1287 if (p->type() == $2 && ++count == $1) { 1288 $$ = p; 1289 break; 1290 } 1291 if (p == 0) { 1292 lex_error("there is no %1%2 %3", $1, ordinal_postfix($1), 1293 object_type_name($2)); 1294 YYABORT; 1295 } 1296 } 1297 | optional_ordinal_last object_type 1298 { 1299 int count = 0; 1300 object *p; 1301 for (p = olist.tail; p != 0; p = p->prev) 1302 if (p->type() == $2 && ++count == $1) { 1303 $$ = p; 1304 break; 1305 } 1306 if (p == 0) { 1307 lex_error("there is no %1%2 last %3", $1, 1308 ordinal_postfix($1), object_type_name($2)); 1309 YYABORT; 1310 } 1311 } 1312 ; 1313 1314 object_type: 1315 BOX 1316 { $$ = BOX_OBJECT; } 1317 | CIRCLE 1318 { $$ = CIRCLE_OBJECT; } 1319 | ELLIPSE 1320 { $$ = ELLIPSE_OBJECT; } 1321 | ARC 1322 { $$ = ARC_OBJECT; } 1323 | LINE 1324 { $$ = LINE_OBJECT; } 1325 | ARROW 1326 { $$ = ARROW_OBJECT; } 1327 | SPLINE 1328 { $$ = SPLINE_OBJECT; } 1329 | '[' ']' 1330 { $$ = BLOCK_OBJECT; } 1331 | TEXT 1332 { $$ = TEXT_OBJECT; } 1333 ; 1334 1335 label_path: 1336 '.' LABEL 1337 { $$ = new path($2); } 1338 | label_path '.' LABEL 1339 { 1340 $$ = $1; 1341 $$->append($3); 1342 } 1343 ; 1344 1345 relative_path: 1346 corner %prec CHOP 1347 { $$ = new path($1); } 1348 /* give this a lower precedence than LEFT and RIGHT so that 1349 [A: box] with .A left == [A: box] with (.A left) */ 1350 | label_path %prec TEXT 1351 { $$ = $1; } 1352 | label_path corner 1353 { 1354 $$ = $1; 1355 $$->append($2); 1356 } 1357 ; 1358 1359 path: 1360 relative_path 1361 { $$ = $1; } 1362 | '(' relative_path ',' relative_path ')' 1363 { 1364 $$ = $2; 1365 $$->set_ypath($4); 1366 } 1367 /* The rest of these rules are a compatibility sop. */ 1368 | ORDINAL LAST object_type relative_path 1369 { 1370 lex_warning("`%1%2 last %3' in `with' argument ignored", 1371 $1, ordinal_postfix($1), object_type_name($3)); 1372 $$ = $4; 1373 } 1374 | LAST object_type relative_path 1375 { 1376 lex_warning("`last %1' in `with' argument ignored", 1377 object_type_name($2)); 1378 $$ = $3; 1379 } 1380 | ORDINAL object_type relative_path 1381 { 1382 lex_warning("`%1%2 %3' in `with' argument ignored", 1383 $1, ordinal_postfix($1), object_type_name($2)); 1384 $$ = $3; 1385 } 1386 | LABEL relative_path 1387 { 1388 lex_warning("initial `%1' in `with' argument ignored", $1); 1389 a_delete $1; 1390 $$ = $2; 1391 } 1392 ; 1393 1394 corner: 1395 DOT_N 1396 { $$ = &object::north; } 1397 | DOT_E 1398 { $$ = &object::east; } 1399 | DOT_W 1400 { $$ = &object::west; } 1401 | DOT_S 1402 { $$ = &object::south; } 1403 | DOT_NE 1404 { $$ = &object::north_east; } 1405 | DOT_SE 1406 { $$ = &object:: south_east; } 1407 | DOT_NW 1408 { $$ = &object::north_west; } 1409 | DOT_SW 1410 { $$ = &object::south_west; } 1411 | DOT_C 1412 { $$ = &object::center; } 1413 | DOT_START 1414 { $$ = &object::start; } 1415 | DOT_END 1416 { $$ = &object::end; } 1417 | TOP 1418 { $$ = &object::north; } 1419 | BOTTOM 1420 { $$ = &object::south; } 1421 | LEFT 1422 { $$ = &object::west; } 1423 | RIGHT 1424 { $$ = &object::east; } 1425 | UPPER LEFT 1426 { $$ = &object::north_west; } 1427 | LOWER LEFT 1428 { $$ = &object::south_west; } 1429 | UPPER RIGHT 1430 { $$ = &object::north_east; } 1431 | LOWER RIGHT 1432 { $$ = &object::south_east; } 1433 | LEFT_CORNER 1434 { $$ = &object::west; } 1435 | RIGHT_CORNER 1436 { $$ = &object::east; } 1437 | UPPER LEFT_CORNER 1438 { $$ = &object::north_west; } 1439 | LOWER LEFT_CORNER 1440 { $$ = &object::south_west; } 1441 | UPPER RIGHT_CORNER 1442 { $$ = &object::north_east; } 1443 | LOWER RIGHT_CORNER 1444 { $$ = &object::south_east; } 1445 | NORTH 1446 { $$ = &object::north; } 1447 | SOUTH 1448 { $$ = &object::south; } 1449 | EAST 1450 { $$ = &object::east; } 1451 | WEST 1452 { $$ = &object::west; } 1453 | CENTER 1454 { $$ = &object::center; } 1455 | START 1456 { $$ = &object::start; } 1457 | END 1458 { $$ = &object::end; } 1459 ; 1460 1461 expr: 1462 VARIABLE 1463 { 1464 if (!lookup_variable($1, & $$)) { 1465 lex_error("there is no variable `%1'", $1); 1466 YYABORT; 1467 } 1468 a_delete $1; 1469 } 1470 | NUMBER 1471 { $$ = $1; } 1472 | place DOT_X 1473 { 1474 if ($1.obj != 0) 1475 $$ = $1.obj->origin().x; 1476 else 1477 $$ = $1.x; 1478 } 1479 | place DOT_Y 1480 { 1481 if ($1.obj != 0) 1482 $$ = $1.obj->origin().y; 1483 else 1484 $$ = $1.y; 1485 } 1486 | place DOT_HT 1487 { 1488 if ($1.obj != 0) 1489 $$ = $1.obj->height(); 1490 else 1491 $$ = 0.0; 1492 } 1493 | place DOT_WID 1494 { 1495 if ($1.obj != 0) 1496 $$ = $1.obj->width(); 1497 else 1498 $$ = 0.0; 1499 } 1500 | place DOT_RAD 1501 { 1502 if ($1.obj != 0) 1503 $$ = $1.obj->radius(); 1504 else 1505 $$ = 0.0; 1506 } 1507 | expr '+' expr 1508 { $$ = $1 + $3; } 1509 | expr '-' expr 1510 { $$ = $1 - $3; } 1511 | expr '*' expr 1512 { $$ = $1 * $3; } 1513 | expr '/' expr 1514 { 1515 if ($3 == 0.0) { 1516 lex_error("division by zero"); 1517 YYABORT; 1518 } 1519 $$ = $1/$3; 1520 } 1521 | expr '%' expr 1522 { 1523 if ($3 == 0.0) { 1524 lex_error("modulus by zero"); 1525 YYABORT; 1526 } 1527 $$ = fmod($1, $3); 1528 } 1529 | expr '^' expr 1530 { 1531 errno = 0; 1532 $$ = pow($1, $3); 1533 if (errno == EDOM) { 1534 lex_error("arguments to `^' operator out of domain"); 1535 YYABORT; 1536 } 1537 if (errno == ERANGE) { 1538 lex_error("result of `^' operator out of range"); 1539 YYABORT; 1540 } 1541 } 1542 | '-' expr %prec '!' 1543 { $$ = -$2; } 1544 | '(' any_expr ')' 1545 { $$ = $2; } 1546 | SIN '(' any_expr ')' 1547 { 1548 errno = 0; 1549 $$ = sin($3); 1550 if (errno == ERANGE) { 1551 lex_error("sin result out of range"); 1552 YYABORT; 1553 } 1554 } 1555 | COS '(' any_expr ')' 1556 { 1557 errno = 0; 1558 $$ = cos($3); 1559 if (errno == ERANGE) { 1560 lex_error("cos result out of range"); 1561 YYABORT; 1562 } 1563 } 1564 | ATAN2 '(' any_expr ',' any_expr ')' 1565 { 1566 errno = 0; 1567 $$ = atan2($3, $5); 1568 if (errno == EDOM) { 1569 lex_error("atan2 argument out of domain"); 1570 YYABORT; 1571 } 1572 if (errno == ERANGE) { 1573 lex_error("atan2 result out of range"); 1574 YYABORT; 1575 } 1576 } 1577 | LOG '(' any_expr ')' 1578 { 1579 errno = 0; 1580 $$ = log10($3); 1581 if (errno == ERANGE) { 1582 lex_error("log result out of range"); 1583 YYABORT; 1584 } 1585 } 1586 | EXP '(' any_expr ')' 1587 { 1588 errno = 0; 1589 $$ = pow(10.0, $3); 1590 if (errno == ERANGE) { 1591 lex_error("exp result out of range"); 1592 YYABORT; 1593 } 1594 } 1595 | SQRT '(' any_expr ')' 1596 { 1597 errno = 0; 1598 $$ = sqrt($3); 1599 if (errno == EDOM) { 1600 lex_error("sqrt argument out of domain"); 1601 YYABORT; 1602 } 1603 } 1604 | K_MAX '(' any_expr ',' any_expr ')' 1605 { $$ = $3 > $5 ? $3 : $5; } 1606 | K_MIN '(' any_expr ',' any_expr ')' 1607 { $$ = $3 < $5 ? $3 : $5; } 1608 | INT '(' any_expr ')' 1609 { $$ = floor($3); } 1610 | RAND '(' any_expr ')' 1611 { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); } 1612 | RAND '(' ')' 1613 { 1614 /* return a random number in the range [0,1) */ 1615 /* portable, but not very random */ 1616 $$ = (rand() & 0x7fff) / double(0x8000); 1617 } 1618 | SRAND '(' any_expr ')' 1619 { 1620 $$ = 0; 1621 srand((unsigned int)$3); 1622 } 1623 | expr '<' expr 1624 { $$ = ($1 < $3); } 1625 | expr LESSEQUAL expr 1626 { $$ = ($1 <= $3); } 1627 | expr '>' expr 1628 { $$ = ($1 > $3); } 1629 | expr GREATEREQUAL expr 1630 { $$ = ($1 >= $3); } 1631 | expr EQUALEQUAL expr 1632 { $$ = ($1 == $3); } 1633 | expr NOTEQUAL expr 1634 { $$ = ($1 != $3); } 1635 | expr ANDAND expr 1636 { $$ = ($1 != 0.0 && $3 != 0.0); } 1637 | expr OROR expr 1638 { $$ = ($1 != 0.0 || $3 != 0.0); } 1639 | '!' expr 1640 { $$ = ($2 == 0.0); } 1641 1642 ; 1643 1644 %% 1645 1646 /* bison defines const to be empty unless __STDC__ is defined, which it 1647 isn't under cfront */ 1648 1649 #ifdef const 1650 #undef const 1651 #endif 1652 1653 static struct { 1654 const char *name; 1655 double val; 1656 int scaled; // non-zero if val should be multiplied by scale 1657 } defaults_table[] = { 1658 { "arcrad", .25, 1 }, 1659 { "arrowht", .1, 1 }, 1660 { "arrowwid", .05, 1 }, 1661 { "circlerad", .25, 1 }, 1662 { "boxht", .5, 1 }, 1663 { "boxwid", .75, 1 }, 1664 { "boxrad", 0.0, 1 }, 1665 { "dashwid", .05, 1 }, 1666 { "ellipseht", .5, 1 }, 1667 { "ellipsewid", .75, 1 }, 1668 { "moveht", .5, 1 }, 1669 { "movewid", .5, 1 }, 1670 { "lineht", .5, 1 }, 1671 { "linewid", .5, 1 }, 1672 { "textht", 0.0, 1 }, 1673 { "textwid", 0.0, 1 }, 1674 { "scale", 1.0, 0 }, 1675 { "linethick", -1.0, 0 }, // in points 1676 { "fillval", .5, 0 }, 1677 { "arrowhead", 1.0, 0 }, 1678 { "maxpswid", 8.5, 0 }, 1679 { "maxpsht", 11.0, 0 }, 1680 }; 1681 1682 place *lookup_label(const char *label) 1683 { 1684 saved_state *state = current_saved_state; 1685 PTABLE(place) *tbl = current_table; 1686 for (;;) { 1687 place *pl = tbl->lookup(label); 1688 if (pl) 1689 return pl; 1690 if (!state) 1691 return 0; 1692 tbl = state->tbl; 1693 state = state->prev; 1694 } 1695 } 1696 1697 void define_label(const char *label, const place *pl) 1698 { 1699 place *p = new place[1]; 1700 *p = *pl; 1701 current_table->define(label, p); 1702 } 1703 1704 int lookup_variable(const char *name, double *val) 1705 { 1706 place *pl = lookup_label(name); 1707 if (pl) { 1708 *val = pl->x; 1709 return 1; 1710 } 1711 return 0; 1712 } 1713 1714 void define_variable(const char *name, double val) 1715 { 1716 place *p = new place[1]; 1717 p->obj = 0; 1718 p->x = val; 1719 p->y = 0.0; 1720 current_table->define(name, p); 1721 if (strcmp(name, "scale") == 0) { 1722 // When the scale changes, reset all scaled pre-defined variables to 1723 // their default values. 1724 for (unsigned int i = 0; 1725 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 1726 if (defaults_table[i].scaled) 1727 define_variable(defaults_table[i].name, val*defaults_table[i].val); 1728 } 1729 } 1730 1731 // called once only (not once per parse) 1732 1733 void parse_init() 1734 { 1735 current_direction = RIGHT_DIRECTION; 1736 current_position.x = 0.0; 1737 current_position.y = 0.0; 1738 // This resets everything to its default value. 1739 reset_all(); 1740 } 1741 1742 void reset(const char *nm) 1743 { 1744 for (unsigned int i = 0; 1745 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 1746 if (strcmp(nm, defaults_table[i].name) == 0) { 1747 double val = defaults_table[i].val; 1748 if (defaults_table[i].scaled) { 1749 double scale; 1750 lookup_variable("scale", &scale); 1751 val *= scale; 1752 } 1753 define_variable(defaults_table[i].name, val); 1754 return; 1755 } 1756 lex_error("`%1' is not a predefined variable", nm); 1757 } 1758 1759 void reset_all() 1760 { 1761 // We only have to explicitly reset the pre-defined variables that 1762 // aren't scaled because `scale' is not scaled, and changing the 1763 // value of `scale' will reset all the pre-defined variables that 1764 // are scaled. 1765 for (unsigned int i = 0; 1766 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 1767 if (!defaults_table[i].scaled) 1768 define_variable(defaults_table[i].name, defaults_table[i].val); 1769 } 1770 1771 // called after each parse 1772 1773 void parse_cleanup() 1774 { 1775 while (current_saved_state != 0) { 1776 delete current_table; 1777 current_table = current_saved_state->tbl; 1778 saved_state *tem = current_saved_state; 1779 current_saved_state = current_saved_state->prev; 1780 delete tem; 1781 } 1782 assert(current_table == &top_table); 1783 PTABLE_ITERATOR(place) iter(current_table); 1784 const char *key; 1785 place *pl; 1786 while (iter.next(&key, &pl)) 1787 if (pl->obj != 0) { 1788 position pos = pl->obj->origin(); 1789 pl->obj = 0; 1790 pl->x = pos.x; 1791 pl->y = pos.y; 1792 } 1793 while (olist.head != 0) { 1794 object *tem = olist.head; 1795 olist.head = olist.head->next; 1796 delete tem; 1797 } 1798 olist.tail = 0; 1799 current_direction = RIGHT_DIRECTION; 1800 current_position.x = 0.0; 1801 current_position.y = 0.0; 1802 } 1803 1804 const char *ordinal_postfix(int n) 1805 { 1806 if (n < 10 || n > 20) 1807 switch (n % 10) { 1808 case 1: 1809 return "st"; 1810 case 2: 1811 return "nd"; 1812 case 3: 1813 return "rd"; 1814 } 1815 return "th"; 1816 } 1817 1818 const char *object_type_name(object_type type) 1819 { 1820 switch (type) { 1821 case BOX_OBJECT: 1822 return "box"; 1823 case CIRCLE_OBJECT: 1824 return "circle"; 1825 case ELLIPSE_OBJECT: 1826 return "ellipse"; 1827 case ARC_OBJECT: 1828 return "arc"; 1829 case SPLINE_OBJECT: 1830 return "spline"; 1831 case LINE_OBJECT: 1832 return "line"; 1833 case ARROW_OBJECT: 1834 return "arrow"; 1835 case MOVE_OBJECT: 1836 return "move"; 1837 case TEXT_OBJECT: 1838 return "\"\""; 1839 case BLOCK_OBJECT: 1840 return "[]"; 1841 case OTHER_OBJECT: 1842 case MARK_OBJECT: 1843 default: 1844 break; 1845 } 1846 return "object"; 1847 } 1848 1849 static char sprintf_buf[1024]; 1850 1851 char *format_number(const char *form, double n) 1852 { 1853 if (form == 0) 1854 form = "%g"; 1855 return do_sprintf(form, &n, 1); 1856 } 1857 1858 char *do_sprintf(const char *form, const double *v, int nv) 1859 { 1860 string result; 1861 int i = 0; 1862 string one_format; 1863 while (*form) { 1864 if (*form == '%') { 1865 one_format += *form++; 1866 for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++) 1867 one_format += *form; 1868 if (*form == '\0' || strchr("eEfgG%", *form) == 0) { 1869 lex_error("bad sprintf format"); 1870 result += one_format; 1871 result += form; 1872 break; 1873 } 1874 if (*form == '%') { 1875 one_format += *form++; 1876 one_format += '\0'; 1877 snprintf(sprintf_buf, sizeof(sprintf_buf), 1878 "%s", one_format.contents()); 1879 } 1880 else { 1881 if (i >= nv) { 1882 lex_error("too few arguments to snprintf"); 1883 result += one_format; 1884 result += form; 1885 break; 1886 } 1887 one_format += *form++; 1888 one_format += '\0'; 1889 snprintf(sprintf_buf, sizeof(sprintf_buf), 1890 one_format.contents(), v[i++]); 1891 } 1892 one_format.clear(); 1893 result += sprintf_buf; 1894 } 1895 else 1896 result += *form++; 1897 } 1898 result += '\0'; 1899 return strsave(result.contents()); 1900 } 1901