PageRenderTime 49ms CodeModel.GetById 11ms app.highlight 25ms RepoModel.GetById 2ms app.codeStats 1ms

/App/UnxUtilsSrc/unxutils/groff-1.11/pic/pic.y

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