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