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