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