1 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
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 do_copy
(const char *);
27 extern
void copy_rest_thru
(const char *, const char *);
28 extern
void copy_file_thru
(const char *, const char *, const char *);
29 extern
void push_body
(const char *);
30 extern
void do_for
(char *var
, double from
, double to
,
31 int by_is_multiplicative
, double by
, char *body
);
32 extern
void do_lookahead
();
36 double fmod
(double, double);
44 #ifdef RET_TYPE_SRAND_IS_VOID
45 void srand
(unsigned int);
47 int srand
(unsigned int);
51 /* Maximum number of characters produced by printf("%g") */
55 void yyerror(const char *);
57 void reset
(const char *nm
);
60 place
*lookup_label
(const char *);
61 void define_label
(const char *label
, const place
*pl
);
63 direction current_direction
;
64 position current_position
;
66 implement_ptable
(place
)
68 PTABLE
(place
) top_table
;
70 PTABLE
(place
) *current_table
= &top_table
;
71 saved_state
*current_saved_state
= 0;
75 const char *ordinal_postfix
(int n
);
76 const char *object_type_name
(object_type type
);
77 char *format_number
(const char *form
, double n
);
78 char *do_sprintf
(const char *form
, const double *v
, int nv
);
87 struct { double x
, y
; } pair
;
88 struct { double x
; char *body
; } if_data
;
89 struct { char *str
; const char *filename
; int lineno
; } lstr
;
90 struct { double *v
; int nv
; int maxv
; } dv
;
91 struct { double val
; int is_multiplicative
; } by
;
103 %token
<str
> VARIABLE
106 %token
<lstr
> COMMAND_LINE
107 %token
<str
> DELIMITED
110 %token LEFT_ARROW_HEAD
111 %token RIGHT_ARROW_HEAD
112 %token DOUBLE_ARROW_HEAD
229 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
233 /* give text adjustments higher precedence than TEXT, so that
234 box "foo" above ljust == box ("foo" above ljust)
237 %left LJUST RJUST ABOVE BELOW
240 /* Give attributes that take an optional expression a higher
241 precedence than left and right, so that eg `line chop left'
243 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
246 %left VARIABLE NUMBER
'(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
247 %left ORDINAL HERE
'`'
249 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE
'['
251 /* these need to be lower than '-' */
252 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
254 /* these must have higher precedence than CHOP so that `label %prec CHOP'
256 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
257 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
258 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
263 %left EQUALEQUAL NOTEQUAL
264 %left
'<' '>' LESSEQUAL GREATEREQUAL
274 %type
<x
> expr any_expr text_expr
275 %type
<by
> optional_by
276 %type
<pair
> expr_pair position_not_place
277 %type
<if_data
> simple_if
278 %type
<obj
> nth_primitive
280 %type
<pth
> path label_path relative_path
281 %type
<pl
> place label element element_list middle_element_list
282 %type
<spec
> object_spec
283 %type
<pair
> position
284 %type
<obtype
> object_type
285 %type
<n
> optional_ordinal_last ordinal
287 %type
<dv
> sprintf_args
288 %type
<lstr
> text print_args print_arg
297 print_picture
(olist.head
);
303 optional_separator middle_element_list optional_separator
310 | middle_element_list separator element
325 VARIABLE
'=' any_expr
327 define_variable
($1, $3);
330 | VARIABLE
':' '=' any_expr
332 place
*p
= lookup_label
($1);
334 lex_error
("variable `%1' not defined", $1);
343 { current_direction
= UP_DIRECTION
; }
345 { current_direction
= DOWN_DIRECTION
; }
347 { current_direction
= LEFT_DIRECTION
; }
349 { current_direction
= RIGHT_DIRECTION
; }
352 olist.append
(make_command_object
($1.str
, $1.filename
,
357 olist.append
(make_command_object
($2.str
, $2.filename
,
362 fprintf
(stderr
, "%s\n", $2.str
);
372 lex_error
("unsafe to run command `%1'", $3);
382 // do not delete the filename
392 copy_file_thru
($2.str
, $5, $7);
393 // do not delete the filename
405 copy_rest_thru
($4, $6);
409 | FOR VARIABLE
'=' expr TO expr optional_by DO
416 do_for
($2, $4, $6, $7.is_multiplicative
, $7.val
, $10);
442 { define_variable
("scale", 1.0); }
451 | reset_variables VARIABLE
456 | reset_variables
',' VARIABLE
466 | print_args print_arg
468 $$.str
= new
char[strlen
($1.str
) + strlen
($2.str
) + 1];
469 strcpy
($$.str
, $1.str
);
470 strcat
($$.str
, $2.str
);
474 $$.filename
= $1.filename
;
475 $$.lineno
= $1.lineno
;
477 else if
($2.filename
) {
478 $$.filename
= $2.filename
;
479 $$.lineno
= $2.lineno
;
487 $$.str
= new
char[GDIGITS
+ 1];
488 sprintf
($$.str
, "%g", $1);
496 $$.str
= new
char[GDIGITS
+ 2 + GDIGITS
+ 1];
497 sprintf
($$.str
, "%g, %g", $1.x
, $1.y
);
531 $$
= strcmp
($1.str
, $3.str
) == 0;
537 $$
= strcmp
($1.str
, $3.str
) != 0;
541 | text_expr ANDAND text_expr
542 { $$
= ($1 != 0.0 && $3 != 0.0); }
543 | text_expr ANDAND expr
544 { $$
= ($1 != 0.0 && $3 != 0.0); }
545 | expr ANDAND text_expr
546 { $$
= ($1 != 0.0 && $3 != 0.0); }
547 | text_expr OROR text_expr
548 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
549 | text_expr OROR expr
550 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
551 | expr OROR text_expr
552 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
554 { $$
= ($2 == 0.0); }
562 $$.is_multiplicative
= 0;
567 $$.is_multiplicative
= 0;
572 $$.is_multiplicative
= 1;
579 $$.obj
= $1->make_object
(¤t_position
,
585 olist.append
($$.obj
);
587 $$.x
= current_position.x
;
588 $$.y
= current_position.y
;
591 | LABEL
':' optional_separator element
594 define_label
($1, & $$
);
597 | LABEL
':' optional_separator position_not_place
602 define_label
($1, & $$
);
605 | LABEL
':' optional_separator place
608 define_label
($1, & $$
);
613 $
<state
>$.x
= current_position.x
;
614 $
<state
>$.y
= current_position.y
;
615 $
<state
>$.dir
= current_direction
;
619 current_position.x
= $
<state
>2.x
;
620 current_position.y
= $
<state
>2.y
;
621 current_direction
= $
<state
>2.dir
;
630 $$.x
= current_position.x
;
631 $$.y
= current_position.y
;
644 { $$
= new object_spec
(BOX_OBJECT
); }
646 { $$
= new object_spec
(CIRCLE_OBJECT
); }
648 { $$
= new object_spec
(ELLIPSE_OBJECT
); }
651 $$
= new object_spec
(ARC_OBJECT
);
652 $$
->dir
= current_direction
;
656 $$
= new object_spec
(LINE_OBJECT
);
657 lookup_variable
("lineht", & $$
->segment_height
);
658 lookup_variable
("linewid", & $$
->segment_width
);
659 $$
->dir
= current_direction
;
663 $$
= new object_spec
(ARROW_OBJECT
);
664 lookup_variable
("lineht", & $$
->segment_height
);
665 lookup_variable
("linewid", & $$
->segment_width
);
666 $$
->dir
= current_direction
;
670 $$
= new object_spec
(MOVE_OBJECT
);
671 lookup_variable
("moveht", & $$
->segment_height
);
672 lookup_variable
("movewid", & $$
->segment_width
);
673 $$
->dir
= current_direction
;
677 $$
= new object_spec
(SPLINE_OBJECT
);
678 lookup_variable
("lineht", & $$
->segment_height
);
679 lookup_variable
("linewid", & $$
->segment_width
);
680 $$
->dir
= current_direction
;
684 $$
= new object_spec
(TEXT_OBJECT
);
685 $$
->text
= new text_item
($1.str
, $1.filename
, $1.lineno
);
689 $$
= new object_spec
(TEXT_OBJECT
);
690 $$
->text
= new text_item
(format_number
(0, $2), 0, -1);
694 $$
= new object_spec
(TEXT_OBJECT
);
695 $$
->text
= new text_item
(format_number
($3.str
, $2),
696 $3.filename
, $3.lineno
);
701 saved_state
*p
= new saved_state
;
703 p
->x
= current_position.x
;
704 p
->y
= current_position.y
;
705 p
->dir
= current_direction
;
706 p
->tbl
= current_table
;
707 p
->prev
= current_saved_state
;
708 current_position.x
= 0.0;
709 current_position.y
= 0.0;
710 current_table
= new PTABLE
(place
);
711 current_saved_state
= p
;
712 olist.append
(make_mark_object
());
716 current_position.x
= $
<pstate
>2->x
;
717 current_position.y
= $
<pstate
>2->y
;
718 current_direction
= $
<pstate
>2->dir
;
719 $$
= new object_spec
(BLOCK_OBJECT
);
720 olist.wrap_up_block
(& $$
->oblist
);
721 $$
->tbl
= current_table
;
722 current_table
= $
<pstate
>2->tbl
;
723 current_saved_state
= $
<pstate
>2->prev
;
726 | object_spec HEIGHT expr
730 $$
->flags |
= HAS_HEIGHT
;
732 | object_spec RADIUS expr
736 $$
->flags |
= HAS_RADIUS
;
738 | object_spec WIDTH expr
742 $$
->flags |
= HAS_WIDTH
;
744 | object_spec DIAMETER expr
748 $$
->flags |
= HAS_RADIUS
;
750 | object_spec expr %prec HEIGHT
753 $$
->flags |
= HAS_SEGMENT
;
756 $$
->segment_pos.y
+= $2;
759 $$
->segment_pos.y
-= $2;
761 case RIGHT_DIRECTION
:
762 $$
->segment_pos.x
+= $2;
765 $$
->segment_pos.x
-= $2;
772 $$
->dir
= UP_DIRECTION
;
773 $$
->flags |
= HAS_SEGMENT
;
774 $$
->segment_pos.y
+= $$
->segment_height
;
776 | object_spec UP expr
779 $$
->dir
= UP_DIRECTION
;
780 $$
->flags |
= HAS_SEGMENT
;
781 $$
->segment_pos.y
+= $3;
786 $$
->dir
= DOWN_DIRECTION
;
787 $$
->flags |
= HAS_SEGMENT
;
788 $$
->segment_pos.y
-= $$
->segment_height
;
790 | object_spec DOWN expr
793 $$
->dir
= DOWN_DIRECTION
;
794 $$
->flags |
= HAS_SEGMENT
;
795 $$
->segment_pos.y
-= $3;
800 $$
->dir
= RIGHT_DIRECTION
;
801 $$
->flags |
= HAS_SEGMENT
;
802 $$
->segment_pos.x
+= $$
->segment_width
;
804 | object_spec RIGHT expr
807 $$
->dir
= RIGHT_DIRECTION
;
808 $$
->flags |
= HAS_SEGMENT
;
809 $$
->segment_pos.x
+= $3;
814 $$
->dir
= LEFT_DIRECTION
;
815 $$
->flags |
= HAS_SEGMENT
;
816 $$
->segment_pos.x
-= $$
->segment_width
;
818 | object_spec LEFT expr
821 $$
->dir
= LEFT_DIRECTION
;
822 $$
->flags |
= HAS_SEGMENT
;
823 $$
->segment_pos.x
-= $3;
825 | object_spec FROM position
828 $$
->flags |
= HAS_FROM
;
832 | object_spec TO position
835 if
($$
->flags
& HAS_SEGMENT
)
836 $$
->segment_list
= new segment
($$
->segment_pos
,
837 $$
->segment_is_absolute
,
839 $$
->flags |
= HAS_SEGMENT
;
840 $$
->segment_pos.x
= $3.x
;
841 $$
->segment_pos.y
= $3.y
;
842 $$
->segment_is_absolute
= 1;
847 | object_spec AT position
853 if
($$
->type
!= ARC_OBJECT
) {
854 $$
->flags |
= HAS_FROM
;
859 | object_spec WITH path
862 $$
->flags |
= HAS_WITH
;
865 | object_spec WITH position %prec
','
868 $$
->flags |
= HAS_WITH
;
872 $$
->with
= new path
(pos
);
874 | object_spec BY expr_pair
877 $$
->flags |
= HAS_SEGMENT
;
878 $$
->segment_pos.x
+= $3.x
;
879 $$
->segment_pos.y
+= $3.y
;
884 if
($$
->flags
& HAS_SEGMENT
) {
885 $$
->segment_list
= new segment
($$
->segment_pos
,
886 $$
->segment_is_absolute
,
888 $$
->flags
&= ~HAS_SEGMENT
;
889 $$
->segment_pos.x
= $$
->segment_pos.y
= 0.0;
890 $$
->segment_is_absolute
= 0;
900 $$
->flags |
= IS_DOTTED
;
901 lookup_variable
("dashwid", & $$
->dash_width
);
903 | object_spec DOTTED expr
906 $$
->flags |
= IS_DOTTED
;
912 $$
->flags |
= IS_DASHED
;
913 lookup_variable
("dashwid", & $$
->dash_width
);
915 | object_spec DASHED expr
918 $$
->flags |
= IS_DASHED
;
924 $$
->flags |
= IS_DEFAULT_FILLED
;
926 | object_spec FILL expr
929 $$
->flags |
= IS_FILLED
;
932 | object_spec SHADED text
935 $$
->flags |
= (IS_SHADED | IS_FILLED
);
936 $$
->shaded
= new
char[strlen
($3.str
)+1];
937 strcpy
($$
->shaded
, $3.str
);
939 | object_spec COLORED text
942 $$
->flags |
= (IS_SHADED | IS_OUTLINED | IS_FILLED
);
943 $$
->shaded
= new
char[strlen
($3.str
)+1];
944 strcpy
($$
->shaded
, $3.str
);
945 $$
->outlined
= new
char[strlen
($3.str
)+1];
946 strcpy
($$
->outlined
, $3.str
);
948 | object_spec OUTLINED text
951 $$
->flags |
= IS_OUTLINED
;
952 $$
->outlined
= new
char[strlen
($3.str
)+1];
953 strcpy
($$
->outlined
, $3.str
);
958 // line chop chop means line chop 0 chop 0
959 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
960 $$
->flags |
= IS_CHOPPED
;
961 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
962 $$
->start_chop
= $$
->end_chop
= 0.0;
964 else if
($$
->flags
& IS_CHOPPED
) {
968 $$
->flags |
= IS_DEFAULT_CHOPPED
;
971 | object_spec CHOP expr
974 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
975 $$
->flags |
= IS_CHOPPED
;
976 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
977 $$
->start_chop
= 0.0;
980 else if
($$
->flags
& IS_CHOPPED
) {
984 $$
->start_chop
= $$
->end_chop
= $3;
985 $$
->flags |
= IS_CHOPPED
;
991 $$
->flags |
= IS_SAME
;
993 | object_spec INVISIBLE
996 $$
->flags |
= IS_INVISIBLE
;
998 | object_spec LEFT_ARROW_HEAD
1001 $$
->flags |
= HAS_LEFT_ARROW_HEAD
;
1003 | object_spec RIGHT_ARROW_HEAD
1006 $$
->flags |
= HAS_RIGHT_ARROW_HEAD
;
1008 | object_spec DOUBLE_ARROW_HEAD
1011 $$
->flags |
= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD
);
1016 $$
->flags |
= IS_CLOCKWISE
;
1021 $$
->flags
&= ~IS_CLOCKWISE
;
1023 | object_spec text %prec TEXT
1027 for
(p
= & $$
->text
; *p
; p
= &(*p
)->next
)
1029 *p
= new text_item
($2.str
, $2.filename
, $2.lineno
);
1036 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1038 p
->adj.h
= LEFT_ADJUST
;
1046 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1048 p
->adj.h
= RIGHT_ADJUST
;
1056 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1058 p
->adj.v
= ABOVE_ADJUST
;
1066 for
(p
= $$
->text
; p
->next
; p
= p
->next
)
1068 p
->adj.v
= BELOW_ADJUST
;
1071 | object_spec THICKNESS expr
1074 $$
->flags |
= HAS_THICKNESS
;
1077 | object_spec ALIGNED
1080 $$
->flags |
= IS_ALIGNED
;
1087 | SPRINTF
'(' TEXT sprintf_args
')'
1089 $$.filename
= $3.filename
;
1090 $$.lineno
= $3.lineno
;
1091 $$.str
= do_sprintf
($3.str
, $4.v
, $4.nv
);
1104 | sprintf_args
',' expr
1107 if
($$.nv
>= $$.maxv
) {
1109 $$.v
= new
double[4];
1113 double *oldv
= $$.v
;
1115 $$.v
= new
double[$$.maxv
];
1116 memcpy
($$.v
, oldv
, $$.nv
*sizeof
(double));
1139 | position
'+' expr_pair
1144 | position
'-' expr_pair
1149 |
'(' position
',' position
')'
1154 | expr between position AND position
1156 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1157 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1159 | expr
'<' position
',' position
'>'
1161 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1162 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1168 | OF THE WAY BETWEEN
1182 /* line at A left == line (at A) left */
1188 if
(!pth.follow
($1, & $$
))
1194 if
(!pth.follow
($2, & $$
))
1200 if
(!pth.follow
($3, & $$
))
1205 $$.x
= current_position.x
;
1206 $$.y
= current_position.y
;
1214 place
*p
= lookup_label
($1);
1216 lex_error
("there is no place `%1'", $1);
1227 if
(!pth.follow
($1, & $$
))
1237 // XXX Check for overflow (and non-integers?).
1242 optional_ordinal_last:
1254 for
(p
= olist.head
; p
!= 0; p
= p
->next
)
1255 if
(p
->type
() == $2 && ++count
== $1) {
1260 lex_error
("there is no %1%2 %3", $1, ordinal_postfix
($1),
1261 object_type_name
($2));
1265 | optional_ordinal_last object_type
1269 for
(p
= olist.tail
; p
!= 0; p
= p
->prev
)
1270 if
(p
->type
() == $2 && ++count
== $1) {
1275 lex_error
("there is no %1%2 last %3", $1,
1276 ordinal_postfix
($1), object_type_name
($2));
1284 { $$
= BOX_OBJECT
; }
1286 { $$
= CIRCLE_OBJECT
; }
1288 { $$
= ELLIPSE_OBJECT
; }
1290 { $$
= ARC_OBJECT
; }
1292 { $$
= LINE_OBJECT
; }
1294 { $$
= ARROW_OBJECT
; }
1296 { $$
= SPLINE_OBJECT
; }
1298 { $$
= BLOCK_OBJECT
; }
1300 { $$
= TEXT_OBJECT
; }
1305 { $$
= new path
($2); }
1306 | label_path
'.' LABEL
1315 { $$
= new path
($1); }
1316 /* give this a lower precedence than LEFT and RIGHT so that
1317 [A: box] with .A left == [A: box] with (.A left) */
1318 | label_path %prec TEXT
1330 |
'(' relative_path
',' relative_path
')'
1335 /* The rest of these rules are a compatibility sop. */
1336 | ORDINAL LAST object_type relative_path
1338 lex_warning
("`%1%2 last %3' in `with' argument ignored",
1339 $1, ordinal_postfix
($1), object_type_name
($3));
1342 | LAST object_type relative_path
1344 lex_warning
("`last %1' in `with' argument ignored",
1345 object_type_name
($2));
1348 | ORDINAL object_type relative_path
1350 lex_warning
("`%1%2 %3' in `with' argument ignored",
1351 $1, ordinal_postfix
($1), object_type_name
($2));
1354 | LABEL relative_path
1356 lex_warning
("initial `%1' in `with' argument ignored", $1);
1364 { $$
= &object
::north
; }
1366 { $$
= &object
::east
; }
1368 { $$
= &object
::west
; }
1370 { $$
= &object
::south
; }
1372 { $$
= &object
::north_east
; }
1374 { $$
= &object
:: south_east
; }
1376 { $$
= &object
::north_west
; }
1378 { $$
= &object
::south_west
; }
1380 { $$
= &object
::center
; }
1382 { $$
= &object
::start
; }
1384 { $$
= &object
::end
; }
1386 { $$
= &object
::north
; }
1388 { $$
= &object
::south
; }
1390 { $$
= &object
::west
; }
1392 { $$
= &object
::east
; }
1394 { $$
= &object
::north_west
; }
1396 { $$
= &object
::south_west
; }
1398 { $$
= &object
::north_east
; }
1400 { $$
= &object
::south_east
; }
1402 { $$
= &object
::west
; }
1404 { $$
= &object
::east
; }
1406 { $$
= &object
::north_west
; }
1408 { $$
= &object
::south_west
; }
1409 | UPPER RIGHT_CORNER
1410 { $$
= &object
::north_east
; }
1411 | LOWER RIGHT_CORNER
1412 { $$
= &object
::south_east
; }
1414 { $$
= &object
::north
; }
1416 { $$
= &object
::south
; }
1418 { $$
= &object
::east
; }
1420 { $$
= &object
::west
; }
1422 { $$
= &object
::center
; }
1424 { $$
= &object
::start
; }
1426 { $$
= &object
::end
; }
1432 if
(!lookup_variable
($1, & $$
)) {
1433 lex_error
("there is no variable `%1'", $1);
1443 $$
= $1.obj
->origin
().x
;
1450 $$
= $1.obj
->origin
().y
;
1457 $$
= $1.obj
->height
();
1464 $$
= $1.obj
->width
();
1471 $$
= $1.obj
->radius
();
1484 lex_error
("division by zero");
1492 lex_error
("modulus by zero");
1501 if
(errno
== EDOM
) {
1502 lex_error
("arguments to `^' operator out of domain");
1505 if
(errno
== ERANGE
) {
1506 lex_error
("result of `^' operator out of range");
1510 |
'-' expr %prec
'!'
1514 | SIN
'(' any_expr
')'
1518 if
(errno
== ERANGE
) {
1519 lex_error
("sin result out of range");
1523 | COS
'(' any_expr
')'
1527 if
(errno
== ERANGE
) {
1528 lex_error
("cos result out of range");
1532 | ATAN2
'(' any_expr
',' any_expr
')'
1536 if
(errno
== EDOM
) {
1537 lex_error
("atan2 argument out of domain");
1540 if
(errno
== ERANGE
) {
1541 lex_error
("atan2 result out of range");
1545 | LOG
'(' any_expr
')'
1549 if
(errno
== ERANGE
) {
1550 lex_error
("log result out of range");
1554 | EXP
'(' any_expr
')'
1558 if
(errno
== ERANGE
) {
1559 lex_error
("exp result out of range");
1563 | SQRT
'(' any_expr
')'
1567 if
(errno
== EDOM
) {
1568 lex_error
("sqrt argument out of domain");
1572 | K_MAX
'(' any_expr
',' any_expr
')'
1573 { $$
= $3 > $5 ?
$3 : $5; }
1574 | K_MIN
'(' any_expr
',' any_expr
')'
1575 { $$
= $3 < $5 ?
$3 : $5; }
1576 | INT
'(' any_expr
')'
1578 | RAND
'(' any_expr
')'
1579 { $$
= 1.0 + floor
(((rand
()&0x7fff)/double(0x7fff))*$3); }
1582 /* return a random number in the range [0,1) */
1583 /* portable, but not very random */
1584 $$
= (rand
() & 0x7fff) / double(0x8000);
1586 | SRAND
'(' any_expr
')'
1589 srand
((unsigned int)$3);
1593 | expr LESSEQUAL expr
1594 { $$
= ($1 <= $3); }
1597 | expr GREATEREQUAL expr
1598 { $$
= ($1 >= $3); }
1599 | expr EQUALEQUAL expr
1600 { $$
= ($1 == $3); }
1601 | expr NOTEQUAL expr
1602 { $$
= ($1 != $3); }
1604 { $$
= ($1 != 0.0 && $3 != 0.0); }
1606 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
1608 { $$
= ($2 == 0.0); }
1614 /* bison defines const to be empty unless __STDC__ is defined, which it
1615 isn't under cfront */
1624 int scaled
; // non-zero if val should be multiplied by scale
1625 } defaults_table
[] = {
1626 { "arcrad", .25, 1 },
1627 { "arrowht", .1, 1 },
1628 { "arrowwid", .05, 1 },
1629 { "circlerad", .25, 1 },
1631 { "boxwid", .75, 1 },
1632 { "boxrad", 0.0, 1 },
1633 { "dashwid", .05, 1 },
1634 { "ellipseht", .5, 1 },
1635 { "ellipsewid", .75, 1 },
1636 { "moveht", .5, 1 },
1637 { "movewid", .5, 1 },
1638 { "lineht", .5, 1 },
1639 { "linewid", .5, 1 },
1640 { "textht", 0.0, 1 },
1641 { "textwid", 0.0, 1 },
1642 { "scale", 1.0, 0 },
1643 { "linethick", -1.0, 0 }, // in points
1644 { "fillval", .5, 0 },
1645 { "arrowhead", 1.0, 0 },
1646 { "maxpswid", 8.5, 0 },
1647 { "maxpsht", 11.0, 0 },
1650 place
*lookup_label
(const char *label
)
1652 saved_state
*state
= current_saved_state
;
1653 PTABLE
(place
) *tbl
= current_table
;
1655 place
*pl
= tbl
->lookup
(label
);
1661 state
= state
->prev
;
1665 void define_label
(const char *label
, const place
*pl
)
1667 place
*p
= new place
;
1669 current_table
->define
(label
, p
);
1672 int lookup_variable
(const char *name
, double *val
)
1674 place
*pl
= lookup_label
(name
);
1682 void define_variable
(const char *name
, double val
)
1684 place
*p
= new place
;
1688 current_table
->define
(name
, p
);
1689 if
(strcmp
(name
, "scale") == 0) {
1690 // When the scale changes, reset all scaled pre-defined variables to
1691 // their default values.
1692 for
(unsigned int i
= 0;
1693 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1694 if
(defaults_table
[i
].scaled
)
1695 define_variable
(defaults_table
[i
].name
, val
*defaults_table
[i
].val
);
1699 // called once only (not once per parse)
1703 current_direction
= RIGHT_DIRECTION
;
1704 current_position.x
= 0.0;
1705 current_position.y
= 0.0;
1706 // This resets everything to its default value.
1710 void reset
(const char *nm
)
1712 for
(unsigned int i
= 0;
1713 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1714 if
(strcmp
(nm
, defaults_table
[i
].name
) == 0) {
1715 double val
= defaults_table
[i
].val
;
1716 if
(defaults_table
[i
].scaled
) {
1718 lookup_variable
("scale", &scale
);
1721 define_variable
(defaults_table
[i
].name
, val
);
1724 lex_error
("`%1' is not a predefined variable", nm
);
1729 // We only have to explicitly reset the pre-defined variables that
1730 // aren't scaled because `scale' is not scaled, and changing the
1731 // value of `scale' will reset all the pre-defined variables that
1733 for
(unsigned int i
= 0;
1734 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1735 if
(!defaults_table
[i
].scaled
)
1736 define_variable
(defaults_table
[i
].name
, defaults_table
[i
].val
);
1739 // called after each parse
1741 void parse_cleanup
()
1743 while
(current_saved_state
!= 0) {
1744 delete current_table
;
1745 current_table
= current_saved_state
->tbl
;
1746 saved_state
*tem
= current_saved_state
;
1747 current_saved_state
= current_saved_state
->prev
;
1750 assert
(current_table
== &top_table
);
1751 PTABLE_ITERATOR
(place
) iter
(current_table
);
1754 while
(iter.next
(&key
, &pl
))
1756 position pos
= pl
->obj
->origin
();
1761 while
(olist.head
!= 0) {
1762 object
*tem
= olist.head
;
1763 olist.head
= olist.head
->next
;
1767 current_direction
= RIGHT_DIRECTION
;
1768 current_position.x
= 0.0;
1769 current_position.y
= 0.0;
1772 const char *ordinal_postfix
(int n
)
1774 if
(n
< 10 || n
> 20)
1786 const char *object_type_name
(object_type type
)
1793 case ELLIPSE_OBJECT
:
1817 static char sprintf_buf
[1024];
1819 char *format_number
(const char *form
, double n
)
1823 return do_sprintf
(form
, &n
, 1);
1826 char *do_sprintf
(const char *form
, const double *v
, int nv
)
1833 one_format
+= *form
++;
1834 for
(; *form
!= '\0' && strchr
("#-+ 0123456789.", *form
) != 0; form
++)
1835 one_format
+= *form
;
1836 if
(*form
== '\0' || strchr
("eEfgG%", *form
) == 0) {
1837 lex_error
("bad sprintf format");
1838 result
+= one_format
;
1843 one_format
+= *form
++;
1845 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1846 "%s", one_format.contents
());
1850 lex_error
("too few arguments to snprintf");
1851 result
+= one_format
;
1855 one_format
+= *form
++;
1857 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1858 one_format.contents
(), v
[i
++]);
1861 result
+= sprintf_buf
;
1867 return strsave
(result.contents
());