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