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