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