2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 - 2007 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This 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
12 * This 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
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.
24 #include "pic-config.h"
30 extern
int delim_flag
;
31 extern
void copy_rest_thru
(const char *, const char *);
32 extern
void copy_file_thru
(const char *, const char *, const char *);
33 extern
void push_body
(const char *);
34 extern
void do_for
(char *var
, double from
, double to
,
35 int by_is_multiplicative
, double by
, char *body
);
36 extern
void do_lookahead
();
39 void yyerror(const char *);
41 void reset
(const char *nm
);
44 place
*lookup_label
(const char *);
45 void define_label
(const char *label
, const place
*pl
);
47 direction current_direction
;
48 position current_position
;
50 implement_ptable
(place
)
52 PTABLE
(place
) top_table
;
54 PTABLE
(place
) *current_table
= &top_table
;
55 saved_state
*current_saved_state
= 0;
59 const char *ordinal_postfix
(int n
);
60 const char *object_type_name
(object_type type
);
61 char *format_number
(const char *form
, double n
);
62 char *do_sprintf
(const char *form
, const double *v
, int nv
);
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
;
88 %token
<lstr
> COMMAND_LINE
89 %token
<str
> DELIMITED
92 %token LEFT_ARROW_HEAD
93 %token RIGHT_ARROW_HEAD
94 %token DOUBLE_ARROW_HEAD
210 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
214 /* give text adjustments higher precedence than TEXT, so that
215 box "foo" above ljust == box ("foo" above ljust)
218 %left LJUST RJUST ABOVE BELOW
221 /* Give attributes that take an optional expression a higher
222 precedence than left and right, so that eg `line chop left'
224 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
225 %left XSLANTED YSLANTED
228 %left VARIABLE NUMBER
'(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
229 %left ORDINAL HERE
'`'
231 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE
'['
233 /* these need to be lower than '-' */
234 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
236 /* these must have higher precedence than CHOP so that `label %prec CHOP'
238 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
239 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
240 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
245 %left EQUALEQUAL NOTEQUAL
246 %left
'<' '>' LESSEQUAL GREATEREQUAL
256 %type
<x
> expr any_expr text_expr
257 %type
<by
> optional_by
258 %type
<pair
> expr_pair position_not_place
259 %type
<if_data
> simple_if
260 %type
<obj
> nth_primitive
262 %type
<pth
> path label_path relative_path
263 %type
<pl
> place label element element_list middle_element_list
264 %type
<spec
> object_spec
265 %type
<pair
> position
266 %type
<obtype
> object_type
267 %type
<n
> optional_ordinal_last ordinal
268 %type
<str
> macro_name until
269 %type
<dv
> sprintf_args
270 %type
<lstr
> text print_args print_arg
279 print_picture
(olist.head
);
285 optional_separator middle_element_list optional_separator
292 | middle_element_list separator element
307 FIGNAME
'=' macro_name
310 graphname
= new
char[strlen
($3) + 1];
311 strcpy
(graphname
, $3);
315 VARIABLE
'=' any_expr
317 define_variable
($1, $3);
320 | VARIABLE
':' '=' any_expr
322 place
*p
= lookup_label
($1);
324 lex_error
("variable `%1' not defined", $1);
333 { current_direction
= UP_DIRECTION
; }
335 { current_direction
= DOWN_DIRECTION
; }
337 { current_direction
= LEFT_DIRECTION
; }
339 { current_direction
= RIGHT_DIRECTION
; }
342 olist.append
(make_command_object
($1.str
, $1.filename
,
347 olist.append
(make_command_object
($2.str
, $2.filename
,
352 fprintf
(stderr
, "%s\n", $2.str
);
362 lex_error
("unsafe to run command `%1'", $3);
372 // do not delete the filename
382 copy_file_thru
($2.str
, $5, $7);
383 // do not delete the filename
395 copy_rest_thru
($4, $6);
399 | FOR VARIABLE
'=' expr TO expr optional_by DO
406 do_for
($2, $4, $6, $7.is_multiplicative
, $7.val
, $10);
432 { define_variable
("scale", 1.0); }
446 | reset_variables VARIABLE
451 | reset_variables
',' VARIABLE
461 | print_args print_arg
463 $$.str
= new
char[strlen
($1.str
) + strlen
($2.str
) + 1];
464 strcpy
($$.str
, $1.str
);
465 strcat
($$.str
, $2.str
);
469 $$.filename
= $1.filename
;
470 $$.lineno
= $1.lineno
;
472 else if
($2.filename
) {
473 $$.filename
= $2.filename
;
474 $$.lineno
= $2.lineno
;
482 $$.str
= new
char[GDIGITS
+ 1];
483 sprintf
($$.str
, "%g", $1);
491 $$.str
= new
char[GDIGITS
+ 2 + GDIGITS
+ 1];
492 sprintf
($$.str
, "%g, %g", $1.x
, $1.y
);
526 $$
= strcmp
($1.str
, $3.str
) == 0;
532 $$
= strcmp
($1.str
, $3.str
) != 0;
536 | text_expr ANDAND text_expr
537 { $$
= ($1 != 0.0 && $3 != 0.0); }
538 | text_expr ANDAND expr
539 { $$
= ($1 != 0.0 && $3 != 0.0); }
540 | expr ANDAND text_expr
541 { $$
= ($1 != 0.0 && $3 != 0.0); }
542 | text_expr OROR text_expr
543 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
544 | text_expr OROR expr
545 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
546 | expr OROR text_expr
547 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
549 { $$
= ($2 == 0.0); }
557 $$.is_multiplicative
= 0;
562 $$.is_multiplicative
= 0;
567 $$.is_multiplicative
= 1;
574 $$.obj
= $1->make_object
(¤t_position
,
580 olist.append
($$.obj
);
582 $$.x
= current_position.x
;
583 $$.y
= current_position.y
;
586 | LABEL
':' optional_separator element
589 define_label
($1, & $$
);
592 | LABEL
':' optional_separator position_not_place
597 define_label
($1, & $$
);
600 | LABEL
':' optional_separator place
603 define_label
($1, & $$
);
608 $
<state
>$.x
= current_position.x
;
609 $
<state
>$.y
= current_position.y
;
610 $
<state
>$.dir
= current_direction
;
614 current_position.x
= $
<state
>2.x
;
615 current_position.y
= $
<state
>2.y
;
616 current_direction
= $
<state
>2.dir
;
625 $$.x
= current_position.x
;
626 $$.y
= current_position.y
;
639 { $$
= new object_spec
(BOX_OBJECT
); }
641 { $$
= new object_spec
(CIRCLE_OBJECT
); }
643 { $$
= new object_spec
(ELLIPSE_OBJECT
); }
646 $$
= new object_spec
(ARC_OBJECT
);
647 $$
->dir
= current_direction
;
651 $$
= new object_spec
(LINE_OBJECT
);
652 lookup_variable
("lineht", & $$
->segment_height
);
653 lookup_variable
("linewid", & $$
->segment_width
);
654 $$
->dir
= current_direction
;
658 $$
= new object_spec
(ARROW_OBJECT
);
659 lookup_variable
("lineht", & $$
->segment_height
);
660 lookup_variable
("linewid", & $$
->segment_width
);
661 $$
->dir
= current_direction
;
665 $$
= new object_spec
(MOVE_OBJECT
);
666 lookup_variable
("moveht", & $$
->segment_height
);
667 lookup_variable
("movewid", & $$
->segment_width
);
668 $$
->dir
= current_direction
;
672 $$
= new object_spec
(SPLINE_OBJECT
);
673 lookup_variable
("lineht", & $$
->segment_height
);
674 lookup_variable
("linewid", & $$
->segment_width
);
675 $$
->dir
= current_direction
;
679 $$
= new object_spec
(TEXT_OBJECT
);
680 $$
->text
= new text_item
($1.str
, $1.filename
, $1.lineno
);
684 $$
= new object_spec
(TEXT_OBJECT
);
685 $$
->text
= new text_item
(format_number
(0, $2), 0, -1);
689 $$
= new object_spec
(TEXT_OBJECT
);
690 $$
->text
= new text_item
(format_number
($3.str
, $2),
691 $3.filename
, $3.lineno
);
696 saved_state
*p
= new saved_state
;
698 p
->x
= current_position.x
;
699 p
->y
= current_position.y
;
700 p
->dir
= current_direction
;
701 p
->tbl
= current_table
;
702 p
->prev
= current_saved_state
;
703 current_position.x
= 0.0;
704 current_position.y
= 0.0;
705 current_table
= new PTABLE
(place
);
706 current_saved_state
= p
;
707 olist.append
(make_mark_object
());
711 current_position.x
= $
<pstate
>2->x
;
712 current_position.y
= $
<pstate
>2->y
;
713 current_direction
= $
<pstate
>2->dir
;
714 $$
= new object_spec
(BLOCK_OBJECT
);
715 olist.wrap_up_block
(& $$
->oblist
);
716 $$
->tbl
= current_table
;
717 current_table
= $
<pstate
>2->tbl
;
718 current_saved_state
= $
<pstate
>2->prev
;
721 | object_spec HEIGHT expr
725 $$
->flags |
= HAS_HEIGHT
;
727 | object_spec RADIUS expr
731 $$
->flags |
= HAS_RADIUS
;
733 | object_spec WIDTH expr
737 $$
->flags |
= HAS_WIDTH
;
739 | object_spec DIAMETER expr
743 $$
->flags |
= HAS_RADIUS
;
745 | object_spec expr %prec HEIGHT
748 $$
->flags |
= HAS_SEGMENT
;
751 $$
->segment_pos.y
+= $2;
754 $$
->segment_pos.y
-= $2;
756 case RIGHT_DIRECTION
:
757 $$
->segment_pos.x
+= $2;
760 $$
->segment_pos.x
-= $2;
767 $$
->dir
= UP_DIRECTION
;
768 $$
->flags |
= HAS_SEGMENT
;
769 $$
->segment_pos.y
+= $$
->segment_height
;
771 | object_spec UP expr
774 $$
->dir
= UP_DIRECTION
;
775 $$
->flags |
= HAS_SEGMENT
;
776 $$
->segment_pos.y
+= $3;
781 $$
->dir
= DOWN_DIRECTION
;
782 $$
->flags |
= HAS_SEGMENT
;
783 $$
->segment_pos.y
-= $$
->segment_height
;
785 | object_spec DOWN expr
788 $$
->dir
= DOWN_DIRECTION
;
789 $$
->flags |
= HAS_SEGMENT
;
790 $$
->segment_pos.y
-= $3;
795 $$
->dir
= RIGHT_DIRECTION
;
796 $$
->flags |
= HAS_SEGMENT
;
797 $$
->segment_pos.x
+= $$
->segment_width
;
799 | object_spec RIGHT expr
802 $$
->dir
= RIGHT_DIRECTION
;
803 $$
->flags |
= HAS_SEGMENT
;
804 $$
->segment_pos.x
+= $3;
809 $$
->dir
= LEFT_DIRECTION
;
810 $$
->flags |
= HAS_SEGMENT
;
811 $$
->segment_pos.x
-= $$
->segment_width
;
813 | object_spec LEFT expr
816 $$
->dir
= LEFT_DIRECTION
;
817 $$
->flags |
= HAS_SEGMENT
;
818 $$
->segment_pos.x
-= $3;
820 | object_spec FROM position
823 $$
->flags |
= HAS_FROM
;
827 | object_spec TO position
830 if
($$
->flags
& HAS_SEGMENT
)
831 $$
->segment_list
= new segment
($$
->segment_pos
,
832 $$
->segment_is_absolute
,
834 $$
->flags |
= HAS_SEGMENT
;
835 $$
->segment_pos.x
= $3.x
;
836 $$
->segment_pos.y
= $3.y
;
837 $$
->segment_is_absolute
= 1;
842 | object_spec AT position
848 if
($$
->type
!= ARC_OBJECT
) {
849 $$
->flags |
= HAS_FROM
;
854 | object_spec WITH path
857 $$
->flags |
= HAS_WITH
;
860 | object_spec WITH position %prec
','
863 $$
->flags |
= HAS_WITH
;
867 $$
->with
= new path
(pos
);
869 | object_spec BY expr_pair
872 $$
->flags |
= HAS_SEGMENT
;
873 $$
->segment_pos.x
+= $3.x
;
874 $$
->segment_pos.y
+= $3.y
;
879 if
(!($$
->flags
& HAS_SEGMENT
))
882 $$
->segment_pos.y
+= $$
->segment_width
;
885 $$
->segment_pos.y
-= $$
->segment_width
;
887 case RIGHT_DIRECTION
:
888 $$
->segment_pos.x
+= $$
->segment_width
;
891 $$
->segment_pos.x
-= $$
->segment_width
;
894 $$
->segment_list
= new segment
($$
->segment_pos
,
895 $$
->segment_is_absolute
,
897 $$
->flags
&= ~HAS_SEGMENT
;
898 $$
->segment_pos.x
= $$
->segment_pos.y
= 0.0;
899 $$
->segment_is_absolute
= 0;
908 $$
->flags |
= IS_DOTTED
;
909 lookup_variable
("dashwid", & $$
->dash_width
);
911 | object_spec DOTTED expr
914 $$
->flags |
= IS_DOTTED
;
920 $$
->flags |
= IS_DASHED
;
921 lookup_variable
("dashwid", & $$
->dash_width
);
923 | object_spec DASHED expr
926 $$
->flags |
= IS_DASHED
;
932 $$
->flags |
= IS_DEFAULT_FILLED
;
934 | object_spec FILL expr
937 $$
->flags |
= IS_FILLED
;
940 | object_spec XSLANTED expr
943 $$
->flags |
= IS_XSLANTED
;
946 | object_spec YSLANTED expr
949 $$
->flags |
= IS_YSLANTED
;
952 | object_spec SHADED text
955 $$
->flags |
= (IS_SHADED | IS_FILLED
);
956 $$
->shaded
= new
char[strlen
($3.str
)+1];
957 strcpy
($$
->shaded
, $3.str
);
959 | object_spec COLORED text
962 $$
->flags |
= (IS_SHADED | IS_OUTLINED | IS_FILLED
);
963 $$
->shaded
= new
char[strlen
($3.str
)+1];
964 strcpy
($$
->shaded
, $3.str
);
965 $$
->outlined
= new
char[strlen
($3.str
)+1];
966 strcpy
($$
->outlined
, $3.str
);
968 | object_spec OUTLINED text
971 $$
->flags |
= IS_OUTLINED
;
972 $$
->outlined
= new
char[strlen
($3.str
)+1];
973 strcpy
($$
->outlined
, $3.str
);
978 // line chop chop means line chop 0 chop 0
979 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
980 $$
->flags |
= IS_CHOPPED
;
981 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
982 $$
->start_chop
= $$
->end_chop
= 0.0;
984 else if
($$
->flags
& IS_CHOPPED
) {
988 $$
->flags |
= IS_DEFAULT_CHOPPED
;
991 | object_spec CHOP expr
994 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
995 $$
->flags |
= IS_CHOPPED
;
996 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
997 $$
->start_chop
= 0.0;
1000 else if
($$
->flags
& IS_CHOPPED
) {
1004 $$
->start_chop
= $$
->end_chop
= $3;
1005 $$
->flags |
= IS_CHOPPED
;
1011 $$
->flags |
= IS_SAME
;
1013 | object_spec INVISIBLE
1016 $$
->flags |
= IS_INVISIBLE
;
1018 | object_spec LEFT_ARROW_HEAD
1021 $$
->flags |
= HAS_LEFT_ARROW_HEAD
;
1023 | object_spec RIGHT_ARROW_HEAD
1026 $$
->flags |
= HAS_RIGHT_ARROW_HEAD
;
1028 | object_spec DOUBLE_ARROW_HEAD
1031 $$
->flags |
= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD
);
1036 $$
->flags |
= IS_CLOCKWISE
;
1041 $$
->flags
&= ~IS_CLOCKWISE
;
1043 | object_spec text %prec TEXT
1047 for
(p
= & $$
->text
; *p
; p
= &(*p
)->next
)
1049 *p
= new text_item
($2.str
, $2.filename
, $2.lineno
);
1056 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1058 p
->adj.h
= LEFT_ADJUST
;
1066 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1068 p
->adj.h
= RIGHT_ADJUST
;
1076 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1078 p
->adj.v
= ABOVE_ADJUST
;
1086 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1088 p
->adj.v
= BELOW_ADJUST
;
1091 | object_spec THICKNESS expr
1094 $$
->flags |
= HAS_THICKNESS
;
1097 | object_spec ALIGNED
1100 $$
->flags |
= IS_ALIGNED
;
1107 | SPRINTF
'(' TEXT sprintf_args
')'
1109 $$.filename
= $3.filename
;
1110 $$.lineno
= $3.lineno
;
1111 $$.str
= do_sprintf
($3.str
, $4.v
, $4.nv
);
1124 | sprintf_args
',' expr
1127 if
($$.nv
>= $$.maxv
) {
1129 $$.v
= new
double[4];
1133 double *oldv
= $$.v
;
1136 $$.v
= new
double[$$.maxv
];
1137 memcpy
($$.v
, oldv
, $$.nv
*sizeof
(double));
1139 // workaround for bug in Compaq C++ V6.5-033
1140 // for Compaq Tru64 UNIX V5.1A (Rev. 1885)
1141 double *foo
= new
double[$$.maxv
];
1142 memcpy
(foo
, oldv
, $$.nv
*sizeof
(double));
1173 | position
'+' expr_pair
1178 |
'(' position
'+' expr_pair
')'
1183 | position
'-' expr_pair
1188 |
'(' position
'-' expr_pair
')'
1193 |
'(' position
',' position
')'
1198 | expr between position AND position
1200 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1201 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1203 |
'(' expr between position AND position
')'
1205 $$.x
= (1.0 - $2)*$4.x
+ $2*$6.x
;
1206 $$.y
= (1.0 - $2)*$4.y
+ $2*$6.y
;
1208 | expr
'<' position
',' position
'>'
1210 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1211 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1213 |
'(' expr
'<' position
',' position
'>' ')'
1215 $$.x
= (1.0 - $2)*$4.x
+ $2*$6.x
;
1216 $$.y
= (1.0 - $2)*$4.y
+ $2*$6.y
;
1222 | OF THE WAY BETWEEN
1236 /* line at A left == line (at A) left */
1242 if
(!pth.follow
($1, & $$
))
1248 if
(!pth.follow
($2, & $$
))
1254 if
(!pth.follow
($3, & $$
))
1259 $$.x
= current_position.x
;
1260 $$.y
= current_position.y
;
1268 place
*p
= lookup_label
($1);
1270 lex_error
("there is no place `%1'", $1);
1281 if
(!pth.follow
($1, & $$
))
1291 // XXX Check for overflow (and non-integers?).
1296 optional_ordinal_last:
1308 for
(p
= olist.head
; p
!= 0; p
= p
->next
)
1309 if
(p
->type
() == $2 && ++count
== $1) {
1314 lex_error
("there is no %1%2 %3", $1, ordinal_postfix
($1),
1315 object_type_name
($2));
1319 | optional_ordinal_last object_type
1323 for
(p
= olist.tail
; p
!= 0; p
= p
->prev
)
1324 if
(p
->type
() == $2 && ++count
== $1) {
1329 lex_error
("there is no %1%2 last %3", $1,
1330 ordinal_postfix
($1), object_type_name
($2));
1338 { $$
= BOX_OBJECT
; }
1340 { $$
= CIRCLE_OBJECT
; }
1342 { $$
= ELLIPSE_OBJECT
; }
1344 { $$
= ARC_OBJECT
; }
1346 { $$
= LINE_OBJECT
; }
1348 { $$
= ARROW_OBJECT
; }
1350 { $$
= SPLINE_OBJECT
; }
1352 { $$
= BLOCK_OBJECT
; }
1354 { $$
= TEXT_OBJECT
; }
1359 { $$
= new path
($2); }
1360 | label_path
'.' LABEL
1369 { $$
= new path
($1); }
1370 /* give this a lower precedence than LEFT and RIGHT so that
1371 [A: box] with .A left == [A: box] with (.A left) */
1372 | label_path %prec TEXT
1384 |
'(' relative_path
',' relative_path
')'
1389 /* The rest of these rules are a compatibility sop. */
1390 | ORDINAL LAST object_type relative_path
1392 lex_warning
("`%1%2 last %3' in `with' argument ignored",
1393 $1, ordinal_postfix
($1), object_type_name
($3));
1396 | LAST object_type relative_path
1398 lex_warning
("`last %1' in `with' argument ignored",
1399 object_type_name
($2));
1402 | ORDINAL object_type relative_path
1404 lex_warning
("`%1%2 %3' in `with' argument ignored",
1405 $1, ordinal_postfix
($1), object_type_name
($2));
1408 | LABEL relative_path
1410 lex_warning
("initial `%1' in `with' argument ignored", $1);
1418 { $$
= &object
::north
; }
1420 { $$
= &object
::east
; }
1422 { $$
= &object
::west
; }
1424 { $$
= &object
::south
; }
1426 { $$
= &object
::north_east
; }
1428 { $$
= &object
:: south_east
; }
1430 { $$
= &object
::north_west
; }
1432 { $$
= &object
::south_west
; }
1434 { $$
= &object
::center
; }
1436 { $$
= &object
::start
; }
1438 { $$
= &object
::end
; }
1440 { $$
= &object
::north
; }
1442 { $$
= &object
::south
; }
1444 { $$
= &object
::west
; }
1446 { $$
= &object
::east
; }
1448 { $$
= &object
::north_west
; }
1450 { $$
= &object
::south_west
; }
1452 { $$
= &object
::north_east
; }
1454 { $$
= &object
::south_east
; }
1456 { $$
= &object
::west
; }
1458 { $$
= &object
::east
; }
1460 { $$
= &object
::north_west
; }
1462 { $$
= &object
::south_west
; }
1463 | UPPER RIGHT_CORNER
1464 { $$
= &object
::north_east
; }
1465 | LOWER RIGHT_CORNER
1466 { $$
= &object
::south_east
; }
1468 { $$
= &object
::north
; }
1470 { $$
= &object
::south
; }
1472 { $$
= &object
::east
; }
1474 { $$
= &object
::west
; }
1476 { $$
= &object
::center
; }
1478 { $$
= &object
::start
; }
1480 { $$
= &object
::end
; }
1486 if
(!lookup_variable
($1, & $$
)) {
1487 lex_error
("there is no variable `%1'", $1);
1497 $$
= $1.obj
->origin
().x
;
1504 $$
= $1.obj
->origin
().y
;
1511 $$
= $1.obj
->height
();
1518 $$
= $1.obj
->width
();
1525 $$
= $1.obj
->radius
();
1538 lex_error
("division by zero");
1546 lex_error
("modulus by zero");
1555 if
(errno
== EDOM
) {
1556 lex_error
("arguments to `^' operator out of domain");
1559 if
(errno
== ERANGE
) {
1560 lex_error
("result of `^' operator out of range");
1564 |
'-' expr %prec
'!'
1568 | SIN
'(' any_expr
')'
1572 if
(errno
== ERANGE
) {
1573 lex_error
("sin result out of range");
1577 | COS
'(' any_expr
')'
1581 if
(errno
== ERANGE
) {
1582 lex_error
("cos result out of range");
1586 | ATAN2
'(' any_expr
',' any_expr
')'
1590 if
(errno
== EDOM
) {
1591 lex_error
("atan2 argument out of domain");
1594 if
(errno
== ERANGE
) {
1595 lex_error
("atan2 result out of range");
1599 | LOG
'(' any_expr
')'
1603 if
(errno
== ERANGE
) {
1604 lex_error
("log result out of range");
1608 | EXP
'(' any_expr
')'
1612 if
(errno
== ERANGE
) {
1613 lex_error
("exp result out of range");
1617 | SQRT
'(' any_expr
')'
1621 if
(errno
== EDOM
) {
1622 lex_error
("sqrt argument out of domain");
1626 | K_MAX
'(' any_expr
',' any_expr
')'
1627 { $$
= $3 > $5 ?
$3 : $5; }
1628 | K_MIN
'(' any_expr
',' any_expr
')'
1629 { $$
= $3 < $5 ?
$3 : $5; }
1630 | INT
'(' any_expr
')'
1631 { $$
= $3 < 0 ?
-floor
(-$3) : floor
($3); }
1632 | RAND
'(' any_expr
')'
1633 { $$
= 1.0 + floor
(((rand
()&0x7fff)/double(0x7fff))*$3); }
1636 /* return a random number in the range [0,1) */
1637 /* portable, but not very random */
1638 $$
= (rand
() & 0x7fff) / double(0x8000);
1640 | SRAND
'(' any_expr
')'
1643 srand
((unsigned int)$3);
1647 | expr LESSEQUAL expr
1648 { $$
= ($1 <= $3); }
1651 | expr GREATEREQUAL expr
1652 { $$
= ($1 >= $3); }
1653 | expr EQUALEQUAL expr
1654 { $$
= ($1 == $3); }
1655 | expr NOTEQUAL expr
1656 { $$
= ($1 != $3); }
1658 { $$
= ($1 != 0.0 && $3 != 0.0); }
1660 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
1662 { $$
= ($2 == 0.0); }
1668 /* bison defines const to be empty unless __STDC__ is defined, which it
1669 isn't under cfront */
1678 int scaled
; // non-zero if val should be multiplied by scale
1679 } /* FIXME const*/defaults_table
[] = {
1680 { "arcrad", .25, 1 },
1681 { "arrowht", .1, 1 },
1682 { "arrowwid", .05, 1 },
1683 { "circlerad", .25, 1 },
1685 { "boxwid", .75, 1 },
1686 { "boxrad", 0.0, 1 },
1687 { "dashwid", .05, 1 },
1688 { "ellipseht", .5, 1 },
1689 { "ellipsewid", .75, 1 },
1690 { "moveht", .5, 1 },
1691 { "movewid", .5, 1 },
1692 { "lineht", .5, 1 },
1693 { "linewid", .5, 1 },
1694 { "textht", 0.0, 1 },
1695 { "textwid", 0.0, 1 },
1696 { "scale", 1.0, 0 },
1697 { "linethick", -1.0, 0 }, // in points
1698 { "fillval", .5, 0 },
1699 { "arrowhead", 1.0, 0 },
1700 { "maxpswid", 8.5, 0 },
1701 { "maxpsht", 11.0, 0 },
1704 place
*lookup_label
(const char *label
)
1706 saved_state
*state
= current_saved_state
;
1707 PTABLE
(place
) *tbl
= current_table
;
1709 place
*pl
= tbl
->lookup
(label
);
1715 state
= state
->prev
;
1719 void define_label
(const char *label
, const place
*pl
)
1721 place
*p
= new place
[1];
1723 current_table
->define
(label
, p
);
1726 int lookup_variable
(const char *name
, double *val
)
1728 place
*pl
= lookup_label
(name
);
1736 void define_variable
(const char *name
, double val
)
1738 place
*p
= new place
[1];
1742 current_table
->define
(name
, p
);
1743 if
(strcmp
(name
, "scale") == 0) {
1744 // When the scale changes, reset all scaled pre-defined variables to
1745 // their default values.
1746 for
(unsigned int i
= 0;
1747 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1748 if
(defaults_table
[i
].scaled
)
1749 define_variable
(defaults_table
[i
].name
, val
*defaults_table
[i
].val
);
1753 // called once only (not once per parse)
1757 current_direction
= RIGHT_DIRECTION
;
1758 current_position.x
= 0.0;
1759 current_position.y
= 0.0;
1760 // This resets everything to its default value.
1764 void reset
(const char *nm
)
1766 for
(unsigned int i
= 0;
1767 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1768 if
(strcmp
(nm
, defaults_table
[i
].name
) == 0) {
1769 double val
= defaults_table
[i
].val
;
1770 if
(defaults_table
[i
].scaled
) {
1772 lookup_variable
("scale", &scale
);
1775 define_variable
(defaults_table
[i
].name
, val
);
1778 lex_error
("`%1' is not a predefined variable", nm
);
1783 // We only have to explicitly reset the pre-defined variables that
1784 // aren't scaled because `scale' is not scaled, and changing the
1785 // value of `scale' will reset all the pre-defined variables that
1787 for
(unsigned int i
= 0;
1788 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1789 if
(!defaults_table
[i
].scaled
)
1790 define_variable
(defaults_table
[i
].name
, defaults_table
[i
].val
);
1793 // called after each parse
1795 void parse_cleanup
()
1797 while
(current_saved_state
!= 0) {
1798 delete current_table
;
1799 current_table
= current_saved_state
->tbl
;
1800 saved_state
*tem
= current_saved_state
;
1801 current_saved_state
= current_saved_state
->prev
;
1804 assert
(current_table
== &top_table
);
1805 PTABLE_ITERATOR
(place
) iter
(current_table
);
1808 while
(iter.next
(&key
, &pl
))
1810 position pos
= pl
->obj
->origin
();
1815 while
(olist.head
!= 0) {
1816 object
*tem
= olist.head
;
1817 olist.head
= olist.head
->next
;
1821 current_direction
= RIGHT_DIRECTION
;
1822 current_position.x
= 0.0;
1823 current_position.y
= 0.0;
1826 const char *ordinal_postfix
(int n
)
1828 if
(n
< 10 || n
> 20)
1840 const char *object_type_name
(object_type type
)
1847 case ELLIPSE_OBJECT
:
1871 static char sprintf_buf
[1024];
1873 char *format_number
(const char *form
, double n
)
1877 return do_sprintf
(form
, &n
, 1);
1880 char *do_sprintf
(const char *form
, const double *v
, int nv
)
1887 one_format
+= *form
++;
1888 for
(; *form
!= '\0' && strchr
("#-+ 0123456789.", *form
) != 0; form
++)
1889 one_format
+= *form
;
1890 if
(*form
== '\0' || strchr
("eEfgG%", *form
) == 0) {
1891 lex_error
("bad sprintf format");
1892 result
+= one_format
;
1897 one_format
+= *form
++;
1899 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1900 "%s", one_format.contents
());
1904 lex_error
("too few arguments to snprintf");
1905 result
+= one_format
;
1909 one_format
+= *form
++;
1911 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1912 one_format.contents
(), v
[i
++]);
1915 result
+= sprintf_buf
;
1921 return strsave
(result.contents
());