1 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
2 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25 extern
int delim_flag
;
26 extern
void copy_rest_thru
(const char *, const char *);
27 extern
void copy_file_thru
(const char *, const char *, const char *);
28 extern
void push_body
(const char *);
29 extern
void do_for
(char *var
, double from
, double to
,
30 int by_is_multiplicative
, double by
, char *body
);
31 extern
void do_lookahead
();
33 /* Maximum number of characters produced by printf("%g") */
37 void yyerror(const char *);
39 void reset
(const char *nm
);
42 place
*lookup_label
(const char *);
43 void define_label
(const char *label
, const place
*pl
);
45 direction current_direction
;
46 position current_position
;
48 implement_ptable
(place
)
50 PTABLE
(place
) top_table
;
52 PTABLE
(place
) *current_table
= &top_table
;
53 saved_state
*current_saved_state
= 0;
57 const char *ordinal_postfix
(int n
);
58 const char *object_type_name
(object_type type
);
59 char *format_number
(const char *form
, double n
);
60 char *do_sprintf
(const char *form
, const double *v
, int nv
);
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
212 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
216 /* give text adjustments higher precedence than TEXT, so that
217 box "foo" above ljust == box ("foo" above ljust)
220 %left LJUST RJUST ABOVE BELOW
223 /* Give attributes that take an optional expression a higher
224 precedence than left and right, so that eg `line chop left'
226 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
229 %left VARIABLE NUMBER
'(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
230 %left ORDINAL HERE
'`'
232 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE
'['
234 /* these need to be lower than '-' */
235 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
237 /* these must have higher precedence than CHOP so that `label %prec CHOP'
239 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
240 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
241 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
246 %left EQUALEQUAL NOTEQUAL
247 %left
'<' '>' LESSEQUAL GREATEREQUAL
257 %type
<x
> expr any_expr text_expr
258 %type
<by
> optional_by
259 %type
<pair
> expr_pair position_not_place
260 %type
<if_data
> simple_if
261 %type
<obj
> nth_primitive
263 %type
<pth
> path label_path relative_path
264 %type
<pl
> place label element element_list middle_element_list
265 %type
<spec
> object_spec
266 %type
<pair
> position
267 %type
<obtype
> object_type
268 %type
<n
> optional_ordinal_last ordinal
269 %type
<str
> macro_name until
270 %type
<dv
> sprintf_args
271 %type
<lstr
> text print_args print_arg
280 print_picture
(olist.head
);
286 optional_separator middle_element_list optional_separator
293 | middle_element_list separator element
308 FIGNAME
'=' macro_name
311 graphname
= new
char[strlen
($3) + 1];
312 strcpy
(graphname
, $3);
316 VARIABLE
'=' any_expr
318 define_variable
($1, $3);
321 | VARIABLE
':' '=' any_expr
323 place
*p
= lookup_label
($1);
325 lex_error
("variable `%1' not defined", $1);
334 { current_direction
= UP_DIRECTION
; }
336 { current_direction
= DOWN_DIRECTION
; }
338 { current_direction
= LEFT_DIRECTION
; }
340 { current_direction
= RIGHT_DIRECTION
; }
343 olist.append
(make_command_object
($1.str
, $1.filename
,
348 olist.append
(make_command_object
($2.str
, $2.filename
,
353 fprintf
(stderr
, "%s\n", $2.str
);
363 lex_error
("unsafe to run command `%1'", $3);
373 // do not delete the filename
383 copy_file_thru
($2.str
, $5, $7);
384 // do not delete the filename
396 copy_rest_thru
($4, $6);
400 | FOR VARIABLE
'=' expr TO expr optional_by DO
407 do_for
($2, $4, $6, $7.is_multiplicative
, $7.val
, $10);
433 { define_variable
("scale", 1.0); }
447 | reset_variables VARIABLE
452 | reset_variables
',' VARIABLE
462 | print_args print_arg
464 $$.str
= new
char[strlen
($1.str
) + strlen
($2.str
) + 1];
465 strcpy
($$.str
, $1.str
);
466 strcat
($$.str
, $2.str
);
470 $$.filename
= $1.filename
;
471 $$.lineno
= $1.lineno
;
473 else if
($2.filename
) {
474 $$.filename
= $2.filename
;
475 $$.lineno
= $2.lineno
;
483 $$.str
= new
char[GDIGITS
+ 1];
484 sprintf
($$.str
, "%g", $1);
492 $$.str
= new
char[GDIGITS
+ 2 + GDIGITS
+ 1];
493 sprintf
($$.str
, "%g, %g", $1.x
, $1.y
);
527 $$
= strcmp
($1.str
, $3.str
) == 0;
533 $$
= strcmp
($1.str
, $3.str
) != 0;
537 | text_expr ANDAND text_expr
538 { $$
= ($1 != 0.0 && $3 != 0.0); }
539 | text_expr ANDAND expr
540 { $$
= ($1 != 0.0 && $3 != 0.0); }
541 | expr ANDAND text_expr
542 { $$
= ($1 != 0.0 && $3 != 0.0); }
543 | text_expr OROR text_expr
544 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
545 | text_expr OROR expr
546 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
547 | expr OROR text_expr
548 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
550 { $$
= ($2 == 0.0); }
558 $$.is_multiplicative
= 0;
563 $$.is_multiplicative
= 0;
568 $$.is_multiplicative
= 1;
575 $$.obj
= $1->make_object
(¤t_position
,
581 olist.append
($$.obj
);
583 $$.x
= current_position.x
;
584 $$.y
= current_position.y
;
587 | LABEL
':' optional_separator element
590 define_label
($1, & $$
);
593 | LABEL
':' optional_separator position_not_place
598 define_label
($1, & $$
);
601 | LABEL
':' optional_separator place
604 define_label
($1, & $$
);
609 $
<state
>$.x
= current_position.x
;
610 $
<state
>$.y
= current_position.y
;
611 $
<state
>$.dir
= current_direction
;
615 current_position.x
= $
<state
>2.x
;
616 current_position.y
= $
<state
>2.y
;
617 current_direction
= $
<state
>2.dir
;
626 $$.x
= current_position.x
;
627 $$.y
= current_position.y
;
640 { $$
= new object_spec
(BOX_OBJECT
); }
642 { $$
= new object_spec
(CIRCLE_OBJECT
); }
644 { $$
= new object_spec
(ELLIPSE_OBJECT
); }
647 $$
= new object_spec
(ARC_OBJECT
);
648 $$
->dir
= current_direction
;
652 $$
= new object_spec
(LINE_OBJECT
);
653 lookup_variable
("lineht", & $$
->segment_height
);
654 lookup_variable
("linewid", & $$
->segment_width
);
655 $$
->dir
= current_direction
;
659 $$
= new object_spec
(ARROW_OBJECT
);
660 lookup_variable
("lineht", & $$
->segment_height
);
661 lookup_variable
("linewid", & $$
->segment_width
);
662 $$
->dir
= current_direction
;
666 $$
= new object_spec
(MOVE_OBJECT
);
667 lookup_variable
("moveht", & $$
->segment_height
);
668 lookup_variable
("movewid", & $$
->segment_width
);
669 $$
->dir
= current_direction
;
673 $$
= new object_spec
(SPLINE_OBJECT
);
674 lookup_variable
("lineht", & $$
->segment_height
);
675 lookup_variable
("linewid", & $$
->segment_width
);
676 $$
->dir
= current_direction
;
680 $$
= new object_spec
(TEXT_OBJECT
);
681 $$
->text
= new text_item
($1.str
, $1.filename
, $1.lineno
);
685 $$
= new object_spec
(TEXT_OBJECT
);
686 $$
->text
= new text_item
(format_number
(0, $2), 0, -1);
690 $$
= new object_spec
(TEXT_OBJECT
);
691 $$
->text
= new text_item
(format_number
($3.str
, $2),
692 $3.filename
, $3.lineno
);
697 saved_state
*p
= new saved_state
;
699 p
->x
= current_position.x
;
700 p
->y
= current_position.y
;
701 p
->dir
= current_direction
;
702 p
->tbl
= current_table
;
703 p
->prev
= current_saved_state
;
704 current_position.x
= 0.0;
705 current_position.y
= 0.0;
706 current_table
= new PTABLE
(place
);
707 current_saved_state
= p
;
708 olist.append
(make_mark_object
());
712 current_position.x
= $
<pstate
>2->x
;
713 current_position.y
= $
<pstate
>2->y
;
714 current_direction
= $
<pstate
>2->dir
;
715 $$
= new object_spec
(BLOCK_OBJECT
);
716 olist.wrap_up_block
(& $$
->oblist
);
717 $$
->tbl
= current_table
;
718 current_table
= $
<pstate
>2->tbl
;
719 current_saved_state
= $
<pstate
>2->prev
;
722 | object_spec HEIGHT expr
726 $$
->flags |
= HAS_HEIGHT
;
728 | object_spec RADIUS expr
732 $$
->flags |
= HAS_RADIUS
;
734 | object_spec WIDTH expr
738 $$
->flags |
= HAS_WIDTH
;
740 | object_spec DIAMETER expr
744 $$
->flags |
= HAS_RADIUS
;
746 | object_spec expr %prec HEIGHT
749 $$
->flags |
= HAS_SEGMENT
;
752 $$
->segment_pos.y
+= $2;
755 $$
->segment_pos.y
-= $2;
757 case RIGHT_DIRECTION
:
758 $$
->segment_pos.x
+= $2;
761 $$
->segment_pos.x
-= $2;
768 $$
->dir
= UP_DIRECTION
;
769 $$
->flags |
= HAS_SEGMENT
;
770 $$
->segment_pos.y
+= $$
->segment_height
;
772 | object_spec UP expr
775 $$
->dir
= UP_DIRECTION
;
776 $$
->flags |
= HAS_SEGMENT
;
777 $$
->segment_pos.y
+= $3;
782 $$
->dir
= DOWN_DIRECTION
;
783 $$
->flags |
= HAS_SEGMENT
;
784 $$
->segment_pos.y
-= $$
->segment_height
;
786 | object_spec DOWN expr
789 $$
->dir
= DOWN_DIRECTION
;
790 $$
->flags |
= HAS_SEGMENT
;
791 $$
->segment_pos.y
-= $3;
796 $$
->dir
= RIGHT_DIRECTION
;
797 $$
->flags |
= HAS_SEGMENT
;
798 $$
->segment_pos.x
+= $$
->segment_width
;
800 | object_spec RIGHT expr
803 $$
->dir
= RIGHT_DIRECTION
;
804 $$
->flags |
= HAS_SEGMENT
;
805 $$
->segment_pos.x
+= $3;
810 $$
->dir
= LEFT_DIRECTION
;
811 $$
->flags |
= HAS_SEGMENT
;
812 $$
->segment_pos.x
-= $$
->segment_width
;
814 | object_spec LEFT expr
817 $$
->dir
= LEFT_DIRECTION
;
818 $$
->flags |
= HAS_SEGMENT
;
819 $$
->segment_pos.x
-= $3;
821 | object_spec FROM position
824 $$
->flags |
= HAS_FROM
;
828 | object_spec TO position
831 if
($$
->flags
& HAS_SEGMENT
)
832 $$
->segment_list
= new segment
($$
->segment_pos
,
833 $$
->segment_is_absolute
,
835 $$
->flags |
= HAS_SEGMENT
;
836 $$
->segment_pos.x
= $3.x
;
837 $$
->segment_pos.y
= $3.y
;
838 $$
->segment_is_absolute
= 1;
843 | object_spec AT position
849 if
($$
->type
!= ARC_OBJECT
) {
850 $$
->flags |
= HAS_FROM
;
855 | object_spec WITH path
858 $$
->flags |
= HAS_WITH
;
861 | object_spec WITH position %prec
','
864 $$
->flags |
= HAS_WITH
;
868 $$
->with
= new path
(pos
);
870 | object_spec BY expr_pair
873 $$
->flags |
= HAS_SEGMENT
;
874 $$
->segment_pos.x
+= $3.x
;
875 $$
->segment_pos.y
+= $3.y
;
880 if
($$
->flags
& HAS_SEGMENT
) {
881 $$
->segment_list
= new segment
($$
->segment_pos
,
882 $$
->segment_is_absolute
,
884 $$
->flags
&= ~HAS_SEGMENT
;
885 $$
->segment_pos.x
= $$
->segment_pos.y
= 0.0;
886 $$
->segment_is_absolute
= 0;
896 $$
->flags |
= IS_DOTTED
;
897 lookup_variable
("dashwid", & $$
->dash_width
);
899 | object_spec DOTTED expr
902 $$
->flags |
= IS_DOTTED
;
908 $$
->flags |
= IS_DASHED
;
909 lookup_variable
("dashwid", & $$
->dash_width
);
911 | object_spec DASHED expr
914 $$
->flags |
= IS_DASHED
;
920 $$
->flags |
= IS_DEFAULT_FILLED
;
922 | object_spec FILL expr
925 $$
->flags |
= IS_FILLED
;
928 | object_spec SHADED text
931 $$
->flags |
= (IS_SHADED | IS_FILLED
);
932 $$
->shaded
= new
char[strlen
($3.str
)+1];
933 strcpy
($$
->shaded
, $3.str
);
935 | object_spec COLORED text
938 $$
->flags |
= (IS_SHADED | IS_OUTLINED | IS_FILLED
);
939 $$
->shaded
= new
char[strlen
($3.str
)+1];
940 strcpy
($$
->shaded
, $3.str
);
941 $$
->outlined
= new
char[strlen
($3.str
)+1];
942 strcpy
($$
->outlined
, $3.str
);
944 | object_spec OUTLINED text
947 $$
->flags |
= IS_OUTLINED
;
948 $$
->outlined
= new
char[strlen
($3.str
)+1];
949 strcpy
($$
->outlined
, $3.str
);
954 // line chop chop means line chop 0 chop 0
955 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
956 $$
->flags |
= IS_CHOPPED
;
957 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
958 $$
->start_chop
= $$
->end_chop
= 0.0;
960 else if
($$
->flags
& IS_CHOPPED
) {
964 $$
->flags |
= IS_DEFAULT_CHOPPED
;
967 | object_spec CHOP expr
970 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
971 $$
->flags |
= IS_CHOPPED
;
972 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
973 $$
->start_chop
= 0.0;
976 else if
($$
->flags
& IS_CHOPPED
) {
980 $$
->start_chop
= $$
->end_chop
= $3;
981 $$
->flags |
= IS_CHOPPED
;
987 $$
->flags |
= IS_SAME
;
989 | object_spec INVISIBLE
992 $$
->flags |
= IS_INVISIBLE
;
994 | object_spec LEFT_ARROW_HEAD
997 $$
->flags |
= HAS_LEFT_ARROW_HEAD
;
999 | object_spec RIGHT_ARROW_HEAD
1002 $$
->flags |
= HAS_RIGHT_ARROW_HEAD
;
1004 | object_spec DOUBLE_ARROW_HEAD
1007 $$
->flags |
= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD
);
1012 $$
->flags |
= IS_CLOCKWISE
;
1017 $$
->flags
&= ~IS_CLOCKWISE
;
1019 | object_spec text %prec TEXT
1023 for
(p
= & $$
->text
; *p
; p
= &(*p
)->next
)
1025 *p
= new text_item
($2.str
, $2.filename
, $2.lineno
);
1032 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1034 p
->adj.h
= LEFT_ADJUST
;
1042 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1044 p
->adj.h
= RIGHT_ADJUST
;
1052 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1054 p
->adj.v
= ABOVE_ADJUST
;
1062 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1064 p
->adj.v
= BELOW_ADJUST
;
1067 | object_spec THICKNESS expr
1070 $$
->flags |
= HAS_THICKNESS
;
1073 | object_spec ALIGNED
1076 $$
->flags |
= IS_ALIGNED
;
1083 | SPRINTF
'(' TEXT sprintf_args
')'
1085 $$.filename
= $3.filename
;
1086 $$.lineno
= $3.lineno
;
1087 $$.str
= do_sprintf
($3.str
, $4.v
, $4.nv
);
1100 | sprintf_args
',' expr
1103 if
($$.nv
>= $$.maxv
) {
1105 $$.v
= new
double[4];
1109 double *oldv
= $$.v
;
1112 $$.v
= new
double[$$.maxv
];
1113 memcpy
($$.v
, oldv
, $$.nv
*sizeof
(double));
1115 // workaround for bug in Compaq C++ V6.5-033
1116 // for Compaq Tru64 UNIX V5.1A (Rev. 1885)
1117 double *foo
= new
double[$$.maxv
];
1118 memcpy
(foo
, oldv
, $$.nv
*sizeof
(double));
1143 | position
'+' expr_pair
1148 | position
'-' expr_pair
1153 |
'(' position
',' position
')'
1158 | expr between position AND position
1160 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1161 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1163 | expr
'<' position
',' position
'>'
1165 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1166 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1172 | OF THE WAY BETWEEN
1186 /* line at A left == line (at A) left */
1192 if
(!pth.follow
($1, & $$
))
1198 if
(!pth.follow
($2, & $$
))
1204 if
(!pth.follow
($3, & $$
))
1209 $$.x
= current_position.x
;
1210 $$.y
= current_position.y
;
1218 place
*p
= lookup_label
($1);
1220 lex_error
("there is no place `%1'", $1);
1231 if
(!pth.follow
($1, & $$
))
1241 // XXX Check for overflow (and non-integers?).
1246 optional_ordinal_last:
1258 for
(p
= olist.head
; p
!= 0; p
= p
->next
)
1259 if
(p
->type
() == $2 && ++count
== $1) {
1264 lex_error
("there is no %1%2 %3", $1, ordinal_postfix
($1),
1265 object_type_name
($2));
1269 | optional_ordinal_last object_type
1273 for
(p
= olist.tail
; p
!= 0; p
= p
->prev
)
1274 if
(p
->type
() == $2 && ++count
== $1) {
1279 lex_error
("there is no %1%2 last %3", $1,
1280 ordinal_postfix
($1), object_type_name
($2));
1288 { $$
= BOX_OBJECT
; }
1290 { $$
= CIRCLE_OBJECT
; }
1292 { $$
= ELLIPSE_OBJECT
; }
1294 { $$
= ARC_OBJECT
; }
1296 { $$
= LINE_OBJECT
; }
1298 { $$
= ARROW_OBJECT
; }
1300 { $$
= SPLINE_OBJECT
; }
1302 { $$
= BLOCK_OBJECT
; }
1304 { $$
= TEXT_OBJECT
; }
1309 { $$
= new path
($2); }
1310 | label_path
'.' LABEL
1319 { $$
= new path
($1); }
1320 /* give this a lower precedence than LEFT and RIGHT so that
1321 [A: box] with .A left == [A: box] with (.A left) */
1322 | label_path %prec TEXT
1334 |
'(' relative_path
',' relative_path
')'
1339 /* The rest of these rules are a compatibility sop. */
1340 | ORDINAL LAST object_type relative_path
1342 lex_warning
("`%1%2 last %3' in `with' argument ignored",
1343 $1, ordinal_postfix
($1), object_type_name
($3));
1346 | LAST object_type relative_path
1348 lex_warning
("`last %1' in `with' argument ignored",
1349 object_type_name
($2));
1352 | ORDINAL object_type relative_path
1354 lex_warning
("`%1%2 %3' in `with' argument ignored",
1355 $1, ordinal_postfix
($1), object_type_name
($2));
1358 | LABEL relative_path
1360 lex_warning
("initial `%1' in `with' argument ignored", $1);
1368 { $$
= &object
::north
; }
1370 { $$
= &object
::east
; }
1372 { $$
= &object
::west
; }
1374 { $$
= &object
::south
; }
1376 { $$
= &object
::north_east
; }
1378 { $$
= &object
:: south_east
; }
1380 { $$
= &object
::north_west
; }
1382 { $$
= &object
::south_west
; }
1384 { $$
= &object
::center
; }
1386 { $$
= &object
::start
; }
1388 { $$
= &object
::end
; }
1390 { $$
= &object
::north
; }
1392 { $$
= &object
::south
; }
1394 { $$
= &object
::west
; }
1396 { $$
= &object
::east
; }
1398 { $$
= &object
::north_west
; }
1400 { $$
= &object
::south_west
; }
1402 { $$
= &object
::north_east
; }
1404 { $$
= &object
::south_east
; }
1406 { $$
= &object
::west
; }
1408 { $$
= &object
::east
; }
1410 { $$
= &object
::north_west
; }
1412 { $$
= &object
::south_west
; }
1413 | UPPER RIGHT_CORNER
1414 { $$
= &object
::north_east
; }
1415 | LOWER RIGHT_CORNER
1416 { $$
= &object
::south_east
; }
1418 { $$
= &object
::north
; }
1420 { $$
= &object
::south
; }
1422 { $$
= &object
::east
; }
1424 { $$
= &object
::west
; }
1426 { $$
= &object
::center
; }
1428 { $$
= &object
::start
; }
1430 { $$
= &object
::end
; }
1436 if
(!lookup_variable
($1, & $$
)) {
1437 lex_error
("there is no variable `%1'", $1);
1447 $$
= $1.obj
->origin
().x
;
1454 $$
= $1.obj
->origin
().y
;
1461 $$
= $1.obj
->height
();
1468 $$
= $1.obj
->width
();
1475 $$
= $1.obj
->radius
();
1488 lex_error
("division by zero");
1496 lex_error
("modulus by zero");
1505 if
(errno
== EDOM
) {
1506 lex_error
("arguments to `^' operator out of domain");
1509 if
(errno
== ERANGE
) {
1510 lex_error
("result of `^' operator out of range");
1514 |
'-' expr %prec
'!'
1518 | SIN
'(' any_expr
')'
1522 if
(errno
== ERANGE
) {
1523 lex_error
("sin result out of range");
1527 | COS
'(' any_expr
')'
1531 if
(errno
== ERANGE
) {
1532 lex_error
("cos result out of range");
1536 | ATAN2
'(' any_expr
',' any_expr
')'
1540 if
(errno
== EDOM
) {
1541 lex_error
("atan2 argument out of domain");
1544 if
(errno
== ERANGE
) {
1545 lex_error
("atan2 result out of range");
1549 | LOG
'(' any_expr
')'
1553 if
(errno
== ERANGE
) {
1554 lex_error
("log result out of range");
1558 | EXP
'(' any_expr
')'
1562 if
(errno
== ERANGE
) {
1563 lex_error
("exp result out of range");
1567 | SQRT
'(' any_expr
')'
1571 if
(errno
== EDOM
) {
1572 lex_error
("sqrt argument out of domain");
1576 | K_MAX
'(' any_expr
',' any_expr
')'
1577 { $$
= $3 > $5 ?
$3 : $5; }
1578 | K_MIN
'(' any_expr
',' any_expr
')'
1579 { $$
= $3 < $5 ?
$3 : $5; }
1580 | INT
'(' any_expr
')'
1582 | RAND
'(' any_expr
')'
1583 { $$
= 1.0 + floor
(((rand
()&0x7fff)/double(0x7fff))*$3); }
1586 /* return a random number in the range [0,1) */
1587 /* portable, but not very random */
1588 $$
= (rand
() & 0x7fff) / double(0x8000);
1590 | SRAND
'(' any_expr
')'
1593 srand
((unsigned int)$3);
1597 | expr LESSEQUAL expr
1598 { $$
= ($1 <= $3); }
1601 | expr GREATEREQUAL expr
1602 { $$
= ($1 >= $3); }
1603 | expr EQUALEQUAL expr
1604 { $$
= ($1 == $3); }
1605 | expr NOTEQUAL expr
1606 { $$
= ($1 != $3); }
1608 { $$
= ($1 != 0.0 && $3 != 0.0); }
1610 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
1612 { $$
= ($2 == 0.0); }
1618 /* bison defines const to be empty unless __STDC__ is defined, which it
1619 isn't under cfront */
1628 int scaled
; // non-zero if val should be multiplied by scale
1629 } defaults_table
[] = {
1630 { "arcrad", .25, 1 },
1631 { "arrowht", .1, 1 },
1632 { "arrowwid", .05, 1 },
1633 { "circlerad", .25, 1 },
1635 { "boxwid", .75, 1 },
1636 { "boxrad", 0.0, 1 },
1637 { "dashwid", .05, 1 },
1638 { "ellipseht", .5, 1 },
1639 { "ellipsewid", .75, 1 },
1640 { "moveht", .5, 1 },
1641 { "movewid", .5, 1 },
1642 { "lineht", .5, 1 },
1643 { "linewid", .5, 1 },
1644 { "textht", 0.0, 1 },
1645 { "textwid", 0.0, 1 },
1646 { "scale", 1.0, 0 },
1647 { "linethick", -1.0, 0 }, // in points
1648 { "fillval", .5, 0 },
1649 { "arrowhead", 1.0, 0 },
1650 { "maxpswid", 8.5, 0 },
1651 { "maxpsht", 11.0, 0 },
1654 place
*lookup_label
(const char *label
)
1656 saved_state
*state
= current_saved_state
;
1657 PTABLE
(place
) *tbl
= current_table
;
1659 place
*pl
= tbl
->lookup
(label
);
1665 state
= state
->prev
;
1669 void define_label
(const char *label
, const place
*pl
)
1671 place
*p
= new place
[1];
1673 current_table
->define
(label
, p
);
1676 int lookup_variable
(const char *name
, double *val
)
1678 place
*pl
= lookup_label
(name
);
1686 void define_variable
(const char *name
, double val
)
1688 place
*p
= new place
[1];
1692 current_table
->define
(name
, p
);
1693 if
(strcmp
(name
, "scale") == 0) {
1694 // When the scale changes, reset all scaled pre-defined variables to
1695 // their default values.
1696 for
(unsigned int i
= 0;
1697 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1698 if
(defaults_table
[i
].scaled
)
1699 define_variable
(defaults_table
[i
].name
, val
*defaults_table
[i
].val
);
1703 // called once only (not once per parse)
1707 current_direction
= RIGHT_DIRECTION
;
1708 current_position.x
= 0.0;
1709 current_position.y
= 0.0;
1710 // This resets everything to its default value.
1714 void reset
(const char *nm
)
1716 for
(unsigned int i
= 0;
1717 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1718 if
(strcmp
(nm
, defaults_table
[i
].name
) == 0) {
1719 double val
= defaults_table
[i
].val
;
1720 if
(defaults_table
[i
].scaled
) {
1722 lookup_variable
("scale", &scale
);
1725 define_variable
(defaults_table
[i
].name
, val
);
1728 lex_error
("`%1' is not a predefined variable", nm
);
1733 // We only have to explicitly reset the pre-defined variables that
1734 // aren't scaled because `scale' is not scaled, and changing the
1735 // value of `scale' will reset all the pre-defined variables that
1737 for
(unsigned int i
= 0;
1738 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1739 if
(!defaults_table
[i
].scaled
)
1740 define_variable
(defaults_table
[i
].name
, defaults_table
[i
].val
);
1743 // called after each parse
1745 void parse_cleanup
()
1747 while
(current_saved_state
!= 0) {
1748 delete current_table
;
1749 current_table
= current_saved_state
->tbl
;
1750 saved_state
*tem
= current_saved_state
;
1751 current_saved_state
= current_saved_state
->prev
;
1754 assert
(current_table
== &top_table
);
1755 PTABLE_ITERATOR
(place
) iter
(current_table
);
1758 while
(iter.next
(&key
, &pl
))
1760 position pos
= pl
->obj
->origin
();
1765 while
(olist.head
!= 0) {
1766 object
*tem
= olist.head
;
1767 olist.head
= olist.head
->next
;
1771 current_direction
= RIGHT_DIRECTION
;
1772 current_position.x
= 0.0;
1773 current_position.y
= 0.0;
1776 const char *ordinal_postfix
(int n
)
1778 if
(n
< 10 || n
> 20)
1790 const char *object_type_name
(object_type type
)
1797 case ELLIPSE_OBJECT
:
1821 static char sprintf_buf
[1024];
1823 char *format_number
(const char *form
, double n
)
1827 return do_sprintf
(form
, &n
, 1);
1830 char *do_sprintf
(const char *form
, const double *v
, int nv
)
1837 one_format
+= *form
++;
1838 for
(; *form
!= '\0' && strchr
("#-+ 0123456789.", *form
) != 0; form
++)
1839 one_format
+= *form
;
1840 if
(*form
== '\0' || strchr
("eEfgG%", *form
) == 0) {
1841 lex_error
("bad sprintf format");
1842 result
+= one_format
;
1847 one_format
+= *form
++;
1849 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1850 "%s", one_format.contents
());
1854 lex_error
("too few arguments to snprintf");
1855 result
+= one_format
;
1859 one_format
+= *form
++;
1861 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1862 one_format.contents
(), v
[i
++]);
1865 result
+= sprintf_buf
;
1871 return strsave
(result.contents
());