* src/preproc/pic/depend: Removed. Unnecessary.
[s-roff.git] / src / preproc / pic / pic.y
blob28b095557b6fbe63feb82cf3a70f234f44f80d19
1 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
2 Written by James Clark (jjc@jclark.com)
4 This file is part of groff.
6 groff is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
11 groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
16 You should have received a copy of the GNU General Public License along
17 with groff; see the file COPYING. If not, write to the Free Software
18 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #include "pic.h"
21 #include "ptable.h"
22 #include "object.h"
24 extern int delim_flag;
25 extern void do_copy(const char *);
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();
33 #ifndef HAVE_FMOD
34 extern "C" {
35 double fmod(double, double);
37 #endif
39 #undef rand
40 #undef srand
41 extern "C" {
42 int rand();
43 #ifdef RET_TYPE_SRAND_IS_VOID
44 void srand(unsigned int);
45 #else
46 int srand(unsigned int);
47 #endif
50 /* Maximum number of characters produced by printf("%g") */
51 #define GDIGITS 14
53 int yylex();
54 void yyerror(const char *);
56 void reset(const char *nm);
57 void reset_all();
59 place *lookup_label(const char *);
60 void define_label(const char *label, const place *pl);
62 direction current_direction;
63 position current_position;
65 implement_ptable(place)
67 PTABLE(place) top_table;
69 PTABLE(place) *current_table = &top_table;
70 saved_state *current_saved_state = 0;
72 object_list olist;
74 const char *ordinal_postfix(int n);
75 const char *object_type_name(object_type type);
76 char *format_number(const char *form, double n);
77 char *do_sprintf(const char *form, const double *v, int nv);
82 %union {
83 char *str;
84 int n;
85 double x;
86 struct { double x, y; } pair;
87 struct { double x; char *body; } if_data;
88 struct { char *str; const char *filename; int lineno; } lstr;
89 struct { double *v; int nv; int maxv; } dv;
90 struct { double val; int is_multiplicative; } by;
91 place pl;
92 object *obj;
93 corner crn;
94 path *pth;
95 object_spec *spec;
96 saved_state *pstate;
97 graphics_state state;
98 object_type obtype;
101 %token <str> LABEL
102 %token <str> VARIABLE
103 %token <x> NUMBER
104 %token <lstr> TEXT
105 %token <lstr> COMMAND_LINE
106 %token <str> DELIMITED
107 %token <n> ORDINAL
108 %token TH
109 %token LEFT_ARROW_HEAD
110 %token RIGHT_ARROW_HEAD
111 %token DOUBLE_ARROW_HEAD
112 %token LAST
113 %token UP
114 %token DOWN
115 %token LEFT
116 %token RIGHT
117 %token BOX
118 %token CIRCLE
119 %token ELLIPSE
120 %token ARC
121 %token LINE
122 %token ARROW
123 %token MOVE
124 %token SPLINE
125 %token HEIGHT
126 %token RADIUS
127 %token WIDTH
128 %token DIAMETER
129 %token UP
130 %token DOWN
131 %token RIGHT
132 %token LEFT
133 %token FROM
134 %token TO
135 %token AT
136 %token WITH
137 %token BY
138 %token THEN
139 %token SOLID
140 %token DOTTED
141 %token DASHED
142 %token CHOP
143 %token SAME
144 %token INVISIBLE
145 %token LJUST
146 %token RJUST
147 %token ABOVE
148 %token BELOW
149 %token OF
150 %token THE
151 %token WAY
152 %token BETWEEN
153 %token AND
154 %token HERE
155 %token DOT_N
156 %token DOT_E
157 %token DOT_W
158 %token DOT_S
159 %token DOT_NE
160 %token DOT_SE
161 %token DOT_NW
162 %token DOT_SW
163 %token DOT_C
164 %token DOT_START
165 %token DOT_END
166 %token DOT_X
167 %token DOT_Y
168 %token DOT_HT
169 %token DOT_WID
170 %token DOT_RAD
171 %token SIN
172 %token COS
173 %token ATAN2
174 %token LOG
175 %token EXP
176 %token SQRT
177 %token K_MAX
178 %token K_MIN
179 %token INT
180 %token RAND
181 %token SRAND
182 %token COPY
183 %token THRU
184 %token TOP
185 %token BOTTOM
186 %token UPPER
187 %token LOWER
188 %token SH
189 %token PRINT
190 %token CW
191 %token CCW
192 %token FOR
193 %token DO
194 %token IF
195 %token ELSE
196 %token ANDAND
197 %token OROR
198 %token NOTEQUAL
199 %token EQUALEQUAL
200 %token LESSEQUAL
201 %token GREATEREQUAL
202 %token LEFT_CORNER
203 %token RIGHT_CORNER
204 %token CENTER
205 %token END
206 %token START
207 %token RESET
208 %token UNTIL
209 %token PLOT
210 %token THICKNESS
211 %token FILL
212 %token ALIGNED
213 %token SPRINTF
214 %token COMMAND
216 %token DEFINE
217 %token UNDEF
219 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
220 %left PLOT
221 %left TEXT SPRINTF
223 /* give text adjustments higher precedence than TEXT, so that
224 box "foo" above ljust == box ("foo" above ljust)
227 %left LJUST RJUST ABOVE BELOW
229 %left LEFT RIGHT
230 /* Give attributes that take an optional expression a higher
231 precedence than left and right, so that eg `line chop left'
232 parses properly. */
233 %left CHOP SOLID DASHED DOTTED UP DOWN FILL
234 %left LABEL
236 %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
237 %left ORDINAL HERE '`'
239 /* these need to be lower than '-' */
240 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
242 /* these must have higher precedence than CHOP so that `label %prec CHOP'
243 works */
244 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
245 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
246 %left UPPER LOWER CENTER START END
248 %left ','
249 %left OROR
250 %left ANDAND
251 %left EQUALEQUAL NOTEQUAL
252 %left '<' '>' LESSEQUAL GREATEREQUAL
254 %left BETWEEN OF
255 %left AND
257 %left '+' '-'
258 %left '*' '/' '%'
259 %right '!'
260 %right '^'
262 %type <x> expr any_expr text_expr
263 %type <by> optional_by
264 %type <pair> expr_pair position_not_place
265 %type <if_data> simple_if
266 %type <obj> nth_primitive
267 %type <crn> corner
268 %type <pth> path label_path relative_path
269 %type <pl> place label element element_list middle_element_list
270 %type <spec> object_spec
271 %type <pair> position
272 %type <obtype> object_type
273 %type <n> optional_ordinal_last ordinal
274 %type <str> until
275 %type <dv> sprintf_args
276 %type <lstr> text print_args print_arg
280 top:
281 optional_separator
282 | element_list
284 if (olist.head)
285 print_picture(olist.head);
290 element_list:
291 optional_separator middle_element_list optional_separator
292 { $$ = $2; }
295 middle_element_list:
296 element
297 { $$ = $1; }
298 | middle_element_list separator element
299 { $$ = $1; }
302 optional_separator:
303 /* empty */
304 | separator
307 separator:
309 | separator ';'
312 placeless_element:
313 VARIABLE '=' any_expr
315 define_variable($1, $3);
316 a_delete $1;
318 | VARIABLE ':' '=' any_expr
320 place *p = lookup_label($1);
321 if (!p) {
322 lex_error("variable `%1' not defined", $1);
323 YYABORT;
325 p->obj = 0;
326 p->x = $4;
327 p->y = 0.0;
328 a_delete $1;
330 | UP
331 { current_direction = UP_DIRECTION; }
332 | DOWN
333 { current_direction = DOWN_DIRECTION; }
334 | LEFT
335 { current_direction = LEFT_DIRECTION; }
336 | RIGHT
337 { current_direction = RIGHT_DIRECTION; }
338 | COMMAND_LINE
340 olist.append(make_command_object($1.str, $1.filename,
341 $1.lineno));
343 | COMMAND print_args
345 olist.append(make_command_object($2.str, $2.filename,
346 $2.lineno));
348 | PRINT print_args
350 fprintf(stderr, "%s\n", $2.str);
351 a_delete $2.str;
352 fflush(stderr);
354 | SH
355 { delim_flag = 1; }
356 DELIMITED
358 delim_flag = 0;
359 if (safer_flag)
360 lex_error("unsafe to run command `%1'", $3);
361 else
362 system($3);
363 a_delete $3;
365 | COPY TEXT
367 if (yychar < 0)
368 do_lookahead();
369 do_copy($2.str);
370 // do not delete the filename
372 | COPY TEXT THRU
373 { delim_flag = 2; }
374 DELIMITED
375 { delim_flag = 0; }
376 until
378 if (yychar < 0)
379 do_lookahead();
380 copy_file_thru($2.str, $5, $7);
381 // do not delete the filename
382 a_delete $5;
383 a_delete $7;
385 | COPY THRU
386 { delim_flag = 2; }
387 DELIMITED
388 { delim_flag = 0; }
389 until
391 if (yychar < 0)
392 do_lookahead();
393 copy_rest_thru($4, $6);
394 a_delete $4;
395 a_delete $6;
397 | FOR VARIABLE '=' expr TO expr optional_by DO
398 { delim_flag = 1; }
399 DELIMITED
401 delim_flag = 0;
402 if (yychar < 0)
403 do_lookahead();
404 do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10);
406 | simple_if
408 if (yychar < 0)
409 do_lookahead();
410 if ($1.x != 0.0)
411 push_body($1.body);
412 a_delete $1.body;
414 | simple_if ELSE
415 { delim_flag = 1; }
416 DELIMITED
418 delim_flag = 0;
419 if (yychar < 0)
420 do_lookahead();
421 if ($1.x != 0.0)
422 push_body($1.body);
423 else
424 push_body($4);
425 a_delete $1.body;
426 a_delete $4;
428 | reset_variables
429 | RESET
430 { define_variable("scale", 1.0); }
433 reset_variables:
434 RESET VARIABLE
435 { reset($2); a_delete $2; }
436 | reset_variables VARIABLE
437 { reset($2); a_delete $2; }
438 | reset_variables ',' VARIABLE
439 { reset($3); a_delete $3; }
442 print_args:
443 print_arg
444 { $$ = $1; }
445 | print_args print_arg
447 $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
448 strcpy($$.str, $1.str);
449 strcat($$.str, $2.str);
450 a_delete $1.str;
451 a_delete $2.str;
452 if ($1.filename) {
453 $$.filename = $1.filename;
454 $$.lineno = $1.lineno;
456 else if ($2.filename) {
457 $$.filename = $2.filename;
458 $$.lineno = $2.lineno;
463 print_arg:
464 expr %prec ','
466 $$.str = new char[GDIGITS + 1];
467 sprintf($$.str, "%g", $1);
468 $$.filename = 0;
469 $$.lineno = 0;
471 | text
472 { $$ = $1; }
473 | position %prec ','
475 $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
476 sprintf($$.str, "%g, %g", $1.x, $1.y);
477 $$.filename = 0;
478 $$.lineno = 0;
481 simple_if:
482 IF any_expr THEN
483 { delim_flag = 1; }
484 DELIMITED
485 { delim_flag = 0; $$.x = $2; $$.body = $5; }
488 until:
489 /* empty */
490 { $$ = 0; }
491 | UNTIL TEXT
492 { $$ = $2.str; }
495 any_expr:
496 expr
497 { $$ = $1; }
498 | text_expr
499 { $$ = $1; }
502 text_expr:
503 text EQUALEQUAL text
505 $$ = strcmp($1.str, $3.str) == 0;
506 a_delete $1.str;
507 a_delete $3.str;
509 | text NOTEQUAL text
511 $$ = strcmp($1.str, $3.str) != 0;
512 a_delete $1.str;
513 a_delete $3.str;
515 | text_expr ANDAND text_expr
516 { $$ = ($1 != 0.0 && $3 != 0.0); }
517 | text_expr ANDAND expr
518 { $$ = ($1 != 0.0 && $3 != 0.0); }
519 | expr ANDAND text_expr
520 { $$ = ($1 != 0.0 && $3 != 0.0); }
521 | text_expr OROR text_expr
522 { $$ = ($1 != 0.0 || $3 != 0.0); }
523 | text_expr OROR expr
524 { $$ = ($1 != 0.0 || $3 != 0.0); }
525 | expr OROR text_expr
526 { $$ = ($1 != 0.0 || $3 != 0.0); }
527 | '!' text_expr
528 { $$ = ($2 == 0.0); }
532 optional_by:
533 /* empty */
534 { $$.val = 1.0; $$.is_multiplicative = 0; }
535 | BY expr
536 { $$.val = $2; $$.is_multiplicative = 0; }
537 | BY '*' expr
538 { $$.val = $3; $$.is_multiplicative = 1; }
541 element:
542 object_spec
544 $$.obj = $1->make_object(&current_position,
545 &current_direction);
546 if ($$.obj == 0)
547 YYABORT;
548 delete $1;
549 if ($$.obj)
550 olist.append($$.obj);
551 else {
552 $$.x = current_position.x;
553 $$.y = current_position.y;
556 | LABEL ':' optional_separator element
557 { $$ = $4; define_label($1, & $$); a_delete $1; }
558 | LABEL ':' optional_separator position_not_place
560 $$.obj = 0;
561 $$.x = $4.x;
562 $$.y = $4.y;
563 define_label($1, & $$);
564 a_delete $1;
566 | LABEL ':' optional_separator place
568 $$ = $4;
569 define_label($1, & $$);
570 a_delete $1;
572 | '{'
574 $<state>$.x = current_position.x;
575 $<state>$.y = current_position.y;
576 $<state>$.dir = current_direction;
578 element_list '}'
580 current_position.x = $<state>2.x;
581 current_position.y = $<state>2.y;
582 current_direction = $<state>2.dir;
584 optional_element
586 $$ = $3;
588 | placeless_element
590 $$.obj = 0;
591 $$.x = current_position.x;
592 $$.y = current_position.y;
596 optional_element:
597 /* empty */
599 | element
603 object_spec:
606 $$ = new object_spec(BOX_OBJECT);
608 | CIRCLE
610 $$ = new object_spec(CIRCLE_OBJECT);
612 | ELLIPSE
614 $$ = new object_spec(ELLIPSE_OBJECT);
616 | ARC
618 $$ = new object_spec(ARC_OBJECT);
619 $$->dir = current_direction;
621 | LINE
623 $$ = new object_spec(LINE_OBJECT);
624 lookup_variable("lineht", & $$->segment_height);
625 lookup_variable("linewid", & $$->segment_width);
626 $$->dir = current_direction;
628 | ARROW
630 $$ = new object_spec(ARROW_OBJECT);
631 lookup_variable("lineht", & $$->segment_height);
632 lookup_variable("linewid", & $$->segment_width);
633 $$->dir = current_direction;
635 | MOVE
637 $$ = new object_spec(MOVE_OBJECT);
638 lookup_variable("moveht", & $$->segment_height);
639 lookup_variable("movewid", & $$->segment_width);
640 $$->dir = current_direction;
642 | SPLINE
644 $$ = new object_spec(SPLINE_OBJECT);
645 lookup_variable("lineht", & $$->segment_height);
646 lookup_variable("linewid", & $$->segment_width);
647 $$->dir = current_direction;
649 | text %prec TEXT
651 $$ = new object_spec(TEXT_OBJECT);
652 $$->text = new text_item($1.str, $1.filename, $1.lineno);
654 | PLOT expr
656 $$ = new object_spec(TEXT_OBJECT);
657 $$->text = new text_item(format_number(0, $2), 0, -1);
659 | PLOT expr text
661 $$ = new object_spec(TEXT_OBJECT);
662 $$->text = new text_item(format_number($3.str, $2),
663 $3.filename, $3.lineno);
664 a_delete $3.str;
666 | '['
668 saved_state *p = new saved_state;
669 $<pstate>$ = p;
670 p->x = current_position.x;
671 p->y = current_position.y;
672 p->dir = current_direction;
673 p->tbl = current_table;
674 p->prev = current_saved_state;
675 current_position.x = 0.0;
676 current_position.y = 0.0;
677 current_table = new PTABLE(place);
678 current_saved_state = p;
679 olist.append(make_mark_object());
681 element_list ']'
683 current_position.x = $<pstate>2->x;
684 current_position.y = $<pstate>2->y;
685 current_direction = $<pstate>2->dir;
686 $$ = new object_spec(BLOCK_OBJECT);
687 olist.wrap_up_block(& $$->oblist);
688 $$->tbl = current_table;
689 current_table = $<pstate>2->tbl;
690 current_saved_state = $<pstate>2->prev;
691 delete $<pstate>2;
693 | object_spec HEIGHT expr
695 $$ = $1;
696 $$->height = $3;
697 $$->flags |= HAS_HEIGHT;
699 | object_spec RADIUS expr
701 $$ = $1;
702 $$->radius = $3;
703 $$->flags |= HAS_RADIUS;
705 | object_spec WIDTH expr
707 $$ = $1;
708 $$->width = $3;
709 $$->flags |= HAS_WIDTH;
711 | object_spec DIAMETER expr
713 $$ = $1;
714 $$->radius = $3/2.0;
715 $$->flags |= HAS_RADIUS;
717 | object_spec expr %prec HEIGHT
719 $$ = $1;
720 $$->flags |= HAS_SEGMENT;
721 switch ($$->dir) {
722 case UP_DIRECTION:
723 $$->segment_pos.y += $2;
724 break;
725 case DOWN_DIRECTION:
726 $$->segment_pos.y -= $2;
727 break;
728 case RIGHT_DIRECTION:
729 $$->segment_pos.x += $2;
730 break;
731 case LEFT_DIRECTION:
732 $$->segment_pos.x -= $2;
733 break;
736 | object_spec UP
738 $$ = $1;
739 $$->dir = UP_DIRECTION;
740 $$->flags |= HAS_SEGMENT;
741 $$->segment_pos.y += $$->segment_height;
743 | object_spec UP expr
745 $$ = $1;
746 $$->dir = UP_DIRECTION;
747 $$->flags |= HAS_SEGMENT;
748 $$->segment_pos.y += $3;
750 | object_spec DOWN
752 $$ = $1;
753 $$->dir = DOWN_DIRECTION;
754 $$->flags |= HAS_SEGMENT;
755 $$->segment_pos.y -= $$->segment_height;
757 | object_spec DOWN expr
759 $$ = $1;
760 $$->dir = DOWN_DIRECTION;
761 $$->flags |= HAS_SEGMENT;
762 $$->segment_pos.y -= $3;
764 | object_spec RIGHT
766 $$ = $1;
767 $$->dir = RIGHT_DIRECTION;
768 $$->flags |= HAS_SEGMENT;
769 $$->segment_pos.x += $$->segment_width;
771 | object_spec RIGHT expr
773 $$ = $1;
774 $$->dir = RIGHT_DIRECTION;
775 $$->flags |= HAS_SEGMENT;
776 $$->segment_pos.x += $3;
778 | object_spec LEFT
780 $$ = $1;
781 $$->dir = LEFT_DIRECTION;
782 $$->flags |= HAS_SEGMENT;
783 $$->segment_pos.x -= $$->segment_width;
785 | object_spec LEFT expr
787 $$ = $1;
788 $$->dir = LEFT_DIRECTION;
789 $$->flags |= HAS_SEGMENT;
790 $$->segment_pos.x -= $3;
792 | object_spec FROM position
794 $$ = $1;
795 $$->flags |= HAS_FROM;
796 $$->from.x = $3.x;
797 $$->from.y = $3.y;
799 | object_spec TO position
801 $$ = $1;
802 if ($$->flags & HAS_SEGMENT)
803 $$->segment_list = new segment($$->segment_pos,
804 $$->segment_is_absolute,
805 $$->segment_list);
806 $$->flags |= HAS_SEGMENT;
807 $$->segment_pos.x = $3.x;
808 $$->segment_pos.y = $3.y;
809 $$->segment_is_absolute = 1;
810 $$->flags |= HAS_TO;
811 $$->to.x = $3.x;
812 $$->to.y = $3.y;
814 | object_spec AT position
816 $$ = $1;
817 $$->flags |= HAS_AT;
818 $$->at.x = $3.x;
819 $$->at.y = $3.y;
820 if ($$->type != ARC_OBJECT) {
821 $$->flags |= HAS_FROM;
822 $$->from.x = $3.x;
823 $$->from.y = $3.y;
826 | object_spec WITH path
828 $$ = $1;
829 $$->flags |= HAS_WITH;
830 $$->with = $3;
832 | object_spec BY expr_pair
834 $$ = $1;
835 $$->flags |= HAS_SEGMENT;
836 $$->segment_pos.x += $3.x;
837 $$->segment_pos.y += $3.y;
839 | object_spec THEN
841 $$ = $1;
842 if ($$->flags & HAS_SEGMENT) {
843 $$->segment_list = new segment($$->segment_pos,
844 $$->segment_is_absolute,
845 $$->segment_list);
846 $$->flags &= ~HAS_SEGMENT;
847 $$->segment_pos.x = $$->segment_pos.y = 0.0;
848 $$->segment_is_absolute = 0;
851 | object_spec SOLID
853 $$ = $1; // nothing
855 | object_spec DOTTED
857 $$ = $1;
858 $$->flags |= IS_DOTTED;
859 lookup_variable("dashwid", & $$->dash_width);
861 | object_spec DOTTED expr
863 $$ = $1;
864 $$->flags |= IS_DOTTED;
865 $$->dash_width = $3;
867 | object_spec DASHED
869 $$ = $1;
870 $$->flags |= IS_DASHED;
871 lookup_variable("dashwid", & $$->dash_width);
873 | object_spec DASHED expr
875 $$ = $1;
876 $$->flags |= IS_DASHED;
877 $$->dash_width = $3;
879 | object_spec FILL
881 $$ = $1;
882 $$->flags |= IS_DEFAULT_FILLED;
884 | object_spec FILL expr
886 $$ = $1;
887 $$->flags |= IS_FILLED;
888 $$->fill = $3;
890 | object_spec CHOP
892 $$ = $1;
893 // line chop chop means line chop 0 chop 0
894 if ($$->flags & IS_DEFAULT_CHOPPED) {
895 $$->flags |= IS_CHOPPED;
896 $$->flags &= ~IS_DEFAULT_CHOPPED;
897 $$->start_chop = $$->end_chop = 0.0;
899 else if ($$->flags & IS_CHOPPED) {
900 $$->end_chop = 0.0;
902 else {
903 $$->flags |= IS_DEFAULT_CHOPPED;
906 | object_spec CHOP expr
908 $$ = $1;
909 if ($$->flags & IS_DEFAULT_CHOPPED) {
910 $$->flags |= IS_CHOPPED;
911 $$->flags &= ~IS_DEFAULT_CHOPPED;
912 $$->start_chop = 0.0;
913 $$->end_chop = $3;
915 else if ($$->flags & IS_CHOPPED) {
916 $$->end_chop = $3;
918 else {
919 $$->start_chop = $$->end_chop = $3;
920 $$->flags |= IS_CHOPPED;
923 | object_spec SAME
925 $$ = $1;
926 $$->flags |= IS_SAME;
928 | object_spec INVISIBLE
930 $$ = $1;
931 $$->flags |= IS_INVISIBLE;
933 | object_spec LEFT_ARROW_HEAD
935 $$ = $1;
936 $$->flags |= HAS_LEFT_ARROW_HEAD;
938 | object_spec RIGHT_ARROW_HEAD
940 $$ = $1;
941 $$->flags |= HAS_RIGHT_ARROW_HEAD;
943 | object_spec DOUBLE_ARROW_HEAD
945 $$ = $1;
946 $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
948 | object_spec CW
950 $$ = $1;
951 $$->flags |= IS_CLOCKWISE;
953 | object_spec CCW
955 $$ = $1;
956 $$->flags &= ~IS_CLOCKWISE;
958 | object_spec text %prec TEXT
960 $$ = $1;
961 text_item **p;
962 for (p = & $$->text; *p; p = &(*p)->next)
964 *p = new text_item($2.str, $2.filename, $2.lineno);
966 | object_spec LJUST
968 $$ = $1;
969 if ($$->text) {
970 text_item *p;
971 for (p = $$->text; p->next; p = p->next)
973 p->adj.h = LEFT_ADJUST;
976 | object_spec RJUST
978 $$ = $1;
979 if ($$->text) {
980 text_item *p;
981 for (p = $$->text; p->next; p = p->next)
983 p->adj.h = RIGHT_ADJUST;
986 | object_spec ABOVE
988 $$ = $1;
989 if ($$->text) {
990 text_item *p;
991 for (p = $$->text; p->next; p = p->next)
993 p->adj.v = ABOVE_ADJUST;
996 | object_spec BELOW
998 $$ = $1;
999 if ($$->text) {
1000 text_item *p;
1001 for (p = $$->text; p->next; p = p->next)
1003 p->adj.v = BELOW_ADJUST;
1006 | object_spec THICKNESS expr
1008 $$ = $1;
1009 $$->flags |= HAS_THICKNESS;
1010 $$->thickness = $3;
1012 | object_spec ALIGNED
1014 $$ = $1;
1015 $$->flags |= IS_ALIGNED;
1019 text:
1020 TEXT
1022 $$ = $1;
1024 | SPRINTF '(' TEXT sprintf_args ')'
1026 $$.filename = $3.filename;
1027 $$.lineno = $3.lineno;
1028 $$.str = do_sprintf($3.str, $4.v, $4.nv);
1029 a_delete $4.v;
1030 a_delete $3.str;
1034 sprintf_args:
1035 /* empty */
1037 $$.v = 0;
1038 $$.nv = 0;
1039 $$.maxv = 0;
1041 | sprintf_args ',' expr
1043 $$ = $1;
1044 if ($$.nv >= $$.maxv) {
1045 if ($$.nv == 0) {
1046 $$.v = new double[4];
1047 $$.maxv = 4;
1049 else {
1050 double *oldv = $$.v;
1051 $$.maxv *= 2;
1052 $$.v = new double[$$.maxv];
1053 memcpy($$.v, oldv, $$.nv*sizeof(double));
1054 a_delete oldv;
1057 $$.v[$$.nv] = $3;
1058 $$.nv += 1;
1062 position:
1063 position_not_place
1064 { $$ = $1; }
1065 | place
1067 position pos = $1;
1068 $$.x = pos.x;
1069 $$.y = pos.y;
1073 position_not_place:
1074 expr_pair
1075 { $$ = $1; }
1076 | position '+' expr_pair
1078 $$.x = $1.x + $3.x;
1079 $$.y = $1.y + $3.y;
1081 | position '-' expr_pair
1083 $$.x = $1.x - $3.x;
1084 $$.y = $1.y - $3.y;
1086 | '(' position ',' position ')'
1088 $$.x = $2.x;
1089 $$.y = $4.y;
1091 | expr between position AND position
1093 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1094 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1096 | expr '<' position ',' position '>'
1098 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1099 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1103 between:
1104 BETWEEN
1105 | OF THE WAY BETWEEN
1108 expr_pair:
1109 expr ',' expr
1110 { $$.x = $1; $$.y = $3; }
1111 | '(' expr_pair ')'
1112 { $$ = $2; }
1115 place:
1116 label %prec CHOP /* line at A left == line (at A) left */
1117 { $$ = $1; }
1118 | label corner
1120 path pth($2);
1121 if (!pth.follow($1, & $$))
1122 YYABORT;
1124 | corner label
1126 path pth($1);
1127 if (!pth.follow($2, & $$))
1128 YYABORT;
1130 | corner OF label
1132 path pth($1);
1133 if (!pth.follow($3, & $$))
1134 YYABORT;
1136 | HERE
1138 $$.x = current_position.x;
1139 $$.y = current_position.y;
1140 $$.obj = 0;
1144 label:
1145 LABEL
1147 place *p = lookup_label($1);
1148 if (!p) {
1149 lex_error("there is no place `%1'", $1);
1150 YYABORT;
1152 $$ = *p;
1153 a_delete $1;
1155 | nth_primitive
1157 $$.obj = $1;
1159 | label '.' LABEL
1161 path pth($3);
1162 if (!pth.follow($1, & $$))
1163 YYABORT;
1167 ordinal:
1168 ORDINAL
1169 { $$ = $1; }
1170 | '`' any_expr TH
1172 // XXX Check for overflow (and non-integers?).
1173 $$ = (int)$2;
1177 optional_ordinal_last:
1178 LAST
1179 { $$ = 1; }
1180 | ordinal LAST
1181 { $$ = $1; }
1184 nth_primitive:
1185 ordinal object_type
1187 int count = 0;
1188 object *p;
1189 for (p = olist.head; p != 0; p = p->next)
1190 if (p->type() == $2 && ++count == $1) {
1191 $$ = p;
1192 break;
1194 if (p == 0) {
1195 lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
1196 object_type_name($2));
1197 YYABORT;
1200 | optional_ordinal_last object_type
1202 int count = 0;
1203 object *p;
1204 for (p = olist.tail; p != 0; p = p->prev)
1205 if (p->type() == $2 && ++count == $1) {
1206 $$ = p;
1207 break;
1209 if (p == 0) {
1210 lex_error("there is no %1%2 last %3", $1,
1211 ordinal_postfix($1), object_type_name($2));
1212 YYABORT;
1217 object_type:
1219 { $$ = BOX_OBJECT; }
1220 | CIRCLE
1221 { $$ = CIRCLE_OBJECT; }
1222 | ELLIPSE
1223 { $$ = ELLIPSE_OBJECT; }
1224 | ARC
1225 { $$ = ARC_OBJECT; }
1226 | LINE
1227 { $$ = LINE_OBJECT; }
1228 | ARROW
1229 { $$ = ARROW_OBJECT; }
1230 | SPLINE
1231 { $$ = SPLINE_OBJECT; }
1232 | '[' ']'
1233 { $$ = BLOCK_OBJECT; }
1234 | TEXT
1235 { $$ = TEXT_OBJECT; }
1238 label_path:
1239 '.' LABEL
1241 $$ = new path($2);
1243 | label_path '.' LABEL
1245 $$ = $1;
1246 $$->append($3);
1250 relative_path:
1251 corner
1253 $$ = new path($1);
1255 /* give this a lower precedence than LEFT and RIGHT so that
1256 [A: box] with .A left == [A: box] with (.A left) */
1258 | label_path %prec TEXT
1260 $$ = $1;
1262 | label_path corner
1264 $$ = $1;
1265 $$->append($2);
1269 path:
1270 relative_path
1272 $$ = $1;
1274 | '(' relative_path ',' relative_path ')'
1276 $$ = $2;
1277 $$->set_ypath($4);
1279 /* The rest of these rules are a compatibility sop. */
1280 | ORDINAL LAST object_type relative_path
1282 lex_warning("`%1%2 last %3' in `with' argument ignored",
1283 $1, ordinal_postfix($1), object_type_name($3));
1284 $$ = $4;
1286 | LAST object_type relative_path
1288 lex_warning("`last %1' in `with' argument ignored",
1289 object_type_name($2));
1290 $$ = $3;
1292 | ORDINAL object_type relative_path
1294 lex_warning("`%1%2 %3' in `with' argument ignored",
1295 $1, ordinal_postfix($1), object_type_name($2));
1296 $$ = $3;
1298 | LABEL relative_path
1300 lex_warning("initial `%1' in `with' argument ignored", $1);
1301 a_delete $1;
1302 $$ = $2;
1306 corner:
1307 DOT_N
1308 { $$ = &object::north; }
1309 | DOT_E
1310 { $$ = &object::east; }
1311 | DOT_W
1312 { $$ = &object::west; }
1313 | DOT_S
1314 { $$ = &object::south; }
1315 | DOT_NE
1316 { $$ = &object::north_east; }
1317 | DOT_SE
1318 { $$ = &object:: south_east; }
1319 | DOT_NW
1320 { $$ = &object::north_west; }
1321 | DOT_SW
1322 { $$ = &object::south_west; }
1323 | DOT_C
1324 { $$ = &object::center; }
1325 | DOT_START
1326 { $$ = &object::start; }
1327 | DOT_END
1328 { $$ = &object::end; }
1329 | TOP
1330 { $$ = &object::north; }
1331 | BOTTOM
1332 { $$ = &object::south; }
1333 | LEFT
1334 { $$ = &object::west; }
1335 | RIGHT
1336 { $$ = &object::east; }
1337 | UPPER LEFT
1338 { $$ = &object::north_west; }
1339 | LOWER LEFT
1340 { $$ = &object::south_west; }
1341 | UPPER RIGHT
1342 { $$ = &object::north_east; }
1343 | LOWER RIGHT
1344 { $$ = &object::south_east; }
1345 | LEFT_CORNER
1346 { $$ = &object::west; }
1347 | RIGHT_CORNER
1348 { $$ = &object::east; }
1349 | UPPER LEFT_CORNER
1350 { $$ = &object::north_west; }
1351 | LOWER LEFT_CORNER
1352 { $$ = &object::south_west; }
1353 | UPPER RIGHT_CORNER
1354 { $$ = &object::north_east; }
1355 | LOWER RIGHT_CORNER
1356 { $$ = &object::south_east; }
1357 | CENTER
1358 { $$ = &object::center; }
1359 | START
1360 { $$ = &object::start; }
1361 | END
1362 { $$ = &object::end; }
1365 expr:
1366 VARIABLE
1368 if (!lookup_variable($1, & $$)) {
1369 lex_error("there is no variable `%1'", $1);
1370 YYABORT;
1372 a_delete $1;
1374 | NUMBER
1375 { $$ = $1; }
1376 | place DOT_X
1378 if ($1.obj != 0)
1379 $$ = $1.obj->origin().x;
1380 else
1381 $$ = $1.x;
1383 | place DOT_Y
1385 if ($1.obj != 0)
1386 $$ = $1.obj->origin().y;
1387 else
1388 $$ = $1.y;
1390 | place DOT_HT
1392 if ($1.obj != 0)
1393 $$ = $1.obj->height();
1394 else
1395 $$ = 0.0;
1397 | place DOT_WID
1399 if ($1.obj != 0)
1400 $$ = $1.obj->width();
1401 else
1402 $$ = 0.0;
1404 | place DOT_RAD
1406 if ($1.obj != 0)
1407 $$ = $1.obj->radius();
1408 else
1409 $$ = 0.0;
1411 | expr '+' expr
1412 { $$ = $1 + $3; }
1413 | expr '-' expr
1414 { $$ = $1 - $3; }
1415 | expr '*' expr
1416 { $$ = $1 * $3; }
1417 | expr '/' expr
1419 if ($3 == 0.0) {
1420 lex_error("division by zero");
1421 YYABORT;
1423 $$ = $1/$3;
1425 | expr '%' expr
1427 if ($3 == 0.0) {
1428 lex_error("modulus by zero");
1429 YYABORT;
1431 $$ = fmod($1, $3);
1433 | expr '^' expr
1435 errno = 0;
1436 $$ = pow($1, $3);
1437 if (errno == EDOM) {
1438 lex_error("arguments to `^' operator out of domain");
1439 YYABORT;
1441 if (errno == ERANGE) {
1442 lex_error("result of `^' operator out of range");
1443 YYABORT;
1446 | '-' expr %prec '!'
1447 { $$ = -$2; }
1448 | '(' any_expr ')'
1449 { $$ = $2; }
1450 | SIN '(' any_expr ')'
1452 errno = 0;
1453 $$ = sin($3);
1454 if (errno == ERANGE) {
1455 lex_error("sin result out of range");
1456 YYABORT;
1459 | COS '(' any_expr ')'
1461 errno = 0;
1462 $$ = cos($3);
1463 if (errno == ERANGE) {
1464 lex_error("cos result out of range");
1465 YYABORT;
1468 | ATAN2 '(' any_expr ',' any_expr ')'
1470 errno = 0;
1471 $$ = atan2($3, $5);
1472 if (errno == EDOM) {
1473 lex_error("atan2 argument out of domain");
1474 YYABORT;
1476 if (errno == ERANGE) {
1477 lex_error("atan2 result out of range");
1478 YYABORT;
1481 | LOG '(' any_expr ')'
1483 errno = 0;
1484 $$ = log10($3);
1485 if (errno == ERANGE) {
1486 lex_error("log result out of range");
1487 YYABORT;
1490 | EXP '(' any_expr ')'
1492 errno = 0;
1493 $$ = pow(10.0, $3);
1494 if (errno == ERANGE) {
1495 lex_error("exp result out of range");
1496 YYABORT;
1499 | SQRT '(' any_expr ')'
1501 errno = 0;
1502 $$ = sqrt($3);
1503 if (errno == EDOM) {
1504 lex_error("sqrt argument out of domain");
1505 YYABORT;
1508 | K_MAX '(' any_expr ',' any_expr ')'
1509 { $$ = $3 > $5 ? $3 : $5; }
1510 | K_MIN '(' any_expr ',' any_expr ')'
1511 { $$ = $3 < $5 ? $3 : $5; }
1512 | INT '(' any_expr ')'
1513 { $$ = floor($3); }
1514 | RAND '(' any_expr ')'
1515 { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
1516 | RAND '(' ')'
1518 /* return a random number in the range [0,1) */
1519 /* portable, but not very random */
1520 $$ = (rand() & 0x7fff) / double(0x8000);
1522 | SRAND '(' any_expr ')'
1523 { $$ = 0; srand((unsigned int)$3); }
1524 | expr '<' expr
1525 { $$ = ($1 < $3); }
1526 | expr LESSEQUAL expr
1527 { $$ = ($1 <= $3); }
1528 | expr '>' expr
1529 { $$ = ($1 > $3); }
1530 | expr GREATEREQUAL expr
1531 { $$ = ($1 >= $3); }
1532 | expr EQUALEQUAL expr
1533 { $$ = ($1 == $3); }
1534 | expr NOTEQUAL expr
1535 { $$ = ($1 != $3); }
1536 | expr ANDAND expr
1537 { $$ = ($1 != 0.0 && $3 != 0.0); }
1538 | expr OROR expr
1539 { $$ = ($1 != 0.0 || $3 != 0.0); }
1540 | '!' expr
1541 { $$ = ($2 == 0.0); }
1547 /* bison defines const to be empty unless __STDC__ is defined, which it
1548 isn't under cfront */
1550 #ifdef const
1551 #undef const
1552 #endif
1554 static struct {
1555 const char *name;
1556 double val;
1557 int scaled; // non-zero if val should be multiplied by scale
1558 } defaults_table[] = {
1559 { "arcrad", .25, 1 },
1560 { "arrowht", .1, 1 },
1561 { "arrowwid", .05, 1 },
1562 { "circlerad", .25, 1 },
1563 { "boxht", .5, 1 },
1564 { "boxwid", .75, 1 },
1565 { "boxrad", 0.0, 1 },
1566 { "dashwid", .05, 1 },
1567 { "ellipseht", .5, 1 },
1568 { "ellipsewid", .75, 1 },
1569 { "moveht", .5, 1 },
1570 { "movewid", .5, 1 },
1571 { "lineht", .5, 1 },
1572 { "linewid", .5, 1 },
1573 { "textht", 0.0, 1 },
1574 { "textwid", 0.0, 1 },
1575 { "scale", 1.0, 0 },
1576 { "linethick", -1.0, 0 }, // in points
1577 { "fillval", .5, 0 },
1578 { "arrowhead", 1.0, 0 },
1579 { "maxpswid", 8.5, 0 },
1580 { "maxpsht", 11.0, 0 },
1583 place *lookup_label(const char *label)
1585 saved_state *state = current_saved_state;
1586 PTABLE(place) *tbl = current_table;
1587 for (;;) {
1588 place *pl = tbl->lookup(label);
1589 if (pl)
1590 return pl;
1591 if (!state)
1592 return 0;
1593 tbl = state->tbl;
1594 state = state->prev;
1598 void define_label(const char *label, const place *pl)
1600 place *p = new place;
1601 *p = *pl;
1602 current_table->define(label, p);
1605 int lookup_variable(const char *name, double *val)
1607 place *pl = lookup_label(name);
1608 if (pl) {
1609 *val = pl->x;
1610 return 1;
1612 return 0;
1615 void define_variable(const char *name, double val)
1617 place *p = new place;
1618 p->obj = 0;
1619 p->x = val;
1620 p->y = 0.0;
1621 current_table->define(name, p);
1622 if (strcmp(name, "scale") == 0) {
1623 // When the scale changes, reset all scaled pre-defined variables to
1624 // their default values.
1625 for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1626 if (defaults_table[i].scaled)
1627 define_variable(defaults_table[i].name, val*defaults_table[i].val);
1631 // called once only (not once per parse)
1633 void parse_init()
1635 current_direction = RIGHT_DIRECTION;
1636 current_position.x = 0.0;
1637 current_position.y = 0.0;
1638 // This resets everything to its default value.
1639 reset_all();
1642 void reset(const char *nm)
1644 for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1645 if (strcmp(nm, defaults_table[i].name) == 0) {
1646 double val = defaults_table[i].val;
1647 if (defaults_table[i].scaled) {
1648 double scale;
1649 lookup_variable("scale", &scale);
1650 val *= scale;
1652 define_variable(defaults_table[i].name, val);
1653 return;
1655 lex_error("`%1' is not a predefined variable", nm);
1658 void reset_all()
1660 // We only have to explicitly reset the pre-defined variables that
1661 // aren't scaled because `scale' is not scaled, and changing the
1662 // value of `scale' will reset all the pre-defined variables that
1663 // are scaled.
1664 for (int i = 0; i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1665 if (!defaults_table[i].scaled)
1666 define_variable(defaults_table[i].name, defaults_table[i].val);
1669 // called after each parse
1671 void parse_cleanup()
1673 while (current_saved_state != 0) {
1674 delete current_table;
1675 current_table = current_saved_state->tbl;
1676 saved_state *tem = current_saved_state;
1677 current_saved_state = current_saved_state->prev;
1678 delete tem;
1680 assert(current_table == &top_table);
1681 PTABLE_ITERATOR(place) iter(current_table);
1682 const char *key;
1683 place *pl;
1684 while (iter.next(&key, &pl))
1685 if (pl->obj != 0) {
1686 position pos = pl->obj->origin();
1687 pl->obj = 0;
1688 pl->x = pos.x;
1689 pl->y = pos.y;
1691 while (olist.head != 0) {
1692 object *tem = olist.head;
1693 olist.head = olist.head->next;
1694 delete tem;
1696 olist.tail = 0;
1697 current_direction = RIGHT_DIRECTION;
1698 current_position.x = 0.0;
1699 current_position.y = 0.0;
1702 const char *ordinal_postfix(int n)
1704 if (n < 10 || n > 20)
1705 switch (n % 10) {
1706 case 1:
1707 return "st";
1708 case 2:
1709 return "nd";
1710 case 3:
1711 return "rd";
1713 return "th";
1716 const char *object_type_name(object_type type)
1718 switch (type) {
1719 case BOX_OBJECT:
1720 return "box";
1721 case CIRCLE_OBJECT:
1722 return "circle";
1723 case ELLIPSE_OBJECT:
1724 return "ellipse";
1725 case ARC_OBJECT:
1726 return "arc";
1727 case SPLINE_OBJECT:
1728 return "spline";
1729 case LINE_OBJECT:
1730 return "line";
1731 case ARROW_OBJECT:
1732 return "arrow";
1733 case MOVE_OBJECT:
1734 return "move";
1735 case TEXT_OBJECT:
1736 return "\"\"";
1737 case BLOCK_OBJECT:
1738 return "[]";
1739 case OTHER_OBJECT:
1740 case MARK_OBJECT:
1741 default:
1742 break;
1744 return "object";
1747 static char sprintf_buf[1024];
1749 char *format_number(const char *form, double n)
1751 if (form == 0)
1752 form = "%g";
1753 else {
1754 // this is a fairly feeble attempt at validation of the format
1755 int nspecs = 0;
1756 for (const char *p = form; *p != '\0'; p++)
1757 if (*p == '%') {
1758 if (p[1] == '%')
1759 p++;
1760 else
1761 nspecs++;
1763 if (nspecs > 1) {
1764 lex_error("bad format `%1'", form);
1765 return strsave(form);
1768 sprintf(sprintf_buf, form, n);
1769 return strsave(sprintf_buf);
1772 char *do_sprintf(const char *form, const double *v, int nv)
1774 string result;
1775 int i = 0;
1776 string one_format;
1777 while (*form) {
1778 if (*form == '%') {
1779 one_format += *form++;
1780 for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
1781 one_format += *form;
1782 if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
1783 lex_error("bad sprintf format");
1784 result += one_format;
1785 result += form;
1786 break;
1788 if (*form == '%') {
1789 one_format += *form++;
1790 one_format += '\0';
1791 sprintf(sprintf_buf, one_format.contents());
1793 else {
1794 if (i >= nv) {
1795 lex_error("too few arguments to sprintf");
1796 result += one_format;
1797 result += form;
1798 break;
1800 one_format += *form++;
1801 one_format += '\0';
1802 sprintf(sprintf_buf, one_format.contents(), v[i++]);
1804 one_format.clear();
1805 result += sprintf_buf;
1807 else
1808 result += *form++;
1810 result += '\0';
1811 return strsave(result.contents());