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