1 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, 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));
1149 | position
'+' expr_pair
1154 |
'(' position
'+' expr_pair
')'
1159 | position
'-' expr_pair
1164 |
'(' position
'-' expr_pair
')'
1169 |
'(' position
',' position
')'
1174 | expr between position AND position
1176 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1177 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1179 |
'(' expr between position AND position
')'
1181 $$.x
= (1.0 - $2)*$4.x
+ $2*$6.x
;
1182 $$.y
= (1.0 - $2)*$4.y
+ $2*$6.y
;
1184 | expr
'<' position
',' position
'>'
1186 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1187 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1189 |
'(' expr
'<' position
',' position
'>' ')'
1191 $$.x
= (1.0 - $2)*$4.x
+ $2*$6.x
;
1192 $$.y
= (1.0 - $2)*$4.y
+ $2*$6.y
;
1198 | OF THE WAY BETWEEN
1212 /* line at A left == line (at A) left */
1218 if
(!pth.follow
($1, & $$
))
1224 if
(!pth.follow
($2, & $$
))
1230 if
(!pth.follow
($3, & $$
))
1235 $$.x
= current_position.x
;
1236 $$.y
= current_position.y
;
1244 place
*p
= lookup_label
($1);
1246 lex_error
("there is no place `%1'", $1);
1257 if
(!pth.follow
($1, & $$
))
1267 // XXX Check for overflow (and non-integers?).
1272 optional_ordinal_last:
1284 for
(p
= olist.head
; p
!= 0; p
= p
->next
)
1285 if
(p
->type
() == $2 && ++count
== $1) {
1290 lex_error
("there is no %1%2 %3", $1, ordinal_postfix
($1),
1291 object_type_name
($2));
1295 | optional_ordinal_last object_type
1299 for
(p
= olist.tail
; p
!= 0; p
= p
->prev
)
1300 if
(p
->type
() == $2 && ++count
== $1) {
1305 lex_error
("there is no %1%2 last %3", $1,
1306 ordinal_postfix
($1), object_type_name
($2));
1314 { $$
= BOX_OBJECT
; }
1316 { $$
= CIRCLE_OBJECT
; }
1318 { $$
= ELLIPSE_OBJECT
; }
1320 { $$
= ARC_OBJECT
; }
1322 { $$
= LINE_OBJECT
; }
1324 { $$
= ARROW_OBJECT
; }
1326 { $$
= SPLINE_OBJECT
; }
1328 { $$
= BLOCK_OBJECT
; }
1330 { $$
= TEXT_OBJECT
; }
1335 { $$
= new path
($2); }
1336 | label_path
'.' LABEL
1345 { $$
= new path
($1); }
1346 /* give this a lower precedence than LEFT and RIGHT so that
1347 [A: box] with .A left == [A: box] with (.A left) */
1348 | label_path %prec TEXT
1360 |
'(' relative_path
',' relative_path
')'
1365 /* The rest of these rules are a compatibility sop. */
1366 | ORDINAL LAST object_type relative_path
1368 lex_warning
("`%1%2 last %3' in `with' argument ignored",
1369 $1, ordinal_postfix
($1), object_type_name
($3));
1372 | LAST object_type relative_path
1374 lex_warning
("`last %1' in `with' argument ignored",
1375 object_type_name
($2));
1378 | ORDINAL object_type relative_path
1380 lex_warning
("`%1%2 %3' in `with' argument ignored",
1381 $1, ordinal_postfix
($1), object_type_name
($2));
1384 | LABEL relative_path
1386 lex_warning
("initial `%1' in `with' argument ignored", $1);
1394 { $$
= &object
::north
; }
1396 { $$
= &object
::east
; }
1398 { $$
= &object
::west
; }
1400 { $$
= &object
::south
; }
1402 { $$
= &object
::north_east
; }
1404 { $$
= &object
:: south_east
; }
1406 { $$
= &object
::north_west
; }
1408 { $$
= &object
::south_west
; }
1410 { $$
= &object
::center
; }
1412 { $$
= &object
::start
; }
1414 { $$
= &object
::end
; }
1416 { $$
= &object
::north
; }
1418 { $$
= &object
::south
; }
1420 { $$
= &object
::west
; }
1422 { $$
= &object
::east
; }
1424 { $$
= &object
::north_west
; }
1426 { $$
= &object
::south_west
; }
1428 { $$
= &object
::north_east
; }
1430 { $$
= &object
::south_east
; }
1432 { $$
= &object
::west
; }
1434 { $$
= &object
::east
; }
1436 { $$
= &object
::north_west
; }
1438 { $$
= &object
::south_west
; }
1439 | UPPER RIGHT_CORNER
1440 { $$
= &object
::north_east
; }
1441 | LOWER RIGHT_CORNER
1442 { $$
= &object
::south_east
; }
1444 { $$
= &object
::north
; }
1446 { $$
= &object
::south
; }
1448 { $$
= &object
::east
; }
1450 { $$
= &object
::west
; }
1452 { $$
= &object
::center
; }
1454 { $$
= &object
::start
; }
1456 { $$
= &object
::end
; }
1462 if
(!lookup_variable
($1, & $$
)) {
1463 lex_error
("there is no variable `%1'", $1);
1473 $$
= $1.obj
->origin
().x
;
1480 $$
= $1.obj
->origin
().y
;
1487 $$
= $1.obj
->height
();
1494 $$
= $1.obj
->width
();
1501 $$
= $1.obj
->radius
();
1514 lex_error
("division by zero");
1522 lex_error
("modulus by zero");
1531 if
(errno
== EDOM
) {
1532 lex_error
("arguments to `^' operator out of domain");
1535 if
(errno
== ERANGE
) {
1536 lex_error
("result of `^' operator out of range");
1540 |
'-' expr %prec
'!'
1544 | SIN
'(' any_expr
')'
1548 if
(errno
== ERANGE
) {
1549 lex_error
("sin result out of range");
1553 | COS
'(' any_expr
')'
1557 if
(errno
== ERANGE
) {
1558 lex_error
("cos result out of range");
1562 | ATAN2
'(' any_expr
',' any_expr
')'
1566 if
(errno
== EDOM
) {
1567 lex_error
("atan2 argument out of domain");
1570 if
(errno
== ERANGE
) {
1571 lex_error
("atan2 result out of range");
1575 | LOG
'(' any_expr
')'
1579 if
(errno
== ERANGE
) {
1580 lex_error
("log result out of range");
1584 | EXP
'(' any_expr
')'
1588 if
(errno
== ERANGE
) {
1589 lex_error
("exp result out of range");
1593 | SQRT
'(' any_expr
')'
1597 if
(errno
== EDOM
) {
1598 lex_error
("sqrt argument out of domain");
1602 | K_MAX
'(' any_expr
',' any_expr
')'
1603 { $$
= $3 > $5 ?
$3 : $5; }
1604 | K_MIN
'(' any_expr
',' any_expr
')'
1605 { $$
= $3 < $5 ?
$3 : $5; }
1606 | INT
'(' any_expr
')'
1608 | RAND
'(' any_expr
')'
1609 { $$
= 1.0 + floor
(((rand
()&0x7fff)/double(0x7fff))*$3); }
1612 /* return a random number in the range [0,1) */
1613 /* portable, but not very random */
1614 $$
= (rand
() & 0x7fff) / double(0x8000);
1616 | SRAND
'(' any_expr
')'
1619 srand
((unsigned int)$3);
1623 | expr LESSEQUAL expr
1624 { $$
= ($1 <= $3); }
1627 | expr GREATEREQUAL expr
1628 { $$
= ($1 >= $3); }
1629 | expr EQUALEQUAL expr
1630 { $$
= ($1 == $3); }
1631 | expr NOTEQUAL expr
1632 { $$
= ($1 != $3); }
1634 { $$
= ($1 != 0.0 && $3 != 0.0); }
1636 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
1638 { $$
= ($2 == 0.0); }
1644 /* bison defines const to be empty unless __STDC__ is defined, which it
1645 isn't under cfront */
1654 int scaled
; // non-zero if val should be multiplied by scale
1655 } defaults_table
[] = {
1656 { "arcrad", .25, 1 },
1657 { "arrowht", .1, 1 },
1658 { "arrowwid", .05, 1 },
1659 { "circlerad", .25, 1 },
1661 { "boxwid", .75, 1 },
1662 { "boxrad", 0.0, 1 },
1663 { "dashwid", .05, 1 },
1664 { "ellipseht", .5, 1 },
1665 { "ellipsewid", .75, 1 },
1666 { "moveht", .5, 1 },
1667 { "movewid", .5, 1 },
1668 { "lineht", .5, 1 },
1669 { "linewid", .5, 1 },
1670 { "textht", 0.0, 1 },
1671 { "textwid", 0.0, 1 },
1672 { "scale", 1.0, 0 },
1673 { "linethick", -1.0, 0 }, // in points
1674 { "fillval", .5, 0 },
1675 { "arrowhead", 1.0, 0 },
1676 { "maxpswid", 8.5, 0 },
1677 { "maxpsht", 11.0, 0 },
1680 place
*lookup_label
(const char *label
)
1682 saved_state
*state
= current_saved_state
;
1683 PTABLE
(place
) *tbl
= current_table
;
1685 place
*pl
= tbl
->lookup
(label
);
1691 state
= state
->prev
;
1695 void define_label
(const char *label
, const place
*pl
)
1697 place
*p
= new place
[1];
1699 current_table
->define
(label
, p
);
1702 int lookup_variable
(const char *name
, double *val
)
1704 place
*pl
= lookup_label
(name
);
1712 void define_variable
(const char *name
, double val
)
1714 place
*p
= new place
[1];
1718 current_table
->define
(name
, p
);
1719 if
(strcmp
(name
, "scale") == 0) {
1720 // When the scale changes, reset all scaled pre-defined variables to
1721 // their default values.
1722 for
(unsigned int i
= 0;
1723 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1724 if
(defaults_table
[i
].scaled
)
1725 define_variable
(defaults_table
[i
].name
, val
*defaults_table
[i
].val
);
1729 // called once only (not once per parse)
1733 current_direction
= RIGHT_DIRECTION
;
1734 current_position.x
= 0.0;
1735 current_position.y
= 0.0;
1736 // This resets everything to its default value.
1740 void reset
(const char *nm
)
1742 for
(unsigned int i
= 0;
1743 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1744 if
(strcmp
(nm
, defaults_table
[i
].name
) == 0) {
1745 double val
= defaults_table
[i
].val
;
1746 if
(defaults_table
[i
].scaled
) {
1748 lookup_variable
("scale", &scale
);
1751 define_variable
(defaults_table
[i
].name
, val
);
1754 lex_error
("`%1' is not a predefined variable", nm
);
1759 // We only have to explicitly reset the pre-defined variables that
1760 // aren't scaled because `scale' is not scaled, and changing the
1761 // value of `scale' will reset all the pre-defined variables that
1763 for
(unsigned int i
= 0;
1764 i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1765 if
(!defaults_table
[i
].scaled
)
1766 define_variable
(defaults_table
[i
].name
, defaults_table
[i
].val
);
1769 // called after each parse
1771 void parse_cleanup
()
1773 while
(current_saved_state
!= 0) {
1774 delete current_table
;
1775 current_table
= current_saved_state
->tbl
;
1776 saved_state
*tem
= current_saved_state
;
1777 current_saved_state
= current_saved_state
->prev
;
1780 assert
(current_table
== &top_table
);
1781 PTABLE_ITERATOR
(place
) iter
(current_table
);
1784 while
(iter.next
(&key
, &pl
))
1786 position pos
= pl
->obj
->origin
();
1791 while
(olist.head
!= 0) {
1792 object
*tem
= olist.head
;
1793 olist.head
= olist.head
->next
;
1797 current_direction
= RIGHT_DIRECTION
;
1798 current_position.x
= 0.0;
1799 current_position.y
= 0.0;
1802 const char *ordinal_postfix
(int n
)
1804 if
(n
< 10 || n
> 20)
1816 const char *object_type_name
(object_type type
)
1823 case ELLIPSE_OBJECT
:
1847 static char sprintf_buf
[1024];
1849 char *format_number
(const char *form
, double n
)
1853 return do_sprintf
(form
, &n
, 1);
1856 char *do_sprintf
(const char *form
, const double *v
, int nv
)
1863 one_format
+= *form
++;
1864 for
(; *form
!= '\0' && strchr
("#-+ 0123456789.", *form
) != 0; form
++)
1865 one_format
+= *form
;
1866 if
(*form
== '\0' || strchr
("eEfgG%", *form
) == 0) {
1867 lex_error
("bad sprintf format");
1868 result
+= one_format
;
1873 one_format
+= *form
++;
1875 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1876 "%s", one_format.contents
());
1880 lex_error
("too few arguments to snprintf");
1881 result
+= one_format
;
1885 one_format
+= *form
++;
1887 snprintf
(sprintf_buf
, sizeof
(sprintf_buf
),
1888 one_format.contents
(), v
[i
++]);
1891 result
+= sprintf_buf
;
1897 return strsave
(result.contents
());