1151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
279543Sru   Free Software Foundation, Inc.
375584Sru     Written by James Clark (jjc@jclark.com)
475584Sru
575584SruThis file is part of groff.
675584Sru
775584Srugroff is free software; you can redistribute it and/or modify it under
875584Sruthe terms of the GNU General Public License as published by the Free
975584SruSoftware Foundation; either version 2, or (at your option) any later
1075584Sruversion.
1175584Sru
1275584Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
1375584SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
1475584SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1575584Srufor more details.
1675584Sru
1775584SruYou should have received a copy of the GNU General Public License along
1875584Sruwith groff; see the file COPYING.  If not, write to the Free Software
19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
2075584Sru%{
2175584Sru#include "pic.h"
2275584Sru#include "ptable.h"
2375584Sru#include "object.h"
2475584Sru
2575584Sruextern int delim_flag;
2675584Sruextern void copy_rest_thru(const char *, const char *);
2775584Sruextern void copy_file_thru(const char *, const char *, const char *);
2875584Sruextern void push_body(const char *);
2975584Sruextern void do_for(char *var, double from, double to,
3075584Sru		   int by_is_multiplicative, double by, char *body);
3175584Sruextern void do_lookahead();
3275584Sru
3375584Sru/* Maximum number of characters produced by printf("%g") */
3475584Sru#define GDIGITS 14
3575584Sru
3675584Sruint yylex();
3775584Sruvoid yyerror(const char *);
3875584Sru
3975584Sruvoid reset(const char *nm);
4075584Sruvoid reset_all();
4175584Sru
4275584Sruplace *lookup_label(const char *);
4375584Sruvoid define_label(const char *label, const place *pl);
4475584Sru
4575584Srudirection current_direction;
4675584Sruposition current_position;
4775584Sru
4875584Sruimplement_ptable(place)
4975584Sru
5075584SruPTABLE(place) top_table;
5175584Sru
5275584SruPTABLE(place) *current_table = &top_table;
5375584Srusaved_state *current_saved_state = 0;
5475584Sru
5575584Sruobject_list olist;
5675584Sru
5775584Sruconst char *ordinal_postfix(int n);
5875584Sruconst char *object_type_name(object_type type);
5975584Sruchar *format_number(const char *form, double n);
6075584Sruchar *do_sprintf(const char *form, const double *v, int nv);
6175584Sru
6275584Sru%}
6375584Sru
6475584Sru
6575584Sru%union {
6675584Sru	char *str;
6775584Sru	int n;
6875584Sru	double x;
6975584Sru	struct { double x, y; } pair;
7075584Sru	struct { double x; char *body; } if_data;
7175584Sru	struct { char *str; const char *filename; int lineno; } lstr;
7275584Sru	struct { double *v; int nv; int maxv; } dv;
7375584Sru	struct { double val; int is_multiplicative; } by;
7475584Sru	place pl;
7575584Sru	object *obj;
7675584Sru	corner crn;
7775584Sru	path *pth;
7875584Sru	object_spec *spec;
7975584Sru	saved_state *pstate;
8075584Sru	graphics_state state;
8175584Sru	object_type obtype;
8275584Sru}
8375584Sru
8475584Sru%token <str> LABEL
8575584Sru%token <str> VARIABLE
8675584Sru%token <x> NUMBER
8775584Sru%token <lstr> TEXT
8875584Sru%token <lstr> COMMAND_LINE
8975584Sru%token <str> DELIMITED
9075584Sru%token <n> ORDINAL
9175584Sru%token TH
9275584Sru%token LEFT_ARROW_HEAD
9375584Sru%token RIGHT_ARROW_HEAD
9475584Sru%token DOUBLE_ARROW_HEAD
9575584Sru%token LAST
9675584Sru%token UP
9775584Sru%token DOWN
9875584Sru%token LEFT
9975584Sru%token RIGHT
10075584Sru%token BOX
10175584Sru%token CIRCLE
10275584Sru%token ELLIPSE
10375584Sru%token ARC
10475584Sru%token LINE
10575584Sru%token ARROW
10675584Sru%token MOVE
10775584Sru%token SPLINE
10875584Sru%token HEIGHT
10975584Sru%token RADIUS
110114402Sru%token FIGNAME
11175584Sru%token WIDTH
11275584Sru%token DIAMETER
11375584Sru%token UP
11475584Sru%token DOWN
11575584Sru%token RIGHT
11675584Sru%token LEFT
11775584Sru%token FROM
11875584Sru%token TO
11975584Sru%token AT
12075584Sru%token WITH
12175584Sru%token BY
12275584Sru%token THEN
12375584Sru%token SOLID
12475584Sru%token DOTTED
12575584Sru%token DASHED
12675584Sru%token CHOP
12775584Sru%token SAME
12875584Sru%token INVISIBLE
12975584Sru%token LJUST
13075584Sru%token RJUST
13175584Sru%token ABOVE
13275584Sru%token BELOW
13375584Sru%token OF
13475584Sru%token THE
13575584Sru%token WAY
13675584Sru%token BETWEEN
13775584Sru%token AND
13875584Sru%token HERE
13975584Sru%token DOT_N
14075584Sru%token DOT_E
14175584Sru%token DOT_W
14275584Sru%token DOT_S
14375584Sru%token DOT_NE
14475584Sru%token DOT_SE
14575584Sru%token DOT_NW
14675584Sru%token DOT_SW
14775584Sru%token DOT_C
14875584Sru%token DOT_START
14975584Sru%token DOT_END
15075584Sru%token DOT_X
15175584Sru%token DOT_Y
15275584Sru%token DOT_HT
15375584Sru%token DOT_WID
15475584Sru%token DOT_RAD
15575584Sru%token SIN
15675584Sru%token COS
15775584Sru%token ATAN2
15875584Sru%token LOG
15975584Sru%token EXP
16075584Sru%token SQRT
16175584Sru%token K_MAX
16275584Sru%token K_MIN
16375584Sru%token INT
16475584Sru%token RAND
16575584Sru%token SRAND
16675584Sru%token COPY
16775584Sru%token THRU
16875584Sru%token TOP
16975584Sru%token BOTTOM
17075584Sru%token UPPER
17175584Sru%token LOWER
17275584Sru%token SH
17375584Sru%token PRINT
17475584Sru%token CW
17575584Sru%token CCW
17675584Sru%token FOR
17775584Sru%token DO
17875584Sru%token IF
17975584Sru%token ELSE
18075584Sru%token ANDAND
18175584Sru%token OROR
18275584Sru%token NOTEQUAL
18375584Sru%token EQUALEQUAL
18475584Sru%token LESSEQUAL
18575584Sru%token GREATEREQUAL
18675584Sru%token LEFT_CORNER
18775584Sru%token RIGHT_CORNER
188104862Sru%token NORTH
189104862Sru%token SOUTH
190104862Sru%token EAST
191104862Sru%token WEST
19275584Sru%token CENTER
19375584Sru%token END
19475584Sru%token START
19575584Sru%token RESET
19675584Sru%token UNTIL
19775584Sru%token PLOT
19875584Sru%token THICKNESS
19975584Sru%token FILL
200104862Sru%token COLORED
201104862Sru%token OUTLINED
202104862Sru%token SHADED
20375584Sru%token ALIGNED
20475584Sru%token SPRINTF
20575584Sru%token COMMAND
20675584Sru
20775584Sru%token DEFINE
20875584Sru%token UNDEF
20975584Sru
210104862Sru%left '.'
211104862Sru
21275584Sru/* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
21375584Sru%left PLOT
21475584Sru%left TEXT SPRINTF
21575584Sru
21675584Sru/* give text adjustments higher precedence than TEXT, so that
21775584Srubox "foo" above ljust == box ("foo" above ljust)
21875584Sru*/
21975584Sru
22075584Sru%left LJUST RJUST ABOVE BELOW
22175584Sru
22275584Sru%left LEFT RIGHT
22375584Sru/* Give attributes that take an optional expression a higher
22475584Sruprecedence than left and right, so that eg `line chop left'
22575584Sruparses properly. */
226104862Sru%left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
22775584Sru%left LABEL
22875584Sru
22975584Sru%left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
23075584Sru%left ORDINAL HERE '`'
23175584Sru
232104862Sru%left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '['
233104862Sru
23475584Sru/* these need to be lower than '-' */
23575584Sru%left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
23675584Sru
23775584Sru/* these must have higher precedence than CHOP so that `label %prec CHOP'
23875584Sruworks */
23975584Sru%left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
24075584Sru%left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
241104862Sru%left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
24275584Sru
24375584Sru%left ','
24475584Sru%left OROR
24575584Sru%left ANDAND
24675584Sru%left EQUALEQUAL NOTEQUAL
24775584Sru%left '<' '>' LESSEQUAL GREATEREQUAL
24875584Sru
24975584Sru%left BETWEEN OF
25075584Sru%left AND
25175584Sru
25275584Sru%left '+' '-'
25375584Sru%left '*' '/' '%'
25475584Sru%right '!'
25575584Sru%right '^'
25675584Sru
25775584Sru%type <x> expr any_expr text_expr
25875584Sru%type <by> optional_by
25975584Sru%type <pair> expr_pair position_not_place
26075584Sru%type <if_data> simple_if
26175584Sru%type <obj> nth_primitive
26275584Sru%type <crn> corner
26375584Sru%type <pth> path label_path relative_path
26475584Sru%type <pl> place label element element_list middle_element_list
26575584Sru%type <spec> object_spec
26675584Sru%type <pair> position
26775584Sru%type <obtype> object_type
26875584Sru%type <n> optional_ordinal_last ordinal
269114402Sru%type <str> macro_name until
27075584Sru%type <dv> sprintf_args
27175584Sru%type <lstr> text print_args print_arg
27275584Sru
27375584Sru%%
27475584Sru
27575584Srutop:
27675584Sru	optional_separator
27775584Sru	| element_list
27875584Sru		{
27975584Sru		  if (olist.head)
28075584Sru		    print_picture(olist.head);
28175584Sru		}
28275584Sru	;
28375584Sru
28475584Sru
28575584Sruelement_list:
28675584Sru	optional_separator middle_element_list optional_separator
28775584Sru		{ $$ = $2; }
28875584Sru	;
28975584Sru
29075584Srumiddle_element_list:
29175584Sru	element
29275584Sru		{ $$ = $1; }
29375584Sru	| middle_element_list separator element
29475584Sru		{ $$ = $1; }
29575584Sru	;
29675584Sru
29775584Sruoptional_separator:
29875584Sru	/* empty */
29975584Sru	| separator
30075584Sru	;
30175584Sru
30275584Sruseparator:
30375584Sru	';'
30475584Sru	| separator ';'
30575584Sru	;
30675584Sru
30775584Sruplaceless_element:
308114402Sru	FIGNAME '=' macro_name
309114402Sru		{
310114402Sru		  a_delete graphname;
311114402Sru		  graphname = new char[strlen($3) + 1];
312114402Sru		  strcpy(graphname, $3);
313114402Sru		  a_delete $3;
314114402Sru		}
315114402Sru	|
31675584Sru	VARIABLE '=' any_expr
31775584Sru		{
31875584Sru		  define_variable($1, $3);
31975584Sru		  a_delete $1;
32075584Sru		}
32175584Sru	| VARIABLE ':' '=' any_expr
32275584Sru		{
32375584Sru		  place *p = lookup_label($1);
32475584Sru		  if (!p) {
32575584Sru		    lex_error("variable `%1' not defined", $1);
32675584Sru		    YYABORT;
32775584Sru		  }
32875584Sru		  p->obj = 0;
32975584Sru		  p->x = $4;
33075584Sru		  p->y = 0.0;
33175584Sru		  a_delete $1;
33275584Sru		}
33375584Sru	| UP
33475584Sru		{ current_direction = UP_DIRECTION; }
33575584Sru	| DOWN
33675584Sru		{ current_direction = DOWN_DIRECTION; }
33775584Sru	| LEFT
33875584Sru		{ current_direction = LEFT_DIRECTION; }
33975584Sru	| RIGHT
34075584Sru		{ current_direction = RIGHT_DIRECTION; }
34175584Sru	| COMMAND_LINE
34275584Sru		{
34375584Sru		  olist.append(make_command_object($1.str, $1.filename,
34475584Sru						   $1.lineno));
34575584Sru		}
34675584Sru	| COMMAND print_args
34775584Sru		{
34875584Sru		  olist.append(make_command_object($2.str, $2.filename,
34975584Sru						   $2.lineno));
35075584Sru		}
35175584Sru	| PRINT print_args
35275584Sru		{
35375584Sru		  fprintf(stderr, "%s\n", $2.str);
35475584Sru		  a_delete $2.str;
355104862Sru		  fflush(stderr);
35675584Sru		}
35775584Sru	| SH
35875584Sru		{ delim_flag = 1; }
35975584Sru	  DELIMITED
36075584Sru		{
36175584Sru		  delim_flag = 0;
36275584Sru		  if (safer_flag)
36375584Sru		    lex_error("unsafe to run command `%1'", $3);
36475584Sru		  else
36575584Sru		    system($3);
36675584Sru		  a_delete $3;
36775584Sru		}
36875584Sru	| COPY TEXT
36975584Sru		{
37075584Sru		  if (yychar < 0)
37175584Sru		    do_lookahead();
37275584Sru		  do_copy($2.str);
37375584Sru		  // do not delete the filename
37475584Sru		}
37575584Sru	| COPY TEXT THRU
37675584Sru		{ delim_flag = 2; }
37775584Sru	  DELIMITED
37875584Sru		{ delim_flag = 0; }
37975584Sru	  until
38075584Sru		{
38175584Sru		  if (yychar < 0)
38275584Sru		    do_lookahead();
38375584Sru		  copy_file_thru($2.str, $5, $7);
38475584Sru		  // do not delete the filename
38575584Sru		  a_delete $5;
38675584Sru		  a_delete $7;
38775584Sru		}
38875584Sru	| COPY THRU
38975584Sru		{ delim_flag = 2; }
39075584Sru	  DELIMITED
39175584Sru		{ delim_flag = 0; }
39275584Sru	  until
39375584Sru		{
39475584Sru		  if (yychar < 0)
39575584Sru		    do_lookahead();
39675584Sru		  copy_rest_thru($4, $6);
39775584Sru		  a_delete $4;
39875584Sru		  a_delete $6;
39975584Sru		}
40075584Sru	| FOR VARIABLE '=' expr TO expr optional_by DO
40175584Sru	  	{ delim_flag = 1; }
40275584Sru	  DELIMITED
40375584Sru	  	{
40475584Sru		  delim_flag = 0;
40575584Sru		  if (yychar < 0)
40675584Sru		    do_lookahead();
40775584Sru		  do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10);
40875584Sru		}
40975584Sru	| simple_if
41075584Sru		{
41175584Sru		  if (yychar < 0)
41275584Sru		    do_lookahead();
41375584Sru		  if ($1.x != 0.0)
41475584Sru		    push_body($1.body);
41575584Sru		  a_delete $1.body;
41675584Sru		}
41775584Sru	| simple_if ELSE
41875584Sru		{ delim_flag = 1; }
41975584Sru	  DELIMITED
42075584Sru		{
42175584Sru		  delim_flag = 0;
42275584Sru		  if (yychar < 0)
42375584Sru		    do_lookahead();
42475584Sru		  if ($1.x != 0.0)
42575584Sru		    push_body($1.body);
42675584Sru		  else
42775584Sru		    push_body($4);
42875584Sru		  a_delete $1.body;
42975584Sru		  a_delete $4;
43075584Sru		}
43175584Sru	| reset_variables
43275584Sru	| RESET
43375584Sru		{ define_variable("scale", 1.0); }
43475584Sru	;
43575584Sru
436114402Srumacro_name:
437114402Sru	VARIABLE
438114402Sru	| LABEL
439114402Sru	;
440114402Sru
44175584Srureset_variables:
44275584Sru	RESET VARIABLE
443104862Sru		{
444104862Sru		  reset($2);
445104862Sru		  a_delete $2;
446104862Sru		}
44775584Sru	| reset_variables VARIABLE
448104862Sru		{
449104862Sru		  reset($2);
450104862Sru		  a_delete $2;
451104862Sru		}
45275584Sru	| reset_variables ',' VARIABLE
453104862Sru		{
454104862Sru		  reset($3);
455104862Sru		  a_delete $3;
456104862Sru		}
45775584Sru	;
45875584Sru
45975584Sruprint_args:
46075584Sru	print_arg
46175584Sru		{ $$ = $1; }
46275584Sru	| print_args print_arg
46375584Sru		{
46475584Sru		  $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
46575584Sru		  strcpy($$.str, $1.str);
46675584Sru		  strcat($$.str, $2.str);
46775584Sru		  a_delete $1.str;
46875584Sru		  a_delete $2.str;
46975584Sru		  if ($1.filename) {
47075584Sru		    $$.filename = $1.filename;
47175584Sru		    $$.lineno = $1.lineno;
47275584Sru		  }
47375584Sru		  else if ($2.filename) {
47475584Sru		    $$.filename = $2.filename;
47575584Sru		    $$.lineno = $2.lineno;
47675584Sru		  }
47775584Sru		}
47875584Sru	;
47975584Sru
48075584Sruprint_arg:
481104862Sru  	expr							%prec ','
48275584Sru		{
48375584Sru		  $$.str = new char[GDIGITS + 1];
48475584Sru		  sprintf($$.str, "%g", $1);
48575584Sru		  $$.filename = 0;
48675584Sru		  $$.lineno = 0;
48775584Sru		}
48875584Sru	| text
48975584Sru		{ $$ = $1; }
490104862Sru	| position						%prec ','
49175584Sru		{
49275584Sru		  $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
49375584Sru		  sprintf($$.str, "%g, %g", $1.x, $1.y);
49475584Sru		  $$.filename = 0;
49575584Sru		  $$.lineno = 0;
49675584Sru		}
497104862Sru	;
49875584Sru
49975584Srusimple_if:
50075584Sru	IF any_expr THEN
50175584Sru		{ delim_flag = 1; }
50275584Sru	DELIMITED
503104862Sru		{
504104862Sru		  delim_flag = 0;
505104862Sru		  $$.x = $2;
506104862Sru		  $$.body = $5;
507104862Sru		}
50875584Sru	;
50975584Sru
51075584Sruuntil:
51175584Sru	/* empty */
51275584Sru		{ $$ = 0; }
51375584Sru	| UNTIL TEXT
51475584Sru		{ $$ = $2.str; }
51575584Sru	;
51675584Sru
51775584Sruany_expr:
51875584Sru	expr
51975584Sru		{ $$ = $1; }
52075584Sru	| text_expr
52175584Sru		{ $$ = $1; }
52275584Sru	;
52375584Sru
52475584Srutext_expr:
52575584Sru	text EQUALEQUAL text
52675584Sru		{
52775584Sru		  $$ = strcmp($1.str, $3.str) == 0;
52875584Sru		  a_delete $1.str;
52975584Sru		  a_delete $3.str;
53075584Sru		}
53175584Sru	| text NOTEQUAL text
53275584Sru		{
53375584Sru		  $$ = strcmp($1.str, $3.str) != 0;
53475584Sru		  a_delete $1.str;
53575584Sru		  a_delete $3.str;
53675584Sru		}
53775584Sru	| text_expr ANDAND text_expr
53875584Sru		{ $$ = ($1 != 0.0 && $3 != 0.0); }
53975584Sru	| text_expr ANDAND expr
54075584Sru		{ $$ = ($1 != 0.0 && $3 != 0.0); }
54175584Sru	| expr ANDAND text_expr
54275584Sru		{ $$ = ($1 != 0.0 && $3 != 0.0); }
54375584Sru	| text_expr OROR text_expr
54475584Sru		{ $$ = ($1 != 0.0 || $3 != 0.0); }
54575584Sru	| text_expr OROR expr
54675584Sru		{ $$ = ($1 != 0.0 || $3 != 0.0); }
54775584Sru	| expr OROR text_expr
54875584Sru		{ $$ = ($1 != 0.0 || $3 != 0.0); }
54975584Sru	| '!' text_expr
55075584Sru		{ $$ = ($2 == 0.0); }
55175584Sru	;
55275584Sru
55375584Sru
55475584Sruoptional_by:
55575584Sru	/* empty */
556104862Sru		{
557104862Sru		  $$.val = 1.0;
558104862Sru		  $$.is_multiplicative = 0;
559104862Sru		}
56075584Sru	| BY expr
561104862Sru		{
562104862Sru		  $$.val = $2;
563104862Sru		  $$.is_multiplicative = 0;
564104862Sru		}
56575584Sru	| BY '*' expr
566104862Sru		{
567104862Sru		  $$.val = $3;
568104862Sru		  $$.is_multiplicative = 1;
569104862Sru		}
57075584Sru	;
57175584Sru
57275584Sruelement:
57375584Sru	object_spec
57475584Sru		{
57575584Sru		  $$.obj = $1->make_object(&current_position,
57675584Sru					   &current_direction);
57775584Sru		  if ($$.obj == 0)
57875584Sru		    YYABORT;
57975584Sru		  delete $1;
58075584Sru		  if ($$.obj)
58175584Sru		    olist.append($$.obj);
58275584Sru		  else {
58375584Sru		    $$.x = current_position.x;
58475584Sru		    $$.y = current_position.y;
58575584Sru		  }
58675584Sru		}
58775584Sru	| LABEL ':' optional_separator element
588104862Sru		{
589104862Sru		  $$ = $4;
590104862Sru		  define_label($1, & $$);
591104862Sru		  a_delete $1;
592104862Sru		}
59375584Sru	| LABEL ':' optional_separator position_not_place
59475584Sru		{
59575584Sru		  $$.obj = 0;
59675584Sru		  $$.x = $4.x;
59775584Sru		  $$.y = $4.y;
59875584Sru		  define_label($1, & $$);
59975584Sru		  a_delete $1;
60075584Sru		}
60175584Sru	| LABEL ':' optional_separator place
60275584Sru		{
60375584Sru		  $$ = $4;
60475584Sru		  define_label($1, & $$);
60575584Sru		  a_delete $1;
60675584Sru		}
60775584Sru	| '{'
60875584Sru		{
60975584Sru		  $<state>$.x = current_position.x;
61075584Sru		  $<state>$.y = current_position.y;
61175584Sru		  $<state>$.dir = current_direction;
61275584Sru		}
61375584Sru	  element_list '}'
61475584Sru		{
61575584Sru		  current_position.x = $<state>2.x;
61675584Sru		  current_position.y = $<state>2.y;
61775584Sru		  current_direction = $<state>2.dir;
61875584Sru		}
61975584Sru	  optional_element
62075584Sru		{
62175584Sru		  $$ = $3;
62275584Sru		}
62375584Sru	| placeless_element
62475584Sru		{
62575584Sru		  $$.obj = 0;
62675584Sru		  $$.x = current_position.x;
62775584Sru		  $$.y = current_position.y;
62875584Sru		}
62975584Sru	;
63075584Sru
63175584Sruoptional_element:
63275584Sru	/* empty */
63375584Sru		{}
63475584Sru	| element
63575584Sru		{}
63675584Sru	;
63775584Sru
63875584Sruobject_spec:
63975584Sru	BOX
640104862Sru		{ $$ = new object_spec(BOX_OBJECT); }
64175584Sru	| CIRCLE
642104862Sru		{ $$ = new object_spec(CIRCLE_OBJECT); }
64375584Sru	| ELLIPSE
644104862Sru		{ $$ = new object_spec(ELLIPSE_OBJECT); }
64575584Sru	| ARC
64675584Sru		{
64775584Sru		  $$ = new object_spec(ARC_OBJECT);
64875584Sru		  $$->dir = current_direction;
64975584Sru		}
65075584Sru	| LINE
65175584Sru		{
65275584Sru		  $$ = new object_spec(LINE_OBJECT);
65375584Sru		  lookup_variable("lineht", & $$->segment_height);
65475584Sru		  lookup_variable("linewid", & $$->segment_width);
65575584Sru		  $$->dir = current_direction;
65675584Sru		}
65775584Sru	| ARROW
65875584Sru		{
65975584Sru		  $$ = new object_spec(ARROW_OBJECT);
66075584Sru		  lookup_variable("lineht", & $$->segment_height);
66175584Sru		  lookup_variable("linewid", & $$->segment_width);
66275584Sru		  $$->dir = current_direction;
66375584Sru		}
66475584Sru	| MOVE
66575584Sru		{
66675584Sru		  $$ = new object_spec(MOVE_OBJECT);
66775584Sru		  lookup_variable("moveht", & $$->segment_height);
66875584Sru		  lookup_variable("movewid", & $$->segment_width);
66975584Sru		  $$->dir = current_direction;
67075584Sru		}
67175584Sru	| SPLINE
67275584Sru		{
67375584Sru		  $$ = new object_spec(SPLINE_OBJECT);
67475584Sru		  lookup_variable("lineht", & $$->segment_height);
67575584Sru		  lookup_variable("linewid", & $$->segment_width);
67675584Sru		  $$->dir = current_direction;
67775584Sru		}
678104862Sru	| text							%prec TEXT
67975584Sru		{
68075584Sru		  $$ = new object_spec(TEXT_OBJECT);
68175584Sru		  $$->text = new text_item($1.str, $1.filename, $1.lineno);
68275584Sru		}
68375584Sru	| PLOT expr
68475584Sru		{
68575584Sru		  $$ = new object_spec(TEXT_OBJECT);
68675584Sru		  $$->text = new text_item(format_number(0, $2), 0, -1);
68775584Sru		}
68875584Sru	| PLOT expr text
68975584Sru		{
69075584Sru		  $$ = new object_spec(TEXT_OBJECT);
69175584Sru		  $$->text = new text_item(format_number($3.str, $2),
69275584Sru					   $3.filename, $3.lineno);
69375584Sru		  a_delete $3.str;
69475584Sru		}
69575584Sru	| '['
69675584Sru		{
69775584Sru		  saved_state *p = new saved_state;
69875584Sru		  $<pstate>$ = p;
69975584Sru		  p->x = current_position.x;
70075584Sru		  p->y = current_position.y;
70175584Sru		  p->dir = current_direction;
70275584Sru		  p->tbl = current_table;
70375584Sru		  p->prev = current_saved_state;
70475584Sru		  current_position.x = 0.0;
70575584Sru		  current_position.y = 0.0;
70675584Sru		  current_table = new PTABLE(place);
70775584Sru		  current_saved_state = p;
70875584Sru		  olist.append(make_mark_object());
70975584Sru		}
71075584Sru	  element_list ']'
71175584Sru		{
71275584Sru		  current_position.x = $<pstate>2->x;
71375584Sru		  current_position.y = $<pstate>2->y;
71475584Sru		  current_direction = $<pstate>2->dir;
71575584Sru		  $$ = new object_spec(BLOCK_OBJECT);
71675584Sru		  olist.wrap_up_block(& $$->oblist);
71775584Sru		  $$->tbl = current_table;
71875584Sru		  current_table = $<pstate>2->tbl;
71975584Sru		  current_saved_state = $<pstate>2->prev;
72075584Sru		  delete $<pstate>2;
72175584Sru		}
72275584Sru	| object_spec HEIGHT expr
72375584Sru		{
72475584Sru		  $$ = $1;
72575584Sru		  $$->height = $3;
72675584Sru		  $$->flags |= HAS_HEIGHT;
72775584Sru		}
72875584Sru	| object_spec RADIUS expr
72975584Sru		{
73075584Sru		  $$ = $1;
73175584Sru		  $$->radius = $3;
73275584Sru		  $$->flags |= HAS_RADIUS;
73375584Sru		}
73475584Sru	| object_spec WIDTH expr
73575584Sru		{
73675584Sru		  $$ = $1;
73775584Sru		  $$->width = $3;
73875584Sru		  $$->flags |= HAS_WIDTH;
73975584Sru		}
74075584Sru	| object_spec DIAMETER expr
74175584Sru		{
74275584Sru		  $$ = $1;
74375584Sru		  $$->radius = $3/2.0;
74475584Sru		  $$->flags |= HAS_RADIUS;
74575584Sru		}
746104862Sru	| object_spec expr					%prec HEIGHT
74775584Sru		{
74875584Sru		  $$ = $1;
74975584Sru		  $$->flags |= HAS_SEGMENT;
75075584Sru		  switch ($$->dir) {
75175584Sru		  case UP_DIRECTION:
75275584Sru		    $$->segment_pos.y += $2;
75375584Sru		    break;
75475584Sru		  case DOWN_DIRECTION:
75575584Sru		    $$->segment_pos.y -= $2;
75675584Sru		    break;
75775584Sru		  case RIGHT_DIRECTION:
75875584Sru		    $$->segment_pos.x += $2;
75975584Sru		    break;
76075584Sru		  case LEFT_DIRECTION:
76175584Sru		    $$->segment_pos.x -= $2;
76275584Sru		    break;
76375584Sru		  }
76475584Sru		}
76575584Sru	| object_spec UP
76675584Sru		{
76775584Sru		  $$ = $1;
76875584Sru		  $$->dir = UP_DIRECTION;
76975584Sru		  $$->flags |= HAS_SEGMENT;
77075584Sru		  $$->segment_pos.y += $$->segment_height;
77175584Sru		}
77275584Sru	| object_spec UP expr
77375584Sru		{
77475584Sru		  $$ = $1;
77575584Sru		  $$->dir = UP_DIRECTION;
77675584Sru		  $$->flags |= HAS_SEGMENT;
77775584Sru		  $$->segment_pos.y += $3;
77875584Sru		}
77975584Sru	| object_spec DOWN
78075584Sru		{
78175584Sru		  $$ = $1;
78275584Sru		  $$->dir = DOWN_DIRECTION;
78375584Sru		  $$->flags |= HAS_SEGMENT;
78475584Sru		  $$->segment_pos.y -= $$->segment_height;
78575584Sru		}
78675584Sru	| object_spec DOWN expr
78775584Sru		{
78875584Sru		  $$ = $1;
78975584Sru		  $$->dir = DOWN_DIRECTION;
79075584Sru		  $$->flags |= HAS_SEGMENT;
79175584Sru		  $$->segment_pos.y -= $3;
79275584Sru		}
79375584Sru	| object_spec RIGHT
79475584Sru		{
79575584Sru		  $$ = $1;
79675584Sru		  $$->dir = RIGHT_DIRECTION;
79775584Sru		  $$->flags |= HAS_SEGMENT;
79875584Sru		  $$->segment_pos.x += $$->segment_width;
79975584Sru		}
80075584Sru	| object_spec RIGHT expr
80175584Sru		{
80275584Sru		  $$ = $1;
80375584Sru		  $$->dir = RIGHT_DIRECTION;
80475584Sru		  $$->flags |= HAS_SEGMENT;
80575584Sru		  $$->segment_pos.x += $3;
80675584Sru		}
80775584Sru	| object_spec LEFT
80875584Sru		{
80975584Sru		  $$ = $1;
81075584Sru		  $$->dir = LEFT_DIRECTION;
81175584Sru		  $$->flags |= HAS_SEGMENT;
81275584Sru		  $$->segment_pos.x -= $$->segment_width;
81375584Sru		}
81475584Sru	| object_spec LEFT expr
81575584Sru		{
81675584Sru		  $$ = $1;
81775584Sru		  $$->dir = LEFT_DIRECTION;
81875584Sru		  $$->flags |= HAS_SEGMENT;
81975584Sru		  $$->segment_pos.x -= $3;
82075584Sru		}
82175584Sru	| object_spec FROM position
82275584Sru		{
82375584Sru		  $$ = $1;
82475584Sru		  $$->flags |= HAS_FROM;
82575584Sru		  $$->from.x = $3.x;
82675584Sru		  $$->from.y = $3.y;
82775584Sru		}
82875584Sru	| object_spec TO position
82975584Sru		{
83075584Sru		  $$ = $1;
83175584Sru		  if ($$->flags & HAS_SEGMENT)
83275584Sru		    $$->segment_list = new segment($$->segment_pos,
83375584Sru						   $$->segment_is_absolute,
83475584Sru						   $$->segment_list);
83575584Sru		  $$->flags |= HAS_SEGMENT;
83675584Sru		  $$->segment_pos.x = $3.x;
83775584Sru		  $$->segment_pos.y = $3.y;
83875584Sru		  $$->segment_is_absolute = 1;
83975584Sru		  $$->flags |= HAS_TO;
84075584Sru		  $$->to.x = $3.x;
84175584Sru		  $$->to.y = $3.y;
84275584Sru		}
84375584Sru	| object_spec AT position
84475584Sru		{
84575584Sru		  $$ = $1;
84675584Sru		  $$->flags |= HAS_AT;
84775584Sru		  $$->at.x = $3.x;
84875584Sru		  $$->at.y = $3.y;
84975584Sru		  if ($$->type != ARC_OBJECT) {
85075584Sru		    $$->flags |= HAS_FROM;
85175584Sru		    $$->from.x = $3.x;
85275584Sru		    $$->from.y = $3.y;
85375584Sru		  }
85475584Sru		}
85575584Sru	| object_spec WITH path
85675584Sru		{
85775584Sru		  $$ = $1;
85875584Sru		  $$->flags |= HAS_WITH;
85975584Sru		  $$->with = $3;
86075584Sru		}
861104862Sru	| object_spec WITH position				%prec ','
862104862Sru		{
863104862Sru		  $$ = $1;
864104862Sru		  $$->flags |= HAS_WITH;
865104862Sru		  position pos;
866104862Sru		  pos.x = $3.x;
867104862Sru		  pos.y = $3.y;
868104862Sru		  $$->with = new path(pos);
869104862Sru		}
87075584Sru	| object_spec BY expr_pair
87175584Sru		{
87275584Sru		  $$ = $1;
87375584Sru		  $$->flags |= HAS_SEGMENT;
87475584Sru		  $$->segment_pos.x += $3.x;
87575584Sru		  $$->segment_pos.y += $3.y;
87675584Sru		}
87775584Sru	| object_spec THEN
87875584Sru  		{
87975584Sru		  $$ = $1;
88075584Sru		  if ($$->flags & HAS_SEGMENT) {
88175584Sru		    $$->segment_list = new segment($$->segment_pos,
88275584Sru						   $$->segment_is_absolute,
88375584Sru						   $$->segment_list);
88475584Sru		    $$->flags &= ~HAS_SEGMENT;
88575584Sru		    $$->segment_pos.x = $$->segment_pos.y = 0.0;
88675584Sru		    $$->segment_is_absolute = 0;
88775584Sru		  }
88875584Sru		}
88975584Sru	| object_spec SOLID
89075584Sru		{
89175584Sru		  $$ = $1;	// nothing
89275584Sru		}
89375584Sru	| object_spec DOTTED
89475584Sru		{
89575584Sru		  $$ = $1;
89675584Sru		  $$->flags |= IS_DOTTED;
89775584Sru		  lookup_variable("dashwid", & $$->dash_width);
89875584Sru		}
89975584Sru	| object_spec DOTTED expr
90075584Sru		{
90175584Sru		  $$ = $1;
90275584Sru		  $$->flags |= IS_DOTTED;
90375584Sru		  $$->dash_width = $3;
90475584Sru		}
90575584Sru	| object_spec DASHED
90675584Sru		{
90775584Sru		  $$ = $1;
90875584Sru		  $$->flags |= IS_DASHED;
90975584Sru		  lookup_variable("dashwid", & $$->dash_width);
91075584Sru		}
91175584Sru	| object_spec DASHED expr
91275584Sru		{
91375584Sru		  $$ = $1;
91475584Sru		  $$->flags |= IS_DASHED;
91575584Sru		  $$->dash_width = $3;
91675584Sru		}
91775584Sru	| object_spec FILL
91875584Sru		{
91975584Sru		  $$ = $1;
92075584Sru		  $$->flags |= IS_DEFAULT_FILLED;
92175584Sru		}
92275584Sru	| object_spec FILL expr
92375584Sru		{
92475584Sru		  $$ = $1;
92575584Sru		  $$->flags |= IS_FILLED;
92675584Sru		  $$->fill = $3;
92775584Sru		}
928104862Sru	| object_spec SHADED text
929104862Sru		{
930104862Sru		  $$ = $1;
931104862Sru		  $$->flags |= (IS_SHADED | IS_FILLED);
932104862Sru		  $$->shaded = new char[strlen($3.str)+1];
933104862Sru		  strcpy($$->shaded, $3.str);
934104862Sru		}
935104862Sru	| object_spec COLORED text
936104862Sru		{
937104862Sru		  $$ = $1;
938104862Sru		  $$->flags |= (IS_SHADED | IS_OUTLINED | IS_FILLED);
939104862Sru		  $$->shaded = new char[strlen($3.str)+1];
940104862Sru		  strcpy($$->shaded, $3.str);
941104862Sru		  $$->outlined = new char[strlen($3.str)+1];
942104862Sru		  strcpy($$->outlined, $3.str);
943104862Sru		}
944104862Sru	| object_spec OUTLINED text
945104862Sru		{
946104862Sru		  $$ = $1;
947104862Sru		  $$->flags |= IS_OUTLINED;
948104862Sru		  $$->outlined = new char[strlen($3.str)+1];
949104862Sru		  strcpy($$->outlined, $3.str);
950104862Sru		}
95175584Sru	| object_spec CHOP
95275584Sru  		{
95375584Sru		  $$ = $1;
95475584Sru		  // line chop chop means line chop 0 chop 0
95575584Sru		  if ($$->flags & IS_DEFAULT_CHOPPED) {
95675584Sru		    $$->flags |= IS_CHOPPED;
95775584Sru		    $$->flags &= ~IS_DEFAULT_CHOPPED;
95875584Sru		    $$->start_chop = $$->end_chop = 0.0;
95975584Sru		  }
96075584Sru		  else if ($$->flags & IS_CHOPPED) {
96175584Sru		    $$->end_chop = 0.0;
96275584Sru		  }
96375584Sru		  else {
96475584Sru		    $$->flags |= IS_DEFAULT_CHOPPED;
96575584Sru		  }
96675584Sru		}
96775584Sru	| object_spec CHOP expr
96875584Sru		{
96975584Sru		  $$ = $1;
97075584Sru		  if ($$->flags & IS_DEFAULT_CHOPPED) {
97175584Sru		    $$->flags |= IS_CHOPPED;
97275584Sru		    $$->flags &= ~IS_DEFAULT_CHOPPED;
97375584Sru		    $$->start_chop = 0.0;
97475584Sru		    $$->end_chop = $3;
97575584Sru		  }
97675584Sru		  else if ($$->flags & IS_CHOPPED) {
97775584Sru		    $$->end_chop = $3;
97875584Sru		  }
97975584Sru		  else {
98075584Sru		    $$->start_chop = $$->end_chop = $3;
98175584Sru		    $$->flags |= IS_CHOPPED;
98275584Sru		  }
98375584Sru		}
98475584Sru	| object_spec SAME
98575584Sru		{
98675584Sru		  $$ = $1;
98775584Sru		  $$->flags |= IS_SAME;
98875584Sru		}
98975584Sru	| object_spec INVISIBLE
99075584Sru		{
99175584Sru		  $$ = $1;
99275584Sru		  $$->flags |= IS_INVISIBLE;
99375584Sru		}
99475584Sru	| object_spec LEFT_ARROW_HEAD
99575584Sru		{
99675584Sru		  $$ = $1;
99775584Sru		  $$->flags |= HAS_LEFT_ARROW_HEAD;
99875584Sru		}
99975584Sru	| object_spec RIGHT_ARROW_HEAD
100075584Sru		{
100175584Sru		  $$ = $1;
100275584Sru		  $$->flags |= HAS_RIGHT_ARROW_HEAD;
100375584Sru		}
100475584Sru	| object_spec DOUBLE_ARROW_HEAD
100575584Sru		{
100675584Sru		  $$ = $1;
100775584Sru		  $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
100875584Sru		}
100975584Sru	| object_spec CW
101075584Sru		{
101175584Sru		  $$ = $1;
101275584Sru		  $$->flags |= IS_CLOCKWISE;
101375584Sru		}
101475584Sru	| object_spec CCW
101575584Sru		{
101675584Sru		  $$ = $1;
101775584Sru		  $$->flags &= ~IS_CLOCKWISE;
101875584Sru		}
1019104862Sru	| object_spec text					%prec TEXT
102075584Sru		{
102175584Sru		  $$ = $1;
102275584Sru		  text_item **p;
102375584Sru		  for (p = & $$->text; *p; p = &(*p)->next)
102475584Sru		    ;
102575584Sru		  *p = new text_item($2.str, $2.filename, $2.lineno);
102675584Sru		}
102775584Sru	| object_spec LJUST
102875584Sru		{
102975584Sru		  $$ = $1;
103075584Sru		  if ($$->text) {
103175584Sru		    text_item *p;
103275584Sru		    for (p = $$->text; p->next; p = p->next)
103375584Sru		      ;
103475584Sru		    p->adj.h = LEFT_ADJUST;
103575584Sru		  }
103675584Sru		}
103775584Sru	| object_spec RJUST
103875584Sru		{
103975584Sru		  $$ = $1;
104075584Sru		  if ($$->text) {
104175584Sru		    text_item *p;
104275584Sru		    for (p = $$->text; p->next; p = p->next)
104375584Sru		      ;
104475584Sru		    p->adj.h = RIGHT_ADJUST;
104575584Sru		  }
104675584Sru		}
104775584Sru	| object_spec ABOVE
104875584Sru		{
104975584Sru		  $$ = $1;
105075584Sru		  if ($$->text) {
105175584Sru		    text_item *p;
105275584Sru		    for (p = $$->text; p->next; p = p->next)
105375584Sru		      ;
105475584Sru		    p->adj.v = ABOVE_ADJUST;
105575584Sru		  }
105675584Sru		}
105775584Sru	| object_spec BELOW
105875584Sru		{
105975584Sru		  $$ = $1;
106075584Sru		  if ($$->text) {
106175584Sru		    text_item *p;
106275584Sru		    for (p = $$->text; p->next; p = p->next)
106375584Sru		      ;
106475584Sru		    p->adj.v = BELOW_ADJUST;
106575584Sru		  }
106675584Sru		}
106775584Sru	| object_spec THICKNESS expr
106875584Sru		{
106975584Sru		  $$ = $1;
107075584Sru		  $$->flags |= HAS_THICKNESS;
107175584Sru		  $$->thickness = $3;
107275584Sru		}
107375584Sru	| object_spec ALIGNED
107475584Sru		{
107575584Sru		  $$ = $1;
107675584Sru		  $$->flags |= IS_ALIGNED;
107775584Sru		}
107875584Sru	;
107975584Sru
108075584Srutext:
108175584Sru	TEXT
1082104862Sru		{ $$ = $1; }
108375584Sru	| SPRINTF '(' TEXT sprintf_args ')'
108475584Sru		{
108575584Sru		  $$.filename = $3.filename;
108675584Sru		  $$.lineno = $3.lineno;
108775584Sru		  $$.str = do_sprintf($3.str, $4.v, $4.nv);
108875584Sru		  a_delete $4.v;
108975584Sru		  a_delete $3.str;
109075584Sru		}
109175584Sru	;
109275584Sru
109375584Srusprintf_args:
109475584Sru	/* empty */
109575584Sru		{
109675584Sru		  $$.v = 0;
109775584Sru		  $$.nv = 0;
109875584Sru		  $$.maxv = 0;
109975584Sru		}
110075584Sru	| sprintf_args ',' expr
110175584Sru		{
110275584Sru		  $$ = $1;
110375584Sru		  if ($$.nv >= $$.maxv) {
110475584Sru		    if ($$.nv == 0) {
110575584Sru		      $$.v = new double[4];
110675584Sru		      $$.maxv = 4;
110775584Sru		    }
110875584Sru		    else {
110975584Sru		      double *oldv = $$.v;
111075584Sru		      $$.maxv *= 2;
1111151497Sru#if 0
111275584Sru		      $$.v = new double[$$.maxv];
111375584Sru		      memcpy($$.v, oldv, $$.nv*sizeof(double));
1114151497Sru#else
1115151497Sru		      // workaround for bug in Compaq C++ V6.5-033
1116151497Sru		      // for Compaq Tru64 UNIX V5.1A (Rev. 1885)
1117151497Sru		      double *foo = new double[$$.maxv];
1118151497Sru		      memcpy(foo, oldv, $$.nv*sizeof(double));
1119151497Sru		      $$.v = foo;
1120151497Sru#endif
112175584Sru		      a_delete oldv;
112275584Sru		    }
112375584Sru		  }
112475584Sru		  $$.v[$$.nv] = $3;
112575584Sru		  $$.nv += 1;
112675584Sru		}
112775584Sru	;
112875584Sru
112975584Sruposition:
113075584Sru  	position_not_place
113175584Sru		{ $$ = $1; }
113275584Sru	| place
113375584Sru  		{
113475584Sru		  position pos = $1;
113575584Sru		  $$.x = pos.x;
113675584Sru		  $$.y = pos.y;
113775584Sru		}
1138151497Sru	| '(' place ')'
1139151497Sru		{
1140151497Sru		  position pos = $2;
1141151497Sru		  $$.x = pos.x;
1142151497Sru		  $$.y = pos.y;
1143151497Sru		}
114475584Sru	;
114575584Sru
114675584Sruposition_not_place:
114775584Sru	expr_pair
114875584Sru		{ $$ = $1; }
114975584Sru	| position '+' expr_pair
115075584Sru		{
115175584Sru		  $$.x = $1.x + $3.x;
115275584Sru		  $$.y = $1.y + $3.y;
115375584Sru		}
1154151497Sru	| '(' position '+' expr_pair ')'
1155151497Sru		{
1156151497Sru		  $$.x = $2.x + $4.x;
1157151497Sru		  $$.y = $2.y + $4.y;
1158151497Sru		}
115975584Sru	| position '-' expr_pair
116075584Sru		{
116175584Sru		  $$.x = $1.x - $3.x;
116275584Sru		  $$.y = $1.y - $3.y;
116375584Sru		}
1164151497Sru	| '(' position '-' expr_pair ')'
1165151497Sru		{
1166151497Sru		  $$.x = $2.x - $4.x;
1167151497Sru		  $$.y = $2.y - $4.y;
1168151497Sru		}
116975584Sru	| '(' position ',' position ')'
117075584Sru		{
117175584Sru		  $$.x = $2.x;
117275584Sru		  $$.y = $4.y;
117375584Sru		}
117475584Sru	| expr between position AND position
117575584Sru		{
117675584Sru		  $$.x = (1.0 - $1)*$3.x + $1*$5.x;
117775584Sru		  $$.y = (1.0 - $1)*$3.y + $1*$5.y;
117875584Sru		}
1179151497Sru	| '(' expr between position AND position ')'
1180151497Sru		{
1181151497Sru		  $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1182151497Sru		  $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1183151497Sru		}
118475584Sru	| expr '<' position ',' position '>'
118575584Sru		{
118675584Sru		  $$.x = (1.0 - $1)*$3.x + $1*$5.x;
118775584Sru		  $$.y = (1.0 - $1)*$3.y + $1*$5.y;
118875584Sru		}
1189151497Sru	| '(' expr '<' position ',' position '>' ')'
1190151497Sru		{
1191151497Sru		  $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1192151497Sru		  $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1193151497Sru		}
119475584Sru	;
119575584Sru
119675584Srubetween:
119775584Sru	BETWEEN
119875584Sru	| OF THE WAY BETWEEN
119975584Sru	;
120075584Sru
120175584Sruexpr_pair:
120275584Sru	expr ',' expr
1203104862Sru		{
1204104862Sru		  $$.x = $1;
1205104862Sru		  $$.y = $3;
1206104862Sru		}
120775584Sru	| '(' expr_pair ')'
120875584Sru		{ $$ = $2; }
120975584Sru	;
121075584Sru
121175584Sruplace:
1212104862Sru	/* line at A left == line (at A) left */
1213104862Sru	label							%prec CHOP
121475584Sru		{ $$ = $1; }
121575584Sru	| label corner
121675584Sru		{
121775584Sru		  path pth($2);
121875584Sru		  if (!pth.follow($1, & $$))
121975584Sru		    YYABORT;
122075584Sru		}
122175584Sru	| corner label
122275584Sru		{
122375584Sru		  path pth($1);
122475584Sru		  if (!pth.follow($2, & $$))
122575584Sru		    YYABORT;
122675584Sru		}
122775584Sru	| corner OF label
122875584Sru		{
122975584Sru		  path pth($1);
123075584Sru		  if (!pth.follow($3, & $$))
123175584Sru		    YYABORT;
123275584Sru		}
123375584Sru	| HERE
123475584Sru		{
123575584Sru		  $$.x = current_position.x;
123675584Sru		  $$.y = current_position.y;
123775584Sru		  $$.obj = 0;
123875584Sru		}
123975584Sru	;
124075584Sru
124175584Srulabel:
124275584Sru	LABEL
124375584Sru		{
124475584Sru		  place *p = lookup_label($1);
124575584Sru		  if (!p) {
124675584Sru		    lex_error("there is no place `%1'", $1);
124775584Sru		    YYABORT;
124875584Sru		  }
124975584Sru		  $$ = *p;
125075584Sru		  a_delete $1;
125175584Sru		}
125275584Sru	| nth_primitive
1253104862Sru		{ $$.obj = $1; }
125475584Sru	| label '.' LABEL
125575584Sru		{
125675584Sru		  path pth($3);
125775584Sru		  if (!pth.follow($1, & $$))
125875584Sru		    YYABORT;
125975584Sru		}
126075584Sru	;
126175584Sru
126275584Sruordinal:
126375584Sru	ORDINAL
126475584Sru		{ $$ = $1; }
126575584Sru	| '`' any_expr TH
126675584Sru		{
126775584Sru		  // XXX Check for overflow (and non-integers?).
126875584Sru		  $$ = (int)$2;
126975584Sru		}
127075584Sru	;
127175584Sru
127275584Sruoptional_ordinal_last:
1273104862Sru	LAST
127475584Sru		{ $$ = 1; }
127575584Sru  	| ordinal LAST
127675584Sru		{ $$ = $1; }
127775584Sru	;
127875584Sru
127975584Srunth_primitive:
128075584Sru	ordinal object_type
128175584Sru		{
128275584Sru		  int count = 0;
128375584Sru		  object *p;
128475584Sru		  for (p = olist.head; p != 0; p = p->next)
128575584Sru		    if (p->type() == $2 && ++count == $1) {
128675584Sru		      $$ = p;
128775584Sru		      break;
128875584Sru		    }
128975584Sru		  if (p == 0) {
129075584Sru		    lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
129175584Sru			      object_type_name($2));
129275584Sru		    YYABORT;
129375584Sru		  }
129475584Sru		}
129575584Sru	| optional_ordinal_last object_type
129675584Sru		{
129775584Sru		  int count = 0;
129875584Sru		  object *p;
129975584Sru		  for (p = olist.tail; p != 0; p = p->prev)
130075584Sru		    if (p->type() == $2 && ++count == $1) {
130175584Sru		      $$ = p;
130275584Sru		      break;
130375584Sru		    }
130475584Sru		  if (p == 0) {
130575584Sru		    lex_error("there is no %1%2 last %3", $1,
130675584Sru			      ordinal_postfix($1), object_type_name($2));
130775584Sru		    YYABORT;
130875584Sru		  }
130975584Sru		}
131075584Sru	;
131175584Sru
131275584Sruobject_type:
131375584Sru	BOX
131475584Sru  		{ $$ = BOX_OBJECT; }
131575584Sru	| CIRCLE
131675584Sru		{ $$ = CIRCLE_OBJECT; }
131775584Sru	| ELLIPSE
131875584Sru		{ $$ = ELLIPSE_OBJECT; }
131975584Sru	| ARC
132075584Sru		{ $$ = ARC_OBJECT; }
132175584Sru	| LINE
132275584Sru		{ $$ = LINE_OBJECT; }
132375584Sru	| ARROW
132475584Sru		{ $$ = ARROW_OBJECT; }
132575584Sru	| SPLINE
132675584Sru		{ $$ = SPLINE_OBJECT; }
132775584Sru	| '[' ']'
132875584Sru		{ $$ = BLOCK_OBJECT; }
132975584Sru	| TEXT
133075584Sru		{ $$ = TEXT_OBJECT; }
133175584Sru	;
133275584Sru
133375584Srulabel_path:
133475584Sru 	'.' LABEL
1335104862Sru		{ $$ = new path($2); }
133675584Sru	| label_path '.' LABEL
133775584Sru		{
133875584Sru		  $$ = $1;
133975584Sru		  $$->append($3);
134075584Sru		}
134175584Sru	;
134275584Sru
134375584Srurelative_path:
1344104862Sru	corner							%prec CHOP
1345104862Sru		{ $$ = new path($1); }
134675584Sru	/* give this a lower precedence than LEFT and RIGHT so that
134775584Sru	   [A: box] with .A left == [A: box] with (.A left) */
1348104862Sru  	| label_path						%prec TEXT
1349104862Sru		{ $$ = $1; }
135075584Sru	| label_path corner
135175584Sru		{
135275584Sru		  $$ = $1;
135375584Sru		  $$->append($2);
135475584Sru		}
135575584Sru	;
135675584Sru
135775584Srupath:
135875584Sru	relative_path
1359104862Sru		{ $$ = $1; }
136075584Sru	| '(' relative_path ',' relative_path ')'
136175584Sru		{
136275584Sru		  $$ = $2;
136375584Sru		  $$->set_ypath($4);
136475584Sru		}
136575584Sru	/* The rest of these rules are a compatibility sop. */
136675584Sru	| ORDINAL LAST object_type relative_path
136775584Sru		{
136875584Sru		  lex_warning("`%1%2 last %3' in `with' argument ignored",
136975584Sru			      $1, ordinal_postfix($1), object_type_name($3));
137075584Sru		  $$ = $4;
137175584Sru		}
137275584Sru	| LAST object_type relative_path
137375584Sru		{
137475584Sru		  lex_warning("`last %1' in `with' argument ignored",
137575584Sru			      object_type_name($2));
137675584Sru		  $$ = $3;
137775584Sru		}
137875584Sru	| ORDINAL object_type relative_path
137975584Sru		{
138075584Sru		  lex_warning("`%1%2 %3' in `with' argument ignored",
138175584Sru			      $1, ordinal_postfix($1), object_type_name($2));
138275584Sru		  $$ = $3;
138375584Sru		}
138475584Sru	| LABEL relative_path
138575584Sru		{
138675584Sru		  lex_warning("initial `%1' in `with' argument ignored", $1);
138775584Sru		  a_delete $1;
138875584Sru		  $$ = $2;
138975584Sru		}
139075584Sru	;
139175584Sru
139275584Srucorner:
139375584Sru	DOT_N
139475584Sru		{ $$ = &object::north; }
139575584Sru	| DOT_E
139675584Sru		{ $$ = &object::east; }
139775584Sru	| DOT_W
139875584Sru		{ $$ = &object::west; }
139975584Sru	| DOT_S
140075584Sru		{ $$ = &object::south; }
140175584Sru	| DOT_NE
140275584Sru		{ $$ = &object::north_east; }
140375584Sru	| DOT_SE
140475584Sru		{ $$ = &object:: south_east; }
140575584Sru	| DOT_NW
140675584Sru		{ $$ = &object::north_west; }
140775584Sru	| DOT_SW
140875584Sru		{ $$ = &object::south_west; }
140975584Sru	| DOT_C
141075584Sru		{ $$ = &object::center; }
141175584Sru	| DOT_START
141275584Sru		{ $$ = &object::start; }
141375584Sru	| DOT_END
141475584Sru		{ $$ = &object::end; }
141575584Sru  	| TOP
141675584Sru		{ $$ = &object::north; }
141775584Sru	| BOTTOM
141875584Sru		{ $$ = &object::south; }
141975584Sru	| LEFT
142075584Sru		{ $$ = &object::west; }
142175584Sru	| RIGHT
142275584Sru		{ $$ = &object::east; }
142375584Sru	| UPPER LEFT
142475584Sru		{ $$ = &object::north_west; }
142575584Sru	| LOWER LEFT
142675584Sru		{ $$ = &object::south_west; }
142775584Sru	| UPPER RIGHT
142875584Sru		{ $$ = &object::north_east; }
142975584Sru	| LOWER RIGHT
143075584Sru		{ $$ = &object::south_east; }
143175584Sru	| LEFT_CORNER
143275584Sru		{ $$ = &object::west; }
143375584Sru	| RIGHT_CORNER
143475584Sru		{ $$ = &object::east; }
143575584Sru	| UPPER LEFT_CORNER
143675584Sru		{ $$ = &object::north_west; }
143775584Sru	| LOWER LEFT_CORNER
143875584Sru		{ $$ = &object::south_west; }
143975584Sru	| UPPER RIGHT_CORNER
144075584Sru		{ $$ = &object::north_east; }
144175584Sru	| LOWER RIGHT_CORNER
144275584Sru		{ $$ = &object::south_east; }
1443104862Sru	| NORTH
1444104862Sru		{ $$ = &object::north; }
1445104862Sru	| SOUTH
1446104862Sru		{ $$ = &object::south; }
1447104862Sru	| EAST
1448104862Sru		{ $$ = &object::east; }
1449104862Sru	| WEST
1450104862Sru		{ $$ = &object::west; }
145175584Sru	| CENTER
145275584Sru		{ $$ = &object::center; }
145375584Sru	| START
145475584Sru		{ $$ = &object::start; }
145575584Sru	| END
145675584Sru		{ $$ = &object::end; }
145775584Sru	;
145875584Sru
145975584Sruexpr:
146075584Sru	VARIABLE
146175584Sru		{
146275584Sru		  if (!lookup_variable($1, & $$)) {
146375584Sru		    lex_error("there is no variable `%1'", $1);
146475584Sru		    YYABORT;
146575584Sru		  }
146675584Sru		  a_delete $1;
146775584Sru		}
146875584Sru	| NUMBER
146975584Sru		{ $$ = $1; }
147075584Sru	| place DOT_X
147175584Sru  		{
147275584Sru		  if ($1.obj != 0)
147375584Sru		    $$ = $1.obj->origin().x;
147475584Sru		  else
147575584Sru		    $$ = $1.x;
147675584Sru		}
147775584Sru	| place DOT_Y
147875584Sru		{
147975584Sru		  if ($1.obj != 0)
148075584Sru		    $$ = $1.obj->origin().y;
148175584Sru		  else
148275584Sru		    $$ = $1.y;
148375584Sru		}
148475584Sru	| place DOT_HT
148575584Sru		{
148675584Sru		  if ($1.obj != 0)
148775584Sru		    $$ = $1.obj->height();
148875584Sru		  else
148975584Sru		    $$ = 0.0;
149075584Sru		}
149175584Sru	| place DOT_WID
149275584Sru		{
149375584Sru		  if ($1.obj != 0)
149475584Sru		    $$ = $1.obj->width();
149575584Sru		  else
149675584Sru		    $$ = 0.0;
149775584Sru		}
149875584Sru	| place DOT_RAD
149975584Sru		{
150075584Sru		  if ($1.obj != 0)
150175584Sru		    $$ = $1.obj->radius();
150275584Sru		  else
150375584Sru		    $$ = 0.0;
150475584Sru		}
150575584Sru	| expr '+' expr
150675584Sru		{ $$ = $1 + $3; }
150775584Sru	| expr '-' expr
150875584Sru		{ $$ = $1 - $3; }
150975584Sru	| expr '*' expr
151075584Sru		{ $$ = $1 * $3; }
151175584Sru	| expr '/' expr
151275584Sru		{
151375584Sru		  if ($3 == 0.0) {
151475584Sru		    lex_error("division by zero");
151575584Sru		    YYABORT;
151675584Sru		  }
151775584Sru		  $$ = $1/$3;
151875584Sru		}
151975584Sru	| expr '%' expr
152075584Sru		{
152175584Sru		  if ($3 == 0.0) {
152275584Sru		    lex_error("modulus by zero");
152375584Sru		    YYABORT;
152475584Sru		  }
152575584Sru		  $$ = fmod($1, $3);
152675584Sru		}
152775584Sru	| expr '^' expr
152875584Sru		{
152975584Sru		  errno = 0;
153075584Sru		  $$ = pow($1, $3);
153175584Sru		  if (errno == EDOM) {
153275584Sru		    lex_error("arguments to `^' operator out of domain");
153375584Sru		    YYABORT;
153475584Sru		  }
153575584Sru		  if (errno == ERANGE) {
153675584Sru		    lex_error("result of `^' operator out of range");
153775584Sru		    YYABORT;
153875584Sru		  }
153975584Sru		}
1540104862Sru	| '-' expr						%prec '!'
154175584Sru		{ $$ = -$2; }
154275584Sru	| '(' any_expr ')'
154375584Sru		{ $$ = $2; }
154475584Sru	| SIN '(' any_expr ')'
154575584Sru		{
154675584Sru		  errno = 0;
154775584Sru		  $$ = sin($3);
154875584Sru		  if (errno == ERANGE) {
154975584Sru		    lex_error("sin result out of range");
155075584Sru		    YYABORT;
155175584Sru		  }
155275584Sru		}
155375584Sru	| COS '(' any_expr ')'
155475584Sru		{
155575584Sru		  errno = 0;
155675584Sru		  $$ = cos($3);
155775584Sru		  if (errno == ERANGE) {
155875584Sru		    lex_error("cos result out of range");
155975584Sru		    YYABORT;
156075584Sru		  }
156175584Sru		}
156275584Sru	| ATAN2 '(' any_expr ',' any_expr ')'
156375584Sru		{
156475584Sru		  errno = 0;
156575584Sru		  $$ = atan2($3, $5);
156675584Sru		  if (errno == EDOM) {
156775584Sru		    lex_error("atan2 argument out of domain");
156875584Sru		    YYABORT;
156975584Sru		  }
157075584Sru		  if (errno == ERANGE) {
157175584Sru		    lex_error("atan2 result out of range");
157275584Sru		    YYABORT;
157375584Sru		  }
157475584Sru		}
157575584Sru	| LOG '(' any_expr ')'
157675584Sru		{
157775584Sru		  errno = 0;
157875584Sru		  $$ = log10($3);
157975584Sru		  if (errno == ERANGE) {
158075584Sru		    lex_error("log result out of range");
158175584Sru		    YYABORT;
158275584Sru		  }
158375584Sru		}
158475584Sru	| EXP '(' any_expr ')'
158575584Sru		{
158675584Sru		  errno = 0;
158775584Sru		  $$ = pow(10.0, $3);
158875584Sru		  if (errno == ERANGE) {
158975584Sru		    lex_error("exp result out of range");
159075584Sru		    YYABORT;
159175584Sru		  }
159275584Sru		}
159375584Sru	| SQRT '(' any_expr ')'
159475584Sru		{
159575584Sru		  errno = 0;
159675584Sru		  $$ = sqrt($3);
159775584Sru		  if (errno == EDOM) {
159875584Sru		    lex_error("sqrt argument out of domain");
159975584Sru		    YYABORT;
160075584Sru		  }
160175584Sru		}
160275584Sru	| K_MAX '(' any_expr ',' any_expr ')'
160375584Sru		{ $$ = $3 > $5 ? $3 : $5; }
160475584Sru	| K_MIN '(' any_expr ',' any_expr ')'
160575584Sru		{ $$ = $3 < $5 ? $3 : $5; }
160675584Sru	| INT '(' any_expr ')'
160775584Sru		{ $$ = floor($3); }
160875584Sru	| RAND '(' any_expr ')'
160975584Sru		{ $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
161075584Sru	| RAND '(' ')'
161175584Sru		{
161275584Sru		  /* return a random number in the range [0,1) */
161375584Sru		  /* portable, but not very random */
161475584Sru		  $$ = (rand() & 0x7fff) / double(0x8000);
161575584Sru		}
161675584Sru	| SRAND '(' any_expr ')'
1617104862Sru		{
1618104862Sru		  $$ = 0;
1619104862Sru		  srand((unsigned int)$3);
1620104862Sru		}
162175584Sru	| expr '<' expr
162275584Sru		{ $$ = ($1 < $3); }
162375584Sru	| expr LESSEQUAL expr
162475584Sru		{ $$ = ($1 <= $3); }
162575584Sru	| expr '>' expr
162675584Sru		{ $$ = ($1 > $3); }
162775584Sru	| expr GREATEREQUAL expr
162875584Sru		{ $$ = ($1 >= $3); }
162975584Sru	| expr EQUALEQUAL expr
163075584Sru		{ $$ = ($1 == $3); }
163175584Sru	| expr NOTEQUAL expr
163275584Sru		{ $$ = ($1 != $3); }
163375584Sru	| expr ANDAND expr
163475584Sru		{ $$ = ($1 != 0.0 && $3 != 0.0); }
163575584Sru	| expr OROR expr
163675584Sru		{ $$ = ($1 != 0.0 || $3 != 0.0); }
163775584Sru	| '!' expr
163875584Sru		{ $$ = ($2 == 0.0); }
163975584Sru
164075584Sru	;
164175584Sru
164275584Sru%%
164375584Sru
164475584Sru/* bison defines const to be empty unless __STDC__ is defined, which it
164575584Sruisn't under cfront */
164675584Sru
164775584Sru#ifdef const
164875584Sru#undef const
164975584Sru#endif
165075584Sru
165175584Srustatic struct {
165275584Sru  const char *name;
165375584Sru  double val;
165475584Sru  int scaled;		     // non-zero if val should be multiplied by scale
165575584Sru} defaults_table[] = {
165675584Sru  { "arcrad", .25, 1 },
165775584Sru  { "arrowht", .1, 1 },
165875584Sru  { "arrowwid", .05, 1 },
165975584Sru  { "circlerad", .25, 1 },
166075584Sru  { "boxht", .5, 1 },
166175584Sru  { "boxwid", .75, 1 },
166275584Sru  { "boxrad", 0.0, 1 },
166375584Sru  { "dashwid", .05, 1 },
166475584Sru  { "ellipseht", .5, 1 },
166575584Sru  { "ellipsewid", .75, 1 },
166675584Sru  { "moveht", .5, 1 },
166775584Sru  { "movewid", .5, 1 },
166875584Sru  { "lineht", .5, 1 },
166975584Sru  { "linewid", .5, 1 },
167075584Sru  { "textht", 0.0, 1 },
167175584Sru  { "textwid", 0.0, 1 },
167275584Sru  { "scale", 1.0, 0 },
167375584Sru  { "linethick", -1.0, 0 },		// in points
167475584Sru  { "fillval", .5, 0 },
167575584Sru  { "arrowhead", 1.0, 0 },
167675584Sru  { "maxpswid", 8.5, 0 },
167775584Sru  { "maxpsht", 11.0, 0 },
167875584Sru};
167975584Sru
168075584Sruplace *lookup_label(const char *label)
168175584Sru{
168275584Sru  saved_state *state = current_saved_state;
168375584Sru  PTABLE(place) *tbl = current_table;
168475584Sru  for (;;) {
168575584Sru    place *pl = tbl->lookup(label);
168675584Sru    if (pl)
168775584Sru      return pl;
168875584Sru    if (!state)
168975584Sru      return 0;
169075584Sru    tbl = state->tbl;
169175584Sru    state = state->prev;
169275584Sru  }
169375584Sru}
169475584Sru
169575584Sruvoid define_label(const char *label, const place *pl)
169675584Sru{
1697114402Sru  place *p = new place[1];
169875584Sru  *p = *pl;
169975584Sru  current_table->define(label, p);
170075584Sru}
170175584Sru
170275584Sruint lookup_variable(const char *name, double *val)
170375584Sru{
170475584Sru  place *pl = lookup_label(name);
170575584Sru  if (pl) {
170675584Sru    *val = pl->x;
170775584Sru    return 1;
170875584Sru  }
170975584Sru  return 0;
171075584Sru}
171175584Sru
171275584Sruvoid define_variable(const char *name, double val)
171375584Sru{
1714114402Sru  place *p = new place[1];
171575584Sru  p->obj = 0;
171675584Sru  p->x = val;
171775584Sru  p->y = 0.0;
171875584Sru  current_table->define(name, p);
171975584Sru  if (strcmp(name, "scale") == 0) {
172075584Sru    // When the scale changes, reset all scaled pre-defined variables to
172175584Sru    // their default values.
172279543Sru    for (unsigned int i = 0;
172379543Sru	 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
172475584Sru      if (defaults_table[i].scaled)
172575584Sru	define_variable(defaults_table[i].name, val*defaults_table[i].val);
172675584Sru  }
172775584Sru}
172875584Sru
172975584Sru// called once only (not once per parse)
173075584Sru
173175584Sruvoid parse_init()
173275584Sru{
173375584Sru  current_direction = RIGHT_DIRECTION;
173475584Sru  current_position.x = 0.0;
173575584Sru  current_position.y = 0.0;
173675584Sru  // This resets everything to its default value.
173775584Sru  reset_all();
173875584Sru}
173975584Sru
174075584Sruvoid reset(const char *nm)
174175584Sru{
174279543Sru  for (unsigned int i = 0;
174379543Sru       i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
174475584Sru    if (strcmp(nm, defaults_table[i].name) == 0) {
174575584Sru      double val = defaults_table[i].val;
174675584Sru      if (defaults_table[i].scaled) {
174775584Sru	double scale;
174875584Sru	lookup_variable("scale", &scale);
174975584Sru	val *= scale;
175075584Sru      }
175175584Sru      define_variable(defaults_table[i].name, val);
175275584Sru      return;
175375584Sru    }
175475584Sru  lex_error("`%1' is not a predefined variable", nm);
175575584Sru}
175675584Sru
175775584Sruvoid reset_all()
175875584Sru{
175975584Sru  // We only have to explicitly reset the pre-defined variables that
176075584Sru  // aren't scaled because `scale' is not scaled, and changing the
176175584Sru  // value of `scale' will reset all the pre-defined variables that
176275584Sru  // are scaled.
176379543Sru  for (unsigned int i = 0;
176479543Sru       i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
176575584Sru    if (!defaults_table[i].scaled)
176675584Sru      define_variable(defaults_table[i].name, defaults_table[i].val);
176775584Sru}
176875584Sru
176975584Sru// called after each parse
177075584Sru
177175584Sruvoid parse_cleanup()
177275584Sru{
177375584Sru  while (current_saved_state != 0) {
177475584Sru    delete current_table;
177575584Sru    current_table = current_saved_state->tbl;
177675584Sru    saved_state *tem = current_saved_state;
177775584Sru    current_saved_state = current_saved_state->prev;
177875584Sru    delete tem;
177975584Sru  }
178075584Sru  assert(current_table == &top_table);
178175584Sru  PTABLE_ITERATOR(place) iter(current_table);
178275584Sru  const char *key;
178375584Sru  place *pl;
178475584Sru  while (iter.next(&key, &pl))
178575584Sru    if (pl->obj != 0) {
178675584Sru      position pos = pl->obj->origin();
178775584Sru      pl->obj = 0;
178875584Sru      pl->x = pos.x;
178975584Sru      pl->y = pos.y;
179075584Sru    }
179175584Sru  while (olist.head != 0) {
179275584Sru    object *tem = olist.head;
179375584Sru    olist.head = olist.head->next;
179475584Sru    delete tem;
179575584Sru  }
179675584Sru  olist.tail = 0;
179775584Sru  current_direction = RIGHT_DIRECTION;
179875584Sru  current_position.x = 0.0;
179975584Sru  current_position.y = 0.0;
180075584Sru}
180175584Sru
180275584Sruconst char *ordinal_postfix(int n)
180375584Sru{
180475584Sru  if (n < 10 || n > 20)
180575584Sru    switch (n % 10) {
180675584Sru    case 1:
180775584Sru      return "st";
180875584Sru    case 2:
180975584Sru      return "nd";
181075584Sru    case 3:
181175584Sru      return "rd";
181275584Sru    }
181375584Sru  return "th";
181475584Sru}
181575584Sru
181675584Sruconst char *object_type_name(object_type type)
181775584Sru{
181875584Sru  switch (type) {
181975584Sru  case BOX_OBJECT:
182075584Sru    return "box";
182175584Sru  case CIRCLE_OBJECT:
182275584Sru    return "circle";
182375584Sru  case ELLIPSE_OBJECT:
182475584Sru    return "ellipse";
182575584Sru  case ARC_OBJECT:
182675584Sru    return "arc";
182775584Sru  case SPLINE_OBJECT:
182875584Sru    return "spline";
182975584Sru  case LINE_OBJECT:
183075584Sru    return "line";
183175584Sru  case ARROW_OBJECT:
183275584Sru    return "arrow";
183375584Sru  case MOVE_OBJECT:
183475584Sru    return "move";
183575584Sru  case TEXT_OBJECT:
183675584Sru    return "\"\"";
183775584Sru  case BLOCK_OBJECT:
183875584Sru    return "[]";
183975584Sru  case OTHER_OBJECT:
184075584Sru  case MARK_OBJECT:
184175584Sru  default:
184275584Sru    break;
184375584Sru  }
184475584Sru  return "object";
184575584Sru}
184675584Sru
184775584Srustatic char sprintf_buf[1024];
184875584Sru
184975584Sruchar *format_number(const char *form, double n)
185075584Sru{
185175584Sru  if (form == 0)
185275584Sru    form = "%g";
1853104862Sru  return do_sprintf(form, &n, 1);
185475584Sru}
185575584Sru
185675584Sruchar *do_sprintf(const char *form, const double *v, int nv)
185775584Sru{
185875584Sru  string result;
185975584Sru  int i = 0;
186075584Sru  string one_format;
186175584Sru  while (*form) {
186275584Sru    if (*form == '%') {
186375584Sru      one_format += *form++;
186475584Sru      for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
186575584Sru	one_format += *form;
186675584Sru      if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
186775584Sru	lex_error("bad sprintf format");
186875584Sru	result += one_format;
186975584Sru	result += form;
187075584Sru	break;
187175584Sru      }
187275584Sru      if (*form == '%') {
187375584Sru	one_format += *form++;
187475584Sru	one_format += '\0';
1875104862Sru	snprintf(sprintf_buf, sizeof(sprintf_buf),
1876104862Sru		 "%s", one_format.contents());
187775584Sru      }
187875584Sru      else {
187975584Sru	if (i >= nv) {
1880104862Sru	  lex_error("too few arguments to snprintf");
188175584Sru	  result += one_format;
188275584Sru	  result += form;
188375584Sru	  break;
188475584Sru	}
188575584Sru	one_format += *form++;
188675584Sru	one_format += '\0';
1887104862Sru	snprintf(sprintf_buf, sizeof(sprintf_buf),
1888104862Sru		 one_format.contents(), v[i++]);
188975584Sru      }
189075584Sru      one_format.clear();
189175584Sru      result += sprintf_buf;
189275584Sru    }
189375584Sru    else
189475584Sru      result += *form++;
189575584Sru  }
189675584Sru  result += '\0';
189775584Sru  return strsave(result.contents());
189875584Sru}
1899