1 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
2 Written by James Clark (jjc@jclark.com)
4 This file is part of groff.
6 groff is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
11 groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 You should have received a copy of the GNU General Public License along
17 with groff; see the file COPYING. If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
24 extern
int delim_flag
;
25 extern
void do_copy
(const char *);
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
();
37 double fmod
(double, double);
41 /* Maximum number of characters produced by printf("%g") */
46 #endif /* __BORLANDC__ */
49 void yyerror(const char *);
51 void reset
(const char *nm
);
54 place
*lookup_label
(const char *);
55 void define_label
(const char *label
, const place
*pl
);
57 direction current_direction
;
58 position current_position
;
60 implement_ptable
(place
)
62 PTABLE
(place
) top_table
;
64 PTABLE
(place
) *current_table
= &top_table
;
65 saved_state
*current_saved_state
= 0;
69 const char *ordinal_postfix
(int n
);
70 const char *object_type_name
(object_type type
);
71 char *format_number
(const char *form
, double n
);
72 char *do_sprintf
(const char *form
, const double *v
, int nv
);
81 struct { double x
, y
; } pair
;
82 struct { double x
; char *body
; } if_data
;
83 struct { char *str
; const char *filename
; int lineno
; } lstr
;
84 struct { double *v
; int nv
; int maxv
; } dv
;
85 struct { double val
; int is_multiplicative
; } by
;
100 %token
<lstr
> COMMAND_LINE
101 %token
<str
> DELIMITED
104 %token LEFT_ARROW_HEAD
105 %token RIGHT_ARROW_HEAD
106 %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 DASHED DOTTED UP DOWN FILL
229 %left VARIABLE NUMBER
'(' SIN COS ATAN2 LOG EXP SQRT MAX MIN INT RAND LAST
230 %left ORDINAL HERE
'`'
232 /* these need to be lower than '-' */
233 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
235 /* these must have higher precedence than CHOP so that `label %prec CHOP'
237 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
238 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
239 %left UPPER LOWER CENTER START END
244 %left EQUALEQUAL NOTEQUAL
245 %left
'<' '>' LESSEQUAL GREATEREQUAL
255 %type
<x
> expr any_expr text_expr
256 %type
<by
> optional_by
257 %type
<pair
> expr_pair position_not_place
258 %type
<if_data
> simple_if
259 %type
<obj
> nth_primitive
261 %type
<pth
> path label_path relative_path
262 %type
<pl
> place label element element_list middle_element_list
263 %type
<spec
> object_spec
264 %type
<pair
> position
265 %type
<obtype
> object_type
266 %type
<n
> optional_ordinal_last ordinal
268 %type
<dv
> sprintf_args
269 %type
<lstr
> text print_args print_arg
278 print_picture
(olist.head
);
284 optional_separator middle_element_list optional_separator
291 | middle_element_list separator element
306 VARIABLE
'=' any_expr
308 define_variable
($1, $3);
311 | VARIABLE
':' '=' any_expr
313 place
*p
= lookup_label
($1);
315 lex_error
("variable `%1' not defined", $1);
324 { current_direction
= UP_DIRECTION
; }
326 { current_direction
= DOWN_DIRECTION
; }
328 { current_direction
= LEFT_DIRECTION
; }
330 { current_direction
= RIGHT_DIRECTION
; }
333 olist.append
(make_command_object
($1.str
, $1.filename
,
338 olist.append
(make_command_object
($2.str
, $2.filename
,
343 fprintf
(stderr
, "%s\n", $2.str
);
359 // do not delete the filename
369 copy_file_thru
($2.str
, $5, $7);
370 // do not delete the filename
382 copy_rest_thru
($4, $6);
386 | FOR VARIABLE
'=' expr TO expr optional_by DO
393 do_for
($2, $4, $6, $7.is_multiplicative
, $7.val
, $10);
419 { define_variable
("scale", 1.0); }
424 { reset
($2); a_delete
$2; }
425 | reset_variables VARIABLE
426 { reset
($2); a_delete
$2; }
427 | reset_variables
',' VARIABLE
428 { reset
($3); a_delete
$3; }
434 | print_args print_arg
436 $$.str
= new
char[strlen
($1.str
) + strlen
($2.str
) + 1];
437 strcpy
($$.str
, $1.str
);
438 strcat
($$.str
, $2.str
);
442 $$.filename
= $1.filename
;
443 $$.lineno
= $1.lineno
;
445 else if
($2.filename
) {
446 $$.filename
= $2.filename
;
447 $$.lineno
= $2.lineno
;
455 $$.str
= new
char[GDIGITS
+ 1];
456 sprintf
($$.str
, "%g", $1);
464 $$.str
= new
char[GDIGITS
+ 2 + GDIGITS
+ 1];
465 sprintf
($$.str
, "%g, %g", $1.x
, $1.y
);
474 { delim_flag
= 0; $$.x
= $2; $$.body
= $5; }
494 $$
= strcmp
($1.str
, $3.str
) == 0;
500 $$
= strcmp
($1.str
, $3.str
) != 0;
504 | text_expr ANDAND text_expr
505 { $$
= ($1 != 0.0 && $3 != 0.0); }
506 | text_expr ANDAND expr
507 { $$
= ($1 != 0.0 && $3 != 0.0); }
508 | expr ANDAND text_expr
509 { $$
= ($1 != 0.0 && $3 != 0.0); }
510 | text_expr OROR text_expr
511 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
512 | text_expr OROR expr
513 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
514 | expr OROR text_expr
515 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
517 { $$
= ($2 == 0.0); }
523 { $$.val
= 1.0; $$.is_multiplicative
= 0; }
525 { $$.val
= $2; $$.is_multiplicative
= 0; }
527 { $$.val
= $3; $$.is_multiplicative
= 1; }
533 $$.obj
= $1->make_object
(¤t_position
,
539 olist.append
($$.obj
);
541 $$.x
= current_position.x
;
542 $$.y
= current_position.y
;
545 | LABEL
':' optional_separator element
546 { $$
= $4; define_label
($1, & $$
); a_delete
$1; }
547 | LABEL
':' optional_separator position_not_place
552 define_label
($1, & $$
);
555 | LABEL
':' optional_separator place
558 define_label
($1, & $$
);
563 $
<state
>$.x
= current_position.x
;
564 $
<state
>$.y
= current_position.y
;
565 $
<state
>$.dir
= current_direction
;
569 current_position.x
= $
<state
>2.x
;
570 current_position.y
= $
<state
>2.y
;
571 current_direction
= $
<state
>2.dir
;
580 $$.x
= current_position.x
;
581 $$.y
= current_position.y
;
595 $$
= new object_spec
(BOX_OBJECT
);
599 $$
= new object_spec
(CIRCLE_OBJECT
);
603 $$
= new object_spec
(ELLIPSE_OBJECT
);
607 $$
= new object_spec
(ARC_OBJECT
);
608 $$
->dir
= current_direction
;
612 $$
= new object_spec
(LINE_OBJECT
);
613 lookup_variable
("lineht", & $$
->segment_height
);
614 lookup_variable
("linewid", & $$
->segment_width
);
615 $$
->dir
= current_direction
;
619 $$
= new object_spec
(ARROW_OBJECT
);
620 lookup_variable
("lineht", & $$
->segment_height
);
621 lookup_variable
("linewid", & $$
->segment_width
);
622 $$
->dir
= current_direction
;
626 $$
= new object_spec
(MOVE_OBJECT
);
627 lookup_variable
("moveht", & $$
->segment_height
);
628 lookup_variable
("movewid", & $$
->segment_width
);
629 $$
->dir
= current_direction
;
633 $$
= new object_spec
(SPLINE_OBJECT
);
634 lookup_variable
("lineht", & $$
->segment_height
);
635 lookup_variable
("linewid", & $$
->segment_width
);
636 $$
->dir
= current_direction
;
640 $$
= new object_spec
(TEXT_OBJECT
);
641 $$
->text
= new text_item
($1.str
, $1.filename
, $1.lineno
);
645 $$
= new object_spec
(TEXT_OBJECT
);
646 $$
->text
= new text_item
(format_number
(0, $2), 0, -1);
650 $$
= new object_spec
(TEXT_OBJECT
);
651 $$
->text
= new text_item
(format_number
($3.str
, $2),
652 $3.filename
, $3.lineno
);
657 saved_state
*p
= new saved_state
;
659 p
->x
= current_position.x
;
660 p
->y
= current_position.y
;
661 p
->dir
= current_direction
;
662 p
->tbl
= current_table
;
663 p
->prev
= current_saved_state
;
664 current_position.x
= 0.0;
665 current_position.y
= 0.0;
666 current_table
= new PTABLE
(place
);
667 current_saved_state
= p
;
668 olist.append
(make_mark_object
());
672 current_position.x
= $
<pstate
>2->x
;
673 current_position.y
= $
<pstate
>2->y
;
674 current_direction
= $
<pstate
>2->dir
;
675 $$
= new object_spec
(BLOCK_OBJECT
);
676 olist.wrap_up_block
(& $$
->oblist
);
677 $$
->tbl
= current_table
;
678 current_table
= $
<pstate
>2->tbl
;
679 current_saved_state
= $
<pstate
>2->prev
;
682 | object_spec HEIGHT expr
686 $$
->flags |
= HAS_HEIGHT
;
688 | object_spec RADIUS expr
692 $$
->flags |
= HAS_RADIUS
;
694 | object_spec WIDTH expr
698 $$
->flags |
= HAS_WIDTH
;
700 | object_spec DIAMETER expr
704 $$
->flags |
= HAS_RADIUS
;
706 | object_spec expr %prec HEIGHT
709 $$
->flags |
= HAS_SEGMENT
;
712 $$
->segment_pos.y
+= $2;
715 $$
->segment_pos.y
-= $2;
717 case RIGHT_DIRECTION
:
718 $$
->segment_pos.x
+= $2;
721 $$
->segment_pos.x
-= $2;
728 $$
->dir
= UP_DIRECTION
;
729 $$
->flags |
= HAS_SEGMENT
;
730 $$
->segment_pos.y
+= $$
->segment_height
;
732 | object_spec UP expr
735 $$
->dir
= UP_DIRECTION
;
736 $$
->flags |
= HAS_SEGMENT
;
737 $$
->segment_pos.y
+= $3;
742 $$
->dir
= DOWN_DIRECTION
;
743 $$
->flags |
= HAS_SEGMENT
;
744 $$
->segment_pos.y
-= $$
->segment_height
;
746 | object_spec DOWN expr
749 $$
->dir
= DOWN_DIRECTION
;
750 $$
->flags |
= HAS_SEGMENT
;
751 $$
->segment_pos.y
-= $3;
756 $$
->dir
= RIGHT_DIRECTION
;
757 $$
->flags |
= HAS_SEGMENT
;
758 $$
->segment_pos.x
+= $$
->segment_width
;
760 | object_spec RIGHT expr
763 $$
->dir
= RIGHT_DIRECTION
;
764 $$
->flags |
= HAS_SEGMENT
;
765 $$
->segment_pos.x
+= $3;
770 $$
->dir
= LEFT_DIRECTION
;
771 $$
->flags |
= HAS_SEGMENT
;
772 $$
->segment_pos.x
-= $$
->segment_width
;
774 | object_spec LEFT expr
777 $$
->dir
= LEFT_DIRECTION
;
778 $$
->flags |
= HAS_SEGMENT
;
779 $$
->segment_pos.x
-= $3;
781 | object_spec FROM position
784 $$
->flags |
= HAS_FROM
;
788 | object_spec TO position
791 if
($$
->flags
& HAS_SEGMENT
)
792 $$
->segment_list
= new segment
($$
->segment_pos
,
793 $$
->segment_is_absolute
,
795 $$
->flags |
= HAS_SEGMENT
;
796 $$
->segment_pos.x
= $3.x
;
797 $$
->segment_pos.y
= $3.y
;
798 $$
->segment_is_absolute
= 1;
803 | object_spec AT position
809 if
($$
->type
!= ARC_OBJECT
) {
810 $$
->flags |
= HAS_FROM
;
815 | object_spec WITH path
818 $$
->flags |
= HAS_WITH
;
821 | object_spec BY expr_pair
824 $$
->flags |
= HAS_SEGMENT
;
825 $$
->segment_pos.x
+= $3.x
;
826 $$
->segment_pos.y
+= $3.y
;
831 if
($$
->flags
& HAS_SEGMENT
) {
832 $$
->segment_list
= new segment
($$
->segment_pos
,
833 $$
->segment_is_absolute
,
835 $$
->flags
&= ~HAS_SEGMENT
;
836 $$
->segment_pos.x
= $$
->segment_pos.y
= 0.0;
837 $$
->segment_is_absolute
= 0;
843 $$
->flags |
= IS_DOTTED
;
844 lookup_variable
("dashwid", & $$
->dash_width
);
846 | object_spec DOTTED expr
849 $$
->flags |
= IS_DOTTED
;
855 $$
->flags |
= IS_DASHED
;
856 lookup_variable
("dashwid", & $$
->dash_width
);
858 | object_spec DASHED expr
861 $$
->flags |
= IS_DASHED
;
867 $$
->flags |
= IS_DEFAULT_FILLED
;
869 | object_spec FILL expr
872 $$
->flags |
= IS_FILLED
;
878 // line chop chop means line chop 0 chop 0
879 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
880 $$
->flags |
= IS_CHOPPED
;
881 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
882 $$
->start_chop
= $$
->end_chop
= 0.0;
884 else if
($$
->flags
& IS_CHOPPED
) {
888 $$
->flags |
= IS_DEFAULT_CHOPPED
;
891 | object_spec CHOP expr
894 if
($$
->flags
& IS_DEFAULT_CHOPPED
) {
895 $$
->flags |
= IS_CHOPPED
;
896 $$
->flags
&= ~IS_DEFAULT_CHOPPED
;
897 $$
->start_chop
= 0.0;
900 else if
($$
->flags
& IS_CHOPPED
) {
904 $$
->start_chop
= $$
->end_chop
= $3;
905 $$
->flags |
= IS_CHOPPED
;
911 $$
->flags |
= IS_SAME
;
913 | object_spec INVISIBLE
916 $$
->flags |
= IS_INVISIBLE
;
918 | object_spec LEFT_ARROW_HEAD
921 $$
->flags |
= HAS_LEFT_ARROW_HEAD
;
923 | object_spec RIGHT_ARROW_HEAD
926 $$
->flags |
= HAS_RIGHT_ARROW_HEAD
;
928 | object_spec DOUBLE_ARROW_HEAD
931 $$
->flags |
= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD
);
936 $$
->flags |
= IS_CLOCKWISE
;
941 $$
->flags
&= ~IS_CLOCKWISE
;
943 | object_spec text %prec TEXT
946 for
(text_item
**p
= & $$
->text
; *p
; p
= &(*p
)->next
)
948 *p
= new text_item
($2.str
, $2.filename
, $2.lineno
);
954 for
(text_item
*p
= $$
->text
; p
->next
; p
= p
->next
)
956 p
->adj.h
= LEFT_ADJUST
;
963 for
(text_item
*p
= $$
->text
; p
->next
; p
= p
->next
)
965 p
->adj.h
= RIGHT_ADJUST
;
972 for
(text_item
*p
= $$
->text
; p
->next
; p
= p
->next
)
974 p
->adj.v
= ABOVE_ADJUST
;
981 for
(text_item
*p
= $$
->text
; p
->next
; p
= p
->next
)
983 p
->adj.v
= BELOW_ADJUST
;
986 | object_spec THICKNESS expr
989 $$
->flags |
= HAS_THICKNESS
;
992 | object_spec ALIGNED
995 $$
->flags |
= IS_ALIGNED
;
1004 | SPRINTF
'(' TEXT sprintf_args
')'
1006 $$.filename
= $3.filename
;
1007 $$.lineno
= $3.lineno
;
1008 $$.str
= do_sprintf
($3.str
, $4.v
, $4.nv
);
1021 | sprintf_args
',' expr
1024 if
($$.nv
>= $$.maxv
) {
1026 $$.v
= new
double[4];
1030 double *oldv
= $$.v
;
1032 $$.v
= new
double[$$.maxv
];
1033 memcpy
($$.v
, oldv
, $$.nv
*sizeof
(double));
1056 | position
'+' expr_pair
1061 | position
'-' expr_pair
1066 |
'(' position
',' position
')'
1071 | expr between position AND position
1073 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1074 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1076 | expr
'<' position
',' position
'>'
1078 $$.x
= (1.0 - $1)*$3.x
+ $1*$5.x
;
1079 $$.y
= (1.0 - $1)*$3.y
+ $1*$5.y
;
1085 | OF THE WAY BETWEEN
1090 { $$.x
= $1; $$.y
= $3; }
1096 label %prec CHOP
/* line at A left == line (at A) left */
1101 if
(!pth.follow
($1, & $$
))
1107 if
(!pth.follow
($2, & $$
))
1113 if
(!pth.follow
($3, & $$
))
1118 $$.x
= current_position.x
;
1119 $$.y
= current_position.y
;
1127 place
*p
= lookup_label
($1);
1129 lex_error
("there is no place `%1'", $1);
1142 if
(!pth.follow
($1, & $$
))
1152 // XXX Check for overflow (and non-integers?).
1157 optional_ordinal_last:
1168 for
(object
*p
= olist.head
; p
!= 0; p
= p
->next
)
1169 if
(p
->type
() == $2 && ++count
== $1) {
1174 lex_error
("there is no %1%2 %3", $1, ordinal_postfix
($1),
1175 object_type_name
($2));
1179 | optional_ordinal_last object_type
1182 for
(object
*p
= olist.tail
; p
!= 0; p
= p
->prev
)
1183 if
(p
->type
() == $2 && ++count
== $1) {
1188 lex_error
("there is no %1%2 last %3", $1,
1189 ordinal_postfix
($1), object_type_name
($2));
1197 { $$
= BOX_OBJECT
; }
1199 { $$
= CIRCLE_OBJECT
; }
1201 { $$
= ELLIPSE_OBJECT
; }
1203 { $$
= ARC_OBJECT
; }
1205 { $$
= LINE_OBJECT
; }
1207 { $$
= ARROW_OBJECT
; }
1209 { $$
= SPLINE_OBJECT
; }
1211 { $$
= BLOCK_OBJECT
; }
1213 { $$
= TEXT_OBJECT
; }
1221 | label_path
'.' LABEL
1233 /* give this a lower precedence than LEFT and RIGHT so that
1234 [A: box] with .A left == [A: box] with (.A left) */
1236 | label_path %prec TEXT
1252 /* The rest of these rules are a compatibility sop. */
1253 | ORDINAL LAST object_type relative_path
1255 lex_warning
("`%1%2 last %3' in `with' argument ignored",
1256 $1, ordinal_postfix
($1), object_type_name
($3));
1259 | LAST object_type relative_path
1261 lex_warning
("`last %1' in `with' argument ignored",
1262 object_type_name
($2));
1265 | ORDINAL object_type relative_path
1267 lex_warning
("`%1%2 %3' in `with' argument ignored",
1268 $1, ordinal_postfix
($1), object_type_name
($2));
1271 | LABEL relative_path
1273 lex_warning
("initial `%1' in `with' argument ignored", $1);
1281 { $$
= &object
::north
; }
1283 { $$
= &object
::east
; }
1285 { $$
= &object
::west
; }
1287 { $$
= &object
::south
; }
1289 { $$
= &object
::north_east
; }
1291 { $$
= &object
:: south_east
; }
1293 { $$
= &object
::north_west
; }
1295 { $$
= &object
::south_west
; }
1297 { $$
= &object
::center
; }
1299 { $$
= &object
::start
; }
1301 { $$
= &object
::end
; }
1303 { $$
= &object
::north
; }
1305 { $$
= &object
::south
; }
1307 { $$
= &object
::west
; }
1309 { $$
= &object
::east
; }
1311 { $$
= &object
::north_west
; }
1313 { $$
= &object
::south_west
; }
1315 { $$
= &object
::north_east
; }
1317 { $$
= &object
::south_east
; }
1319 { $$
= &object
::west
; }
1321 { $$
= &object
::east
; }
1323 { $$
= &object
::north_west
; }
1325 { $$
= &object
::south_west
; }
1326 | UPPER RIGHT_CORNER
1327 { $$
= &object
::north_east
; }
1328 | LOWER RIGHT_CORNER
1329 { $$
= &object
::south_east
; }
1331 { $$
= &object
::center
; }
1333 { $$
= &object
::start
; }
1335 { $$
= &object
::end
; }
1341 if
(!lookup_variable
($1, & $$
)) {
1342 lex_error
("there is no variable `%1'", $1);
1352 $$
= $1.obj
->origin
().x
;
1359 $$
= $1.obj
->origin
().y
;
1366 $$
= $1.obj
->height
();
1373 $$
= $1.obj
->width
();
1380 $$
= $1.obj
->radius
();
1393 lex_error
("division by zero");
1401 lex_error
("modulus by zero");
1410 if
(errno
== EDOM
) {
1411 lex_error
("arguments to `^' operator out of domain");
1414 if
(errno
== ERANGE
) {
1415 lex_error
("result of `^' operator out of range");
1419 |
'-' expr %prec
'!'
1423 | SIN
'(' any_expr
')'
1427 if
(errno
== ERANGE
) {
1428 lex_error
("sin result out of range");
1432 | COS
'(' any_expr
')'
1436 if
(errno
== ERANGE
) {
1437 lex_error
("cos result out of range");
1441 | ATAN2
'(' any_expr
',' any_expr
')'
1445 if
(errno
== EDOM
) {
1446 lex_error
("atan2 argument out of domain");
1449 if
(errno
== ERANGE
) {
1450 lex_error
("atan2 result out of range");
1454 | LOG
'(' any_expr
')'
1458 if
(errno
== ERANGE
) {
1459 lex_error
("log result out of range");
1463 | EXP
'(' any_expr
')'
1467 if
(errno
== ERANGE
) {
1468 lex_error
("exp result out of range");
1472 | SQRT
'(' any_expr
')'
1476 if
(errno
== EDOM
) {
1477 lex_error
("sqrt argument out of domain");
1481 | MAX
'(' any_expr
',' any_expr
')'
1482 { $$
= $3 > $5 ?
$3 : $5; }
1483 | MIN
'(' any_expr
',' any_expr
')'
1484 { $$
= $3 < $5 ?
$3 : $5; }
1485 | INT
'(' any_expr
')'
1487 | RAND
'(' any_expr
')'
1488 { $$
= 1.0 + floor
(((rand
()&0x7fff)/double(0x7fff))*$3); }
1491 /* return a random number in the range [0,1) */
1492 /* portable, but not very random */
1493 $$
= (rand
() & 0x7fff) / double(0x8000);
1497 | expr LESSEQUAL expr
1498 { $$
= ($1 <= $3); }
1501 | expr GREATEREQUAL expr
1502 { $$
= ($1 >= $3); }
1503 | expr EQUALEQUAL expr
1504 { $$
= ($1 == $3); }
1505 | expr NOTEQUAL expr
1506 { $$
= ($1 != $3); }
1508 { $$
= ($1 != 0.0 && $3 != 0.0); }
1510 { $$
= ($1 != 0.0 ||
$3 != 0.0); }
1512 { $$
= ($2 == 0.0); }
1518 /* bison defines const to be empty unless __STDC__ is defined, which it
1519 isn't under cfront */
1528 int scaled
; // non-zero if val should be multiplied by scale
1529 } defaults_table
[] = {
1533 "circlerad", .25, 1,
1539 "ellipsewid", .75, 1,
1547 "linethick", -1.0, 0, // in points
1549 "arrowhead", 1.0, 0,
1554 place
*lookup_label
(const char *label
)
1556 saved_state
*state
= current_saved_state
;
1557 PTABLE
(place
) *tbl
= current_table
;
1559 place
*pl
= tbl
->lookup
(label
);
1565 state
= state
->prev
;
1569 void define_label
(const char *label
, const place
*pl
)
1571 place
*p
= new place
;
1573 current_table
->define
(label
, p
);
1576 int lookup_variable
(const char *name
, double *val
)
1578 place
*pl
= lookup_label
(name
);
1586 void define_variable
(const char *name
, double val
)
1588 place
*p
= new place
;
1592 current_table
->define
(name
, p
);
1593 if
(strcmp
(name
, "scale") == 0) {
1594 // When the scale changes, reset all scaled pre-defined variables to
1595 // their default values.
1596 for
(int i
= 0; i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1597 if
(defaults_table
[i
].scaled
)
1598 define_variable
(defaults_table
[i
].name
, val
*defaults_table
[i
].val
);
1602 // called once only (not once per parse)
1606 current_direction
= RIGHT_DIRECTION
;
1607 current_position.x
= 0.0;
1608 current_position.y
= 0.0;
1609 // This resets everything to its default value.
1613 void reset
(const char *nm
)
1615 for
(int i
= 0; i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1616 if
(strcmp
(nm
, defaults_table
[i
].name
) == 0) {
1617 double val
= defaults_table
[i
].val
;
1618 if
(defaults_table
[i
].scaled
) {
1620 lookup_variable
("scale", &scale
);
1623 define_variable
(defaults_table
[i
].name
, val
);
1626 lex_error
("`%1' is not a predefined variable", nm
);
1631 // We only have to explicitly reset the pre-defined variables that
1632 // aren't scaled because `scale' is not scaled, and changing the
1633 // value of `scale' will reset all the pre-defined variables that
1635 for
(int i
= 0; i
< sizeof
(defaults_table
)/sizeof
(defaults_table
[0]); i
++)
1636 if
(!defaults_table
[i
].scaled
)
1637 define_variable
(defaults_table
[i
].name
, defaults_table
[i
].val
);
1640 // called after each parse
1642 void parse_cleanup
()
1644 while
(current_saved_state
!= 0) {
1645 delete current_table
;
1646 current_table
= current_saved_state
->tbl
;
1647 saved_state
*tem
= current_saved_state
;
1648 current_saved_state
= current_saved_state
->prev
;
1651 assert
(current_table
== &top_table
);
1652 PTABLE_ITERATOR
(place
) iter
(current_table
);
1655 while
(iter.next
(&key
, &pl
))
1657 position pos
= pl
->obj
->origin
();
1662 while
(olist.head
!= 0) {
1663 object
*tem
= olist.head
;
1664 olist.head
= olist.head
->next
;
1668 current_direction
= RIGHT_DIRECTION
;
1669 current_position.x
= 0.0;
1670 current_position.y
= 0.0;
1673 const char *ordinal_postfix
(int n
)
1675 if
(n
< 10 || n
> 20)
1687 const char *object_type_name
(object_type type
)
1694 case ELLIPSE_OBJECT
:
1718 static char sprintf_buf
[1024];
1720 char *format_number
(const char *form
, double n
)
1725 // this is a fairly feeble attempt at validation of the format
1727 for
(const char *p
= form
; *p
!= '\0'; p
++)
1735 lex_error
("bad format `%1'", form
);
1736 return strsave
(form
);
1739 sprintf
(sprintf_buf
, form
, n
);
1740 return strsave
(sprintf_buf
);
1743 char *do_sprintf
(const char *form
, const double *v
, int nv
)
1750 one_format
+= *form
++;
1751 for
(; *form
!= '\0' && strchr
("#-+ 0123456789.", *form
) != 0; form
++)
1752 one_format
+= *form
;
1753 if
(*form
== '\0' || strchr
("eEfgG%", *form
) == 0) {
1754 lex_error
("bad sprintf format");
1755 result
+= one_format
;
1760 one_format
+= *form
++;
1762 sprintf
(sprintf_buf
, one_format.contents
());
1766 lex_error
("too few arguments to sprintf");
1767 result
+= one_format
;
1771 one_format
+= *form
++;
1773 sprintf
(sprintf_buf
, one_format.contents
(), v
[i
++]);
1776 result
+= sprintf_buf
;
1782 return strsave
(result.contents
());