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());