1 /* Copyright (C) 1989-2018 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 3 of the License, or
9 (at your option) any later version.
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
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 extern int delim_flag;
25 extern void copy_rest_thru(const char *, const char *);
26 extern void copy_file_thru(const char *, const char *, const char *);
27 extern void push_body(const char *);
28 extern void do_for(char *var, double from, double to,
29 int by_is_multiplicative, double by, char *body);
30 extern void do_lookahead();
32 /* Maximum number of characters produced by printf("%g") */
36 void yyerror(const char *);
38 void reset(const char *nm);
41 place *lookup_label(const char *);
42 void define_label(const char *label, const place *pl);
44 direction current_direction;
45 position current_position;
47 implement_ptable(place)
49 PTABLE(place) top_table;
51 PTABLE(place) *current_table = &top_table;
52 saved_state *current_saved_state = 0;
56 const char *ordinal_postfix(int n);
57 const char *object_type_name(object_type type);
58 char *format_number(const char *form, double n);
59 char *do_sprintf(const char *form, const double *v, int nv);
69 struct { double x, y; } pair;
70 struct { double x; char *body; } if_data;
71 struct { char *str; const char *filename; int lineno; } lstr;
72 struct { double *v; int nv; int maxv; } dv;
73 struct { double val; int is_multiplicative; } by;
88 %token <lstr> COMMAND_LINE
89 %token <str> DELIMITED
92 %token LEFT_ARROW_HEAD
93 %token RIGHT_ARROW_HEAD
94 %token DOUBLE_ARROW_HEAD
210 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
214 /* give text adjustments higher precedence than TEXT, so that
215 box "foo" above ljust == box ("foo" above ljust)
218 %left LJUST RJUST ABOVE BELOW
221 /* Give attributes that take an optional expression a higher
222 precedence than left and right, so that, e.g., 'line chop left'
224 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
225 %left XSLANTED YSLANTED
228 %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST
229 %left ORDINAL HERE '`'
231 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '['
233 /* these need to be lower than '-' */
234 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
236 /* these must have higher precedence than CHOP so that 'label %prec CHOP'
238 %left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
239 %left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
240 %left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END
245 %left EQUALEQUAL NOTEQUAL
246 %left '<' '>' LESSEQUAL GREATEREQUAL
256 %type <x> expr expr_lower_than expr_not_lower_than any_expr text_expr
257 %type <by> optional_by
258 %type <pair> expr_pair position_not_place
259 %type <if_data> simple_if
260 %type <obj> nth_primitive
262 %type <pth> path label_path relative_path
263 %type <pl> place label element element_list middle_element_list
264 %type <spec> object_spec
265 %type <pair> position
266 %type <obtype> object_type
267 %type <n> optional_ordinal_last ordinal
268 %type <str> macro_name until
269 %type <dv> sprintf_args
270 %type <lstr> text print_args print_arg
279 print_picture(olist.head);
285 optional_separator middle_element_list optional_separator
292 | middle_element_list separator element
307 FIGNAME '=' macro_name
310 graphname = new char[strlen($3) + 1];
311 strcpy(graphname, $3);
315 VARIABLE '=' any_expr
317 define_variable($1, $3);
320 | VARIABLE ':' '=' any_expr
322 place *p = lookup_label($1);
324 lex_error("variable '%1' not defined", $1);
333 { current_direction = UP_DIRECTION; }
335 { current_direction = DOWN_DIRECTION; }
337 { current_direction = LEFT_DIRECTION; }
339 { current_direction = RIGHT_DIRECTION; }
342 olist.append(make_command_object($1.str, $1.filename,
347 olist.append(make_command_object($2.str, $2.filename,
352 fprintf(stderr, "%s\n", $2.str);
362 lex_error("unsafe to run command '%1'", $3);
372 // do not delete the filename
382 copy_file_thru($2.str, $5, $7);
383 // do not delete the filename
395 copy_rest_thru($4, $6);
399 | FOR VARIABLE '=' expr TO expr optional_by DO
406 do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10);
432 { define_variable("scale", 1.0); }
446 | reset_variables VARIABLE
451 | reset_variables ',' VARIABLE
461 | print_args print_arg
463 $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
464 strcpy($$.str, $1.str);
465 strcat($$.str, $2.str);
469 $$.filename = $1.filename;
470 $$.lineno = $1.lineno;
472 else if ($2.filename) {
473 $$.filename = $2.filename;
474 $$.lineno = $2.lineno;
482 $$.str = new char[GDIGITS + 1];
483 sprintf($$.str, "%g", $1);
491 $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
492 sprintf($$.str, "%g, %g", $1.x, $1.y);
526 $$ = strcmp($1.str, $3.str) == 0;
532 $$ = strcmp($1.str, $3.str) != 0;
536 | text_expr ANDAND text_expr
537 { $$ = ($1 != 0.0 && $3 != 0.0); }
538 | text_expr ANDAND expr
539 { $$ = ($1 != 0.0 && $3 != 0.0); }
540 | expr ANDAND text_expr
541 { $$ = ($1 != 0.0 && $3 != 0.0); }
542 | text_expr OROR text_expr
543 { $$ = ($1 != 0.0 || $3 != 0.0); }
544 | text_expr OROR expr
545 { $$ = ($1 != 0.0 || $3 != 0.0); }
546 | expr OROR text_expr
547 { $$ = ($1 != 0.0 || $3 != 0.0); }
549 { $$ = ($2 == 0.0); }
557 $$.is_multiplicative = 0;
562 $$.is_multiplicative = 0;
567 $$.is_multiplicative = 1;
574 $$.obj = $1->make_object(¤t_position,
580 olist.append($$.obj);
582 $$.x = current_position.x;
583 $$.y = current_position.y;
586 | LABEL ':' optional_separator element
589 define_label($1, & $$);
592 | LABEL ':' optional_separator position_not_place
597 define_label($1, & $$);
600 | LABEL ':' optional_separator place
603 define_label($1, & $$);
608 $<state>$.x = current_position.x;
609 $<state>$.y = current_position.y;
610 $<state>$.dir = current_direction;
614 current_position.x = $<state>2.x;
615 current_position.y = $<state>2.y;
616 current_direction = $<state>2.dir;
625 $$.x = current_position.x;
626 $$.y = current_position.y;
639 { $$ = new object_spec(BOX_OBJECT); }
641 { $$ = new object_spec(CIRCLE_OBJECT); }
643 { $$ = new object_spec(ELLIPSE_OBJECT); }
646 $$ = new object_spec(ARC_OBJECT);
647 $$->dir = current_direction;
651 $$ = new object_spec(LINE_OBJECT);
652 lookup_variable("lineht", & $$->segment_height);
653 lookup_variable("linewid", & $$->segment_width);
654 $$->dir = current_direction;
658 $$ = new object_spec(ARROW_OBJECT);
659 lookup_variable("lineht", & $$->segment_height);
660 lookup_variable("linewid", & $$->segment_width);
661 $$->dir = current_direction;
665 $$ = new object_spec(MOVE_OBJECT);
666 lookup_variable("moveht", & $$->segment_height);
667 lookup_variable("movewid", & $$->segment_width);
668 $$->dir = current_direction;
672 $$ = new object_spec(SPLINE_OBJECT);
673 lookup_variable("lineht", & $$->segment_height);
674 lookup_variable("linewid", & $$->segment_width);
675 $$->dir = current_direction;
679 $$ = new object_spec(TEXT_OBJECT);
680 $$->text = new text_item($1.str, $1.filename, $1.lineno);
684 $$ = new object_spec(TEXT_OBJECT);
685 $$->text = new text_item(format_number(0, $2), 0, -1);
689 $$ = new object_spec(TEXT_OBJECT);
690 $$->text = new text_item(format_number($3.str, $2),
691 $3.filename, $3.lineno);
696 saved_state *p = new saved_state;
698 p->x = current_position.x;
699 p->y = current_position.y;
700 p->dir = current_direction;
701 p->tbl = current_table;
702 p->prev = current_saved_state;
703 current_position.x = 0.0;
704 current_position.y = 0.0;
705 current_table = new PTABLE(place);
706 current_saved_state = p;
707 olist.append(make_mark_object());
711 current_position.x = $<pstate>2->x;
712 current_position.y = $<pstate>2->y;
713 current_direction = $<pstate>2->dir;
714 $$ = new object_spec(BLOCK_OBJECT);
715 olist.wrap_up_block(& $$->oblist);
716 $$->tbl = current_table;
717 current_table = $<pstate>2->tbl;
718 current_saved_state = $<pstate>2->prev;
721 | object_spec HEIGHT expr
725 $$->flags |= HAS_HEIGHT;
727 | object_spec RADIUS expr
731 $$->flags |= HAS_RADIUS;
733 | object_spec WIDTH expr
737 $$->flags |= HAS_WIDTH;
739 | object_spec DIAMETER expr
743 $$->flags |= HAS_RADIUS;
745 | object_spec expr %prec HEIGHT
748 $$->flags |= HAS_SEGMENT;
751 $$->segment_pos.y += $2;
754 $$->segment_pos.y -= $2;
756 case RIGHT_DIRECTION:
757 $$->segment_pos.x += $2;
760 $$->segment_pos.x -= $2;
767 $$->dir = UP_DIRECTION;
768 $$->flags |= HAS_SEGMENT;
769 $$->segment_pos.y += $$->segment_height;
771 | object_spec UP expr
774 $$->dir = UP_DIRECTION;
775 $$->flags |= HAS_SEGMENT;
776 $$->segment_pos.y += $3;
781 $$->dir = DOWN_DIRECTION;
782 $$->flags |= HAS_SEGMENT;
783 $$->segment_pos.y -= $$->segment_height;
785 | object_spec DOWN expr
788 $$->dir = DOWN_DIRECTION;
789 $$->flags |= HAS_SEGMENT;
790 $$->segment_pos.y -= $3;
795 $$->dir = RIGHT_DIRECTION;
796 $$->flags |= HAS_SEGMENT;
797 $$->segment_pos.x += $$->segment_width;
799 | object_spec RIGHT expr
802 $$->dir = RIGHT_DIRECTION;
803 $$->flags |= HAS_SEGMENT;
804 $$->segment_pos.x += $3;
809 $$->dir = LEFT_DIRECTION;
810 $$->flags |= HAS_SEGMENT;
811 $$->segment_pos.x -= $$->segment_width;
813 | object_spec LEFT expr
816 $$->dir = LEFT_DIRECTION;
817 $$->flags |= HAS_SEGMENT;
818 $$->segment_pos.x -= $3;
820 | object_spec FROM position
823 $$->flags |= HAS_FROM;
827 | object_spec TO position
830 if ($$->flags & HAS_SEGMENT)
831 $$->segment_list = new segment($$->segment_pos,
832 $$->segment_is_absolute,
834 $$->flags |= HAS_SEGMENT;
835 $$->segment_pos.x = $3.x;
836 $$->segment_pos.y = $3.y;
837 $$->segment_is_absolute = 1;
842 | object_spec AT position
848 if ($$->type != ARC_OBJECT) {
849 $$->flags |= HAS_FROM;
854 | object_spec WITH path
857 $$->flags |= HAS_WITH;
860 | object_spec WITH position %prec ','
863 $$->flags |= HAS_WITH;
867 $$->with = new path(pos);
869 | object_spec BY expr_pair
872 $$->flags |= HAS_SEGMENT;
873 $$->segment_pos.x += $3.x;
874 $$->segment_pos.y += $3.y;
879 if (!($$->flags & HAS_SEGMENT))
882 $$->segment_pos.y += $$->segment_width;
885 $$->segment_pos.y -= $$->segment_width;
887 case RIGHT_DIRECTION:
888 $$->segment_pos.x += $$->segment_width;
891 $$->segment_pos.x -= $$->segment_width;
894 $$->segment_list = new segment($$->segment_pos,
895 $$->segment_is_absolute,
897 $$->flags &= ~HAS_SEGMENT;
898 $$->segment_pos.x = $$->segment_pos.y = 0.0;
899 $$->segment_is_absolute = 0;
908 $$->flags |= IS_DOTTED;
909 lookup_variable("dashwid", & $$->dash_width);
911 | object_spec DOTTED expr
914 $$->flags |= IS_DOTTED;
920 $$->flags |= IS_DASHED;
921 lookup_variable("dashwid", & $$->dash_width);
923 | object_spec DASHED expr
926 $$->flags |= IS_DASHED;
932 $$->flags |= IS_DEFAULT_FILLED;
934 | object_spec FILL expr
937 $$->flags |= IS_FILLED;
940 | object_spec XSLANTED expr
943 $$->flags |= IS_XSLANTED;
946 | object_spec YSLANTED expr
949 $$->flags |= IS_YSLANTED;
952 | object_spec SHADED text
955 $$->flags |= (IS_SHADED | IS_FILLED);
956 $$->shaded = new char[strlen($3.str)+1];
957 strcpy($$->shaded, $3.str);
959 | object_spec COLORED text
962 $$->flags |= (IS_SHADED | IS_OUTLINED | IS_FILLED);
963 $$->shaded = new char[strlen($3.str)+1];
964 strcpy($$->shaded, $3.str);
965 $$->outlined = new char[strlen($3.str)+1];
966 strcpy($$->outlined, $3.str);
968 | object_spec OUTLINED text
971 $$->flags |= IS_OUTLINED;
972 $$->outlined = new char[strlen($3.str)+1];
973 strcpy($$->outlined, $3.str);
978 // line chop chop means line chop 0 chop 0
979 if ($$->flags & IS_DEFAULT_CHOPPED) {
980 $$->flags |= IS_CHOPPED;
981 $$->flags &= ~IS_DEFAULT_CHOPPED;
982 $$->start_chop = $$->end_chop = 0.0;
984 else if ($$->flags & IS_CHOPPED) {
988 $$->flags |= IS_DEFAULT_CHOPPED;
991 | object_spec CHOP expr
994 if ($$->flags & IS_DEFAULT_CHOPPED) {
995 $$->flags |= IS_CHOPPED;
996 $$->flags &= ~IS_DEFAULT_CHOPPED;
997 $$->start_chop = 0.0;
1000 else if ($$->flags & IS_CHOPPED) {
1004 $$->start_chop = $$->end_chop = $3;
1005 $$->flags |= IS_CHOPPED;
1011 $$->flags |= IS_SAME;
1013 | object_spec INVISIBLE
1016 $$->flags |= IS_INVISIBLE;
1018 | object_spec LEFT_ARROW_HEAD
1021 $$->flags |= HAS_LEFT_ARROW_HEAD;
1023 | object_spec RIGHT_ARROW_HEAD
1026 $$->flags |= HAS_RIGHT_ARROW_HEAD;
1028 | object_spec DOUBLE_ARROW_HEAD
1031 $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
1036 $$->flags |= IS_CLOCKWISE;
1041 $$->flags &= ~IS_CLOCKWISE;
1043 | object_spec text %prec TEXT
1047 for (p = & $$->text; *p; p = &(*p)->next)
1049 *p = new text_item($2.str, $2.filename, $2.lineno);
1056 for (p = $$->text; p->next; p = p->next)
1058 p->adj.h = LEFT_ADJUST;
1066 for (p = $$->text; p->next; p = p->next)
1068 p->adj.h = RIGHT_ADJUST;
1076 for (p = $$->text; p->next; p = p->next)
1078 p->adj.v = ABOVE_ADJUST;
1086 for (p = $$->text; p->next; p = p->next)
1088 p->adj.v = BELOW_ADJUST;
1091 | object_spec THICKNESS expr
1094 $$->flags |= HAS_THICKNESS;
1097 | object_spec ALIGNED
1100 $$->flags |= IS_ALIGNED;
1107 | SPRINTF '(' TEXT sprintf_args ')'
1109 $$.filename = $3.filename;
1110 $$.lineno = $3.lineno;
1111 $$.str = do_sprintf($3.str, $4.v, $4.nv);
1124 | sprintf_args ',' expr
1127 if ($$.nv >= $$.maxv) {
1129 $$.v = new double[4];
1133 double *oldv = $$.v;
1136 $$.v = new double[$$.maxv];
1137 memcpy($$.v, oldv, $$.nv*sizeof(double));
1139 // workaround for bug in Compaq C++ V6.5-033
1140 // for Compaq Tru64 UNIX V5.1A (Rev. 1885)
1141 double *foo = new double[$$.maxv];
1142 memcpy(foo, oldv, $$.nv*sizeof(double));
1173 | position '+' expr_pair
1178 | '(' position '+' expr_pair ')'
1183 | position '-' expr_pair
1188 | '(' position '-' expr_pair ')'
1193 | '(' position ',' position ')'
1198 | expr between position AND position
1200 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1201 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1203 | '(' expr between position AND position ')'
1205 $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1206 $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1208 /* the next two rules cause harmless shift/reduce warnings */
1209 | expr_not_lower_than '<' position ',' position '>'
1211 $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1212 $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1214 | '(' expr_not_lower_than '<' position ',' position '>' ')'
1216 $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1217 $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1223 | OF THE WAY BETWEEN
1237 /* line at A left == line (at A) left */
1243 if (!pth.follow($1, & $$))
1249 if (!pth.follow($2, & $$))
1255 if (!pth.follow($3, & $$))
1260 $$.x = current_position.x;
1261 $$.y = current_position.y;
1269 place *p = lookup_label($1);
1271 lex_error("there is no place '%1'", $1);
1282 if (!pth.follow($1, & $$))
1292 // XXX Check for overflow (and non-integers?).
1297 optional_ordinal_last:
1309 for (p = olist.head; p != 0; p = p->next)
1310 if (p->type() == $2 && ++count == $1) {
1315 lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
1316 object_type_name($2));
1320 | optional_ordinal_last object_type
1324 for (p = olist.tail; p != 0; p = p->prev)
1325 if (p->type() == $2 && ++count == $1) {
1330 lex_error("there is no %1%2 last %3", $1,
1331 ordinal_postfix($1), object_type_name($2));
1339 { $$ = BOX_OBJECT; }
1341 { $$ = CIRCLE_OBJECT; }
1343 { $$ = ELLIPSE_OBJECT; }
1345 { $$ = ARC_OBJECT; }
1347 { $$ = LINE_OBJECT; }
1349 { $$ = ARROW_OBJECT; }
1351 { $$ = SPLINE_OBJECT; }
1353 { $$ = BLOCK_OBJECT; }
1355 { $$ = TEXT_OBJECT; }
1360 { $$ = new path($2); }
1361 | label_path '.' LABEL
1370 { $$ = new path($1); }
1371 /* give this a lower precedence than LEFT and RIGHT so that
1372 [A: box] with .A left == [A: box] with (.A left) */
1373 | label_path %prec TEXT
1385 | '(' relative_path ',' relative_path ')'
1390 /* The rest of these rules are a compatibility sop. */
1391 | ORDINAL LAST object_type relative_path
1393 lex_warning("'%1%2 last %3' in 'with' argument ignored",
1394 $1, ordinal_postfix($1), object_type_name($3));
1397 | LAST object_type relative_path
1399 lex_warning("'last %1' in 'with' argument ignored",
1400 object_type_name($2));
1403 | ORDINAL object_type relative_path
1405 lex_warning("'%1%2 %3' in 'with' argument ignored",
1406 $1, ordinal_postfix($1), object_type_name($2));
1409 | LABEL relative_path
1411 lex_warning("initial '%1' in 'with' argument ignored", $1);
1419 { $$ = &object::north; }
1421 { $$ = &object::east; }
1423 { $$ = &object::west; }
1425 { $$ = &object::south; }
1427 { $$ = &object::north_east; }
1429 { $$ = &object:: south_east; }
1431 { $$ = &object::north_west; }
1433 { $$ = &object::south_west; }
1435 { $$ = &object::center; }
1437 { $$ = &object::start; }
1439 { $$ = &object::end; }
1441 { $$ = &object::north; }
1443 { $$ = &object::south; }
1445 { $$ = &object::west; }
1447 { $$ = &object::east; }
1449 { $$ = &object::north_west; }
1451 { $$ = &object::south_west; }
1453 { $$ = &object::north_east; }
1455 { $$ = &object::south_east; }
1457 { $$ = &object::west; }
1459 { $$ = &object::east; }
1461 { $$ = &object::north_west; }
1463 { $$ = &object::south_west; }
1464 | UPPER RIGHT_CORNER
1465 { $$ = &object::north_east; }
1466 | LOWER RIGHT_CORNER
1467 { $$ = &object::south_east; }
1469 { $$ = &object::north; }
1471 { $$ = &object::south; }
1473 { $$ = &object::east; }
1475 { $$ = &object::west; }
1477 { $$ = &object::center; }
1479 { $$ = &object::start; }
1481 { $$ = &object::end; }
1487 | expr_not_lower_than
1496 expr_not_lower_than:
1499 if (!lookup_variable($1, & $$)) {
1500 lex_error("there is no variable '%1'", $1);
1510 $$ = $1.obj->origin().x;
1517 $$ = $1.obj->origin().y;
1524 $$ = $1.obj->height();
1531 $$ = $1.obj->width();
1538 $$ = $1.obj->radius();
1551 lex_error("division by zero");
1559 lex_error("modulus by zero");
1568 if (errno == EDOM) {
1569 lex_error("arguments to '^' operator out of domain");
1572 if (errno == ERANGE) {
1573 lex_error("result of '^' operator out of range");
1577 | '-' expr %prec '!'
1581 | SIN '(' any_expr ')'
1585 if (errno == ERANGE) {
1586 lex_error("sin result out of range");
1590 | COS '(' any_expr ')'
1594 if (errno == ERANGE) {
1595 lex_error("cos result out of range");
1599 | ATAN2 '(' any_expr ',' any_expr ')'
1603 if (errno == EDOM) {
1604 lex_error("atan2 argument out of domain");
1607 if (errno == ERANGE) {
1608 lex_error("atan2 result out of range");
1612 | LOG '(' any_expr ')'
1616 if (errno == ERANGE) {
1617 lex_error("log result out of range");
1621 | EXP '(' any_expr ')'
1625 if (errno == ERANGE) {
1626 lex_error("exp result out of range");
1630 | SQRT '(' any_expr ')'
1634 if (errno == EDOM) {
1635 lex_error("sqrt argument out of domain");
1639 | K_MAX '(' any_expr ',' any_expr ')'
1640 { $$ = $3 > $5 ? $3 : $5; }
1641 | K_MIN '(' any_expr ',' any_expr ')'
1642 { $$ = $3 < $5 ? $3 : $5; }
1643 | INT '(' any_expr ')'
1644 { $$ = $3 < 0 ? -floor(-$3) : floor($3); }
1645 | RAND '(' any_expr ')'
1646 { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
1649 /* return a random number in the range [0,1) */
1650 /* portable, but not very random */
1651 $$ = (rand() & 0x7fff) / double(0x8000);
1653 | SRAND '(' any_expr ')'
1656 srand((unsigned int)$3);
1658 | expr LESSEQUAL expr
1659 { $$ = ($1 <= $3); }
1662 | expr GREATEREQUAL expr
1663 { $$ = ($1 >= $3); }
1664 | expr EQUALEQUAL expr
1665 { $$ = ($1 == $3); }
1666 | expr NOTEQUAL expr
1667 { $$ = ($1 != $3); }
1669 { $$ = ($1 != 0.0 && $3 != 0.0); }
1671 { $$ = ($1 != 0.0 || $3 != 0.0); }
1673 { $$ = ($2 == 0.0); }
1679 /* bison defines const to be empty unless __STDC__ is defined, which it
1680 isn't under cfront */
1689 int scaled; // non-zero if val should be multiplied by scale
1690 } defaults_table[] = {
1691 { "arcrad", .25, 1 },
1692 { "arrowht", .1, 1 },
1693 { "arrowwid", .05, 1 },
1694 { "circlerad", .25, 1 },
1696 { "boxwid", .75, 1 },
1697 { "boxrad", 0.0, 1 },
1698 { "dashwid", .05, 1 },
1699 { "ellipseht", .5, 1 },
1700 { "ellipsewid", .75, 1 },
1701 { "moveht", .5, 1 },
1702 { "movewid", .5, 1 },
1703 { "lineht", .5, 1 },
1704 { "linewid", .5, 1 },
1705 { "textht", 0.0, 1 },
1706 { "textwid", 0.0, 1 },
1707 { "scale", 1.0, 0 },
1708 { "linethick", -1.0, 0 }, // in points
1709 { "fillval", .5, 0 },
1710 { "arrowhead", 1.0, 0 },
1711 { "maxpswid", 8.5, 0 },
1712 { "maxpsht", 11.0, 0 },
1715 place *lookup_label(const char *label)
1717 saved_state *state = current_saved_state;
1718 PTABLE(place) *tbl = current_table;
1720 place *pl = tbl->lookup(label);
1726 state = state->prev;
1730 void define_label(const char *label, const place *pl)
1732 place *p = new place[1];
1734 current_table->define(label, p);
1737 int lookup_variable(const char *name, double *val)
1739 place *pl = lookup_label(name);
1747 void define_variable(const char *name, double val)
1749 place *p = new place[1];
1753 current_table->define(name, p);
1754 if (strcmp(name, "scale") == 0) {
1755 // When the scale changes, reset all scaled pre-defined variables to
1756 // their default values.
1757 for (unsigned int i = 0;
1758 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1759 if (defaults_table[i].scaled)
1760 define_variable(defaults_table[i].name, val*defaults_table[i].val);
1764 // called once only (not once per parse)
1768 current_direction = RIGHT_DIRECTION;
1769 current_position.x = 0.0;
1770 current_position.y = 0.0;
1771 // This resets everything to its default value.
1775 void reset(const char *nm)
1777 for (unsigned int i = 0;
1778 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1779 if (strcmp(nm, defaults_table[i].name) == 0) {
1780 double val = defaults_table[i].val;
1781 if (defaults_table[i].scaled) {
1783 lookup_variable("scale", &scale);
1786 define_variable(defaults_table[i].name, val);
1789 lex_error("'%1' is not a predefined variable", nm);
1794 // We only have to explicitly reset the pre-defined variables that
1795 // aren't scaled because 'scale' is not scaled, and changing the
1796 // value of 'scale' will reset all the pre-defined variables that
1798 for (unsigned int i = 0;
1799 i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1800 if (!defaults_table[i].scaled)
1801 define_variable(defaults_table[i].name, defaults_table[i].val);
1804 // called after each parse
1806 void parse_cleanup()
1808 while (current_saved_state != 0) {
1809 delete current_table;
1810 current_table = current_saved_state->tbl;
1811 saved_state *tem = current_saved_state;
1812 current_saved_state = current_saved_state->prev;
1815 assert(current_table == &top_table);
1816 PTABLE_ITERATOR(place) iter(current_table);
1819 while (iter.next(&key, &pl))
1821 position pos = pl->obj->origin();
1826 while (olist.head != 0) {
1827 object *tem = olist.head;
1828 olist.head = olist.head->next;
1832 current_direction = RIGHT_DIRECTION;
1833 current_position.x = 0.0;
1834 current_position.y = 0.0;
1837 const char *ordinal_postfix(int n)
1839 if (n < 10 || n > 20)
1851 const char *object_type_name(object_type type)
1858 case ELLIPSE_OBJECT:
1882 static char sprintf_buf[1024];
1884 char *format_number(const char *form, double n)
1888 return do_sprintf(form, &n, 1);
1891 char *do_sprintf(const char *form, const double *v, int nv)
1898 one_format += *form++;
1899 for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
1900 one_format += *form;
1901 if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
1902 lex_error("bad sprintf format");
1903 result += one_format;
1908 one_format += *form++;
1910 snprintf(sprintf_buf, sizeof(sprintf_buf),
1911 "%s", one_format.contents());
1915 lex_error("too few arguments to snprintf");
1916 result += one_format;
1920 one_format += *form++;
1922 snprintf(sprintf_buf, sizeof(sprintf_buf),
1923 one_format.contents(), v[i++]);
1926 result += sprintf_buf;
1932 return strsave(result.contents());