groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / preproc / pic / pic.y
1 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
2                  2006, 2007, 2009
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 %{
21 #include "pic.h"
22 #include "ptable.h"
23 #include "object.h"
24
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();
32
33 /* Maximum number of characters produced by printf("%g") */
34 #define GDIGITS 14
35
36 int yylex();
37 void yyerror(const char *);
38
39 void reset(const char *nm);
40 void reset_all();
41
42 place *lookup_label(const char *);
43 void define_label(const char *label, const place *pl);
44
45 direction current_direction;
46 position current_position;
47
48 implement_ptable(place)
49
50 PTABLE(place) top_table;
51
52 PTABLE(place) *current_table = &top_table;
53 saved_state *current_saved_state = 0;
54
55 object_list olist;
56
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);
61
62 %}
63
64
65 %union {
66         char *str;
67         int n;
68         double x;
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;
74         place pl;
75         object *obj;
76         corner crn;
77         path *pth;
78         object_spec *spec;
79         saved_state *pstate;
80         graphics_state state;
81         object_type obtype;
82 }
83
84 %token <str> LABEL
85 %token <str> VARIABLE
86 %token <x> NUMBER
87 %token <lstr> TEXT
88 %token <lstr> COMMAND_LINE
89 %token <str> DELIMITED
90 %token <n> ORDINAL
91 %token TH
92 %token LEFT_ARROW_HEAD
93 %token RIGHT_ARROW_HEAD
94 %token DOUBLE_ARROW_HEAD
95 %token LAST
96 %token BOX
97 %token CIRCLE
98 %token ELLIPSE
99 %token ARC
100 %token LINE
101 %token ARROW
102 %token MOVE
103 %token SPLINE
104 %token HEIGHT
105 %token RADIUS
106 %token FIGNAME
107 %token WIDTH
108 %token DIAMETER
109 %token UP
110 %token DOWN
111 %token RIGHT
112 %token LEFT
113 %token FROM
114 %token TO
115 %token AT
116 %token WITH
117 %token BY
118 %token THEN
119 %token SOLID
120 %token DOTTED
121 %token DASHED
122 %token CHOP
123 %token SAME
124 %token INVISIBLE
125 %token LJUST
126 %token RJUST
127 %token ABOVE
128 %token BELOW
129 %token OF
130 %token THE
131 %token WAY
132 %token BETWEEN
133 %token AND
134 %token HERE
135 %token DOT_N
136 %token DOT_E    
137 %token DOT_W
138 %token DOT_S
139 %token DOT_NE
140 %token DOT_SE
141 %token DOT_NW
142 %token DOT_SW
143 %token DOT_C
144 %token DOT_START
145 %token DOT_END
146 %token DOT_X
147 %token DOT_Y
148 %token DOT_HT
149 %token DOT_WID
150 %token DOT_RAD
151 %token SIN
152 %token COS
153 %token ATAN2
154 %token LOG
155 %token EXP
156 %token SQRT
157 %token K_MAX
158 %token K_MIN
159 %token INT
160 %token RAND
161 %token SRAND
162 %token COPY
163 %token THRU
164 %token TOP
165 %token BOTTOM
166 %token UPPER
167 %token LOWER
168 %token SH
169 %token PRINT
170 %token CW
171 %token CCW
172 %token FOR
173 %token DO
174 %token IF
175 %token ELSE
176 %token ANDAND
177 %token OROR
178 %token NOTEQUAL
179 %token EQUALEQUAL
180 %token LESSEQUAL
181 %token GREATEREQUAL
182 %token LEFT_CORNER
183 %token RIGHT_CORNER
184 %token NORTH
185 %token SOUTH
186 %token EAST
187 %token WEST
188 %token CENTER
189 %token END
190 %token START
191 %token RESET
192 %token UNTIL
193 %token PLOT
194 %token THICKNESS
195 %token FILL
196 %token COLORED
197 %token OUTLINED
198 %token SHADED
199 %token XSLANTED
200 %token YSLANTED
201 %token ALIGNED
202 %token SPRINTF
203 %token COMMAND
204
205 %token DEFINE
206 %token UNDEF
207
208 %left '.'
209
210 /* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
211 %left PLOT
212 %left TEXT SPRINTF
213
214 /* give text adjustments higher precedence than TEXT, so that
215 box "foo" above ljust == box ("foo" above ljust)
216 */
217
218 %left LJUST RJUST ABOVE BELOW
219
220 %left LEFT RIGHT
221 /* Give attributes that take an optional expression a higher
222 precedence than left and right, so that eg `line chop left'
223 parses properly. */
224 %left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
225 %left XSLANTED YSLANTED
226 %left LABEL
227
228 %left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND LAST 
229 %left ORDINAL HERE '`'
230
231 %left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '['
232
233 /* these need to be lower than '-' */
234 %left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS
235
236 /* these must have higher precedence than CHOP so that `label %prec CHOP'
237 works */
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
241
242 %left ','
243 %left OROR
244 %left ANDAND
245 %left EQUALEQUAL NOTEQUAL
246 %left '<' '>' LESSEQUAL GREATEREQUAL
247
248 %left BETWEEN OF
249 %left AND
250
251 %left '+' '-'
252 %left '*' '/' '%'
253 %right '!'
254 %right '^'
255
256 %type <x> expr 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
261 %type <crn> corner
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
271
272 %%
273
274 top:
275         optional_separator
276         | element_list
277                 {
278                   if (olist.head)
279                     print_picture(olist.head);
280                 }
281         ;
282
283
284 element_list:
285         optional_separator middle_element_list optional_separator
286                 { $$ = $2; }
287         ;
288
289 middle_element_list:
290         element
291                 { $$ = $1; }
292         | middle_element_list separator element
293                 { $$ = $1; }
294         ;
295
296 optional_separator:
297         /* empty */
298         | separator
299         ;
300
301 separator:
302         ';'
303         | separator ';'
304         ;
305
306 placeless_element:
307         FIGNAME '=' macro_name
308                 {
309                   a_delete graphname;
310                   graphname = new char[strlen($3) + 1];
311                   strcpy(graphname, $3);
312                   a_delete $3;
313                 }
314         |
315         VARIABLE '=' any_expr
316                 {
317                   define_variable($1, $3);
318                   a_delete $1;
319                 }
320         | VARIABLE ':' '=' any_expr
321                 {
322                   place *p = lookup_label($1);
323                   if (!p) {
324                     lex_error("variable `%1' not defined", $1);
325                     YYABORT;
326                   }
327                   p->obj = 0;
328                   p->x = $4;
329                   p->y = 0.0;
330                   a_delete $1;
331                 }
332         | UP
333                 { current_direction = UP_DIRECTION; }
334         | DOWN
335                 { current_direction = DOWN_DIRECTION; }
336         | LEFT
337                 { current_direction = LEFT_DIRECTION; }
338         | RIGHT
339                 { current_direction = RIGHT_DIRECTION; }
340         | COMMAND_LINE
341                 {
342                   olist.append(make_command_object($1.str, $1.filename,
343                                                    $1.lineno));
344                 }
345         | COMMAND print_args
346                 {
347                   olist.append(make_command_object($2.str, $2.filename,
348                                                    $2.lineno));
349                 }
350         | PRINT print_args
351                 {
352                   fprintf(stderr, "%s\n", $2.str);
353                   a_delete $2.str;
354                   fflush(stderr);
355                 }
356         | SH
357                 { delim_flag = 1; }
358           DELIMITED
359                 {
360                   delim_flag = 0;
361                   if (safer_flag)
362                     lex_error("unsafe to run command `%1'", $3);
363                   else
364                     system($3);
365                   a_delete $3;
366                 }
367         | COPY TEXT
368                 {
369                   if (yychar < 0)
370                     do_lookahead();
371                   do_copy($2.str);
372                   // do not delete the filename
373                 }
374         | COPY TEXT THRU
375                 { delim_flag = 2; }
376           DELIMITED 
377                 { delim_flag = 0; }
378           until
379                 {
380                   if (yychar < 0)
381                     do_lookahead();
382                   copy_file_thru($2.str, $5, $7);
383                   // do not delete the filename
384                   a_delete $5;
385                   a_delete $7;
386                 }
387         | COPY THRU
388                 { delim_flag = 2; }
389           DELIMITED
390                 { delim_flag = 0; }
391           until
392                 {
393                   if (yychar < 0)
394                     do_lookahead();
395                   copy_rest_thru($4, $6);
396                   a_delete $4;
397                   a_delete $6;
398                 }
399         | FOR VARIABLE '=' expr TO expr optional_by DO
400                 { delim_flag = 1; }
401           DELIMITED
402                 {
403                   delim_flag = 0;
404                   if (yychar < 0)
405                     do_lookahead();
406                   do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10); 
407                 }
408         | simple_if
409                 {
410                   if (yychar < 0)
411                     do_lookahead();
412                   if ($1.x != 0.0)
413                     push_body($1.body);
414                   a_delete $1.body;
415                 }
416         | simple_if ELSE
417                 { delim_flag = 1; }
418           DELIMITED
419                 {
420                   delim_flag = 0;
421                   if (yychar < 0)
422                     do_lookahead();
423                   if ($1.x != 0.0)
424                     push_body($1.body);
425                   else
426                     push_body($4);
427                   a_delete $1.body;
428                   a_delete $4;
429                 }
430         | reset_variables
431         | RESET
432                 { define_variable("scale", 1.0); }
433         ;
434
435 macro_name:
436         VARIABLE
437         | LABEL
438         ;
439
440 reset_variables:
441         RESET VARIABLE
442                 {
443                   reset($2);
444                   a_delete $2;
445                 }
446         | reset_variables VARIABLE
447                 {
448                   reset($2);
449                   a_delete $2;
450                 }
451         | reset_variables ',' VARIABLE
452                 {
453                   reset($3);
454                   a_delete $3;
455                 }
456         ;
457
458 print_args:
459         print_arg
460                 { $$ = $1; }
461         | print_args print_arg
462                 {
463                   $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
464                   strcpy($$.str, $1.str);
465                   strcat($$.str, $2.str);
466                   a_delete $1.str;
467                   a_delete $2.str;
468                   if ($1.filename) {
469                     $$.filename = $1.filename;
470                     $$.lineno = $1.lineno;
471                   }
472                   else if ($2.filename) {
473                     $$.filename = $2.filename;
474                     $$.lineno = $2.lineno;
475                   }
476                 }
477         ;
478
479 print_arg:
480         expr                                                    %prec ','
481                 {
482                   $$.str = new char[GDIGITS + 1];
483                   sprintf($$.str, "%g", $1);
484                   $$.filename = 0;
485                   $$.lineno = 0;
486                 }
487         | text
488                 { $$ = $1; }
489         | position                                              %prec ','
490                 {
491                   $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
492                   sprintf($$.str, "%g, %g", $1.x, $1.y);
493                   $$.filename = 0;
494                   $$.lineno = 0;
495                 }
496         ;
497
498 simple_if:
499         IF any_expr THEN
500                 { delim_flag = 1; }
501         DELIMITED
502                 {
503                   delim_flag = 0;
504                   $$.x = $2;
505                   $$.body = $5;
506                 }
507         ;
508
509 until:
510         /* empty */
511                 { $$ = 0; }
512         | UNTIL TEXT
513                 { $$ = $2.str; }
514         ;
515         
516 any_expr:
517         expr
518                 { $$ = $1; }
519         | text_expr
520                 { $$ = $1; }
521         ;
522         
523 text_expr:
524         text EQUALEQUAL text
525                 {
526                   $$ = strcmp($1.str, $3.str) == 0;
527                   a_delete $1.str;
528                   a_delete $3.str;
529                 }
530         | text NOTEQUAL text
531                 {
532                   $$ = strcmp($1.str, $3.str) != 0;
533                   a_delete $1.str;
534                   a_delete $3.str;
535                 }
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); }
548         | '!' text_expr
549                 { $$ = ($2 == 0.0); }
550         ;
551
552
553 optional_by:
554         /* empty */
555                 {
556                   $$.val = 1.0;
557                   $$.is_multiplicative = 0;
558                 }
559         | BY expr
560                 {
561                   $$.val = $2;
562                   $$.is_multiplicative = 0;
563                 }
564         | BY '*' expr
565                 {
566                   $$.val = $3;
567                   $$.is_multiplicative = 1;
568                 }
569         ;
570
571 element:
572         object_spec
573                 {
574                   $$.obj = $1->make_object(&current_position,
575                                            &current_direction);
576                   if ($$.obj == 0)
577                     YYABORT;
578                   delete $1;
579                   if ($$.obj)
580                     olist.append($$.obj);
581                   else {
582                     $$.x = current_position.x;
583                     $$.y = current_position.y;
584                   }
585                 }
586         | LABEL ':' optional_separator element
587                 {
588                   $$ = $4;
589                   define_label($1, & $$);
590                   a_delete $1;
591                 }
592         | LABEL ':' optional_separator position_not_place
593                 {
594                   $$.obj = 0;
595                   $$.x = $4.x;
596                   $$.y = $4.y;
597                   define_label($1, & $$);
598                   a_delete $1;
599                 }
600         | LABEL ':' optional_separator place
601                 {
602                   $$ = $4;
603                   define_label($1, & $$);
604                   a_delete $1;
605                 }
606         | '{'
607                 {
608                   $<state>$.x = current_position.x;
609                   $<state>$.y = current_position.y;
610                   $<state>$.dir = current_direction;
611                 }
612           element_list '}'
613                 {
614                   current_position.x = $<state>2.x;
615                   current_position.y = $<state>2.y;
616                   current_direction = $<state>2.dir;
617                 }
618           optional_element
619                 {
620                   $$ = $3;
621                 }
622         | placeless_element
623                 {
624                   $$.obj = 0;
625                   $$.x = current_position.x;
626                   $$.y = current_position.y;
627                 }
628         ;
629
630 optional_element:
631         /* empty */
632                 {}
633         | element
634                 {}
635         ;
636
637 object_spec:
638         BOX
639                 { $$ = new object_spec(BOX_OBJECT); }
640         | CIRCLE
641                 { $$ = new object_spec(CIRCLE_OBJECT); }
642         | ELLIPSE
643                 { $$ = new object_spec(ELLIPSE_OBJECT); }
644         | ARC
645                 {
646                   $$ = new object_spec(ARC_OBJECT);
647                   $$->dir = current_direction;
648                 }
649         | LINE
650                 {
651                   $$ = new object_spec(LINE_OBJECT);
652                   lookup_variable("lineht", & $$->segment_height);
653                   lookup_variable("linewid", & $$->segment_width);
654                   $$->dir = current_direction;
655                 }
656         | ARROW
657                 {
658                   $$ = new object_spec(ARROW_OBJECT);
659                   lookup_variable("lineht", & $$->segment_height);
660                   lookup_variable("linewid", & $$->segment_width);
661                   $$->dir = current_direction;
662                 }
663         | MOVE
664                 {
665                   $$ = new object_spec(MOVE_OBJECT);
666                   lookup_variable("moveht", & $$->segment_height);
667                   lookup_variable("movewid", & $$->segment_width);
668                   $$->dir = current_direction;
669                 }
670         | SPLINE
671                 {
672                   $$ = new object_spec(SPLINE_OBJECT);
673                   lookup_variable("lineht", & $$->segment_height);
674                   lookup_variable("linewid", & $$->segment_width);
675                   $$->dir = current_direction;
676                 }
677         | text                                                  %prec TEXT
678                 {
679                   $$ = new object_spec(TEXT_OBJECT);
680                   $$->text = new text_item($1.str, $1.filename, $1.lineno);
681                 }
682         | PLOT expr
683                 {
684                   $$ = new object_spec(TEXT_OBJECT);
685                   $$->text = new text_item(format_number(0, $2), 0, -1);
686                 }
687         | PLOT expr text
688                 {
689                   $$ = new object_spec(TEXT_OBJECT);
690                   $$->text = new text_item(format_number($3.str, $2),
691                                            $3.filename, $3.lineno);
692                   a_delete $3.str;
693                 }
694         | '[' 
695                 {
696                   saved_state *p = new saved_state;
697                   $<pstate>$ = p;
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());
708                 }
709           element_list ']'
710                 {
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;
719                   delete $<pstate>2;
720                 }
721         | object_spec HEIGHT expr
722                 {
723                   $$ = $1;
724                   $$->height = $3;
725                   $$->flags |= HAS_HEIGHT;
726                 }
727         | object_spec RADIUS expr
728                 {
729                   $$ = $1;
730                   $$->radius = $3;
731                   $$->flags |= HAS_RADIUS;
732                 }
733         | object_spec WIDTH expr
734                 {
735                   $$ = $1;
736                   $$->width = $3;
737                   $$->flags |= HAS_WIDTH;
738                 }
739         | object_spec DIAMETER expr
740                 {
741                   $$ = $1;
742                   $$->radius = $3/2.0;
743                   $$->flags |= HAS_RADIUS;
744                 }
745         | object_spec expr                                      %prec HEIGHT
746                 {
747                   $$ = $1;
748                   $$->flags |= HAS_SEGMENT;
749                   switch ($$->dir) {
750                   case UP_DIRECTION:
751                     $$->segment_pos.y += $2;
752                     break;
753                   case DOWN_DIRECTION:
754                     $$->segment_pos.y -= $2;
755                     break;
756                   case RIGHT_DIRECTION:
757                     $$->segment_pos.x += $2;
758                     break;
759                   case LEFT_DIRECTION:
760                     $$->segment_pos.x -= $2;
761                     break;
762                   }
763                 }
764         | object_spec UP
765                 {
766                   $$ = $1;
767                   $$->dir = UP_DIRECTION;
768                   $$->flags |= HAS_SEGMENT;
769                   $$->segment_pos.y += $$->segment_height;
770                 }
771         | object_spec UP expr
772                 {
773                   $$ = $1;
774                   $$->dir = UP_DIRECTION;
775                   $$->flags |= HAS_SEGMENT;
776                   $$->segment_pos.y += $3;
777                 }
778         | object_spec DOWN
779                 {
780                   $$ = $1;
781                   $$->dir = DOWN_DIRECTION;
782                   $$->flags |= HAS_SEGMENT;
783                   $$->segment_pos.y -= $$->segment_height;
784                 }
785         | object_spec DOWN expr
786                 {
787                   $$ = $1;
788                   $$->dir = DOWN_DIRECTION;
789                   $$->flags |= HAS_SEGMENT;
790                   $$->segment_pos.y -= $3;
791                 }
792         | object_spec RIGHT
793                 {
794                   $$ = $1;
795                   $$->dir = RIGHT_DIRECTION;
796                   $$->flags |= HAS_SEGMENT;
797                   $$->segment_pos.x += $$->segment_width;
798                 }
799         | object_spec RIGHT expr
800                 {
801                   $$ = $1;
802                   $$->dir = RIGHT_DIRECTION;
803                   $$->flags |= HAS_SEGMENT;
804                   $$->segment_pos.x += $3;
805                 }
806         | object_spec LEFT
807                 {
808                   $$ = $1;
809                   $$->dir = LEFT_DIRECTION;
810                   $$->flags |= HAS_SEGMENT;
811                   $$->segment_pos.x -= $$->segment_width;
812                 }
813         | object_spec LEFT expr
814                 {
815                   $$ = $1;
816                   $$->dir = LEFT_DIRECTION;
817                   $$->flags |= HAS_SEGMENT;
818                   $$->segment_pos.x -= $3;
819                 }
820         | object_spec FROM position
821                 {
822                   $$ = $1;
823                   $$->flags |= HAS_FROM;
824                   $$->from.x = $3.x;
825                   $$->from.y = $3.y;
826                 }
827         | object_spec TO position
828                 {
829                   $$ = $1;
830                   if ($$->flags & HAS_SEGMENT)
831                     $$->segment_list = new segment($$->segment_pos,
832                                                    $$->segment_is_absolute,
833                                                    $$->segment_list);
834                   $$->flags |= HAS_SEGMENT;
835                   $$->segment_pos.x = $3.x;
836                   $$->segment_pos.y = $3.y;
837                   $$->segment_is_absolute = 1;
838                   $$->flags |= HAS_TO;
839                   $$->to.x = $3.x;
840                   $$->to.y = $3.y;
841                 }
842         | object_spec AT position
843                 {
844                   $$ = $1;
845                   $$->flags |= HAS_AT;
846                   $$->at.x = $3.x;
847                   $$->at.y = $3.y;
848                   if ($$->type != ARC_OBJECT) {
849                     $$->flags |= HAS_FROM;
850                     $$->from.x = $3.x;
851                     $$->from.y = $3.y;
852                   }
853                 }
854         | object_spec WITH path
855                 {
856                   $$ = $1;
857                   $$->flags |= HAS_WITH;
858                   $$->with = $3;
859                 }
860         | object_spec WITH position                             %prec ','
861                 {
862                   $$ = $1;
863                   $$->flags |= HAS_WITH;
864                   position pos;
865                   pos.x = $3.x;
866                   pos.y = $3.y;
867                   $$->with = new path(pos);
868                 }
869         | object_spec BY expr_pair
870                 {
871                   $$ = $1;
872                   $$->flags |= HAS_SEGMENT;
873                   $$->segment_pos.x += $3.x;
874                   $$->segment_pos.y += $3.y;
875                 }
876         | object_spec THEN
877                 {
878                   $$ = $1;
879                   if (!($$->flags & HAS_SEGMENT))
880                     switch ($$->dir) {
881                     case UP_DIRECTION:
882                       $$->segment_pos.y += $$->segment_width;
883                       break;
884                     case DOWN_DIRECTION:
885                       $$->segment_pos.y -= $$->segment_width;
886                       break;
887                     case RIGHT_DIRECTION:
888                       $$->segment_pos.x += $$->segment_width;
889                       break;
890                     case LEFT_DIRECTION:
891                       $$->segment_pos.x -= $$->segment_width;
892                       break;
893                     }
894                   $$->segment_list = new segment($$->segment_pos,
895                                                  $$->segment_is_absolute,
896                                                  $$->segment_list);
897                   $$->flags &= ~HAS_SEGMENT;
898                   $$->segment_pos.x = $$->segment_pos.y = 0.0;
899                   $$->segment_is_absolute = 0;
900                 }
901         | object_spec SOLID
902                 {
903                   $$ = $1;      // nothing
904                 }
905         | object_spec DOTTED
906                 {
907                   $$ = $1;
908                   $$->flags |= IS_DOTTED;
909                   lookup_variable("dashwid", & $$->dash_width);
910                 }
911         | object_spec DOTTED expr
912                 {
913                   $$ = $1;
914                   $$->flags |= IS_DOTTED;
915                   $$->dash_width = $3;
916                 }
917         | object_spec DASHED
918                 {
919                   $$ = $1;
920                   $$->flags |= IS_DASHED;
921                   lookup_variable("dashwid", & $$->dash_width);
922                 }
923         | object_spec DASHED expr
924                 {
925                   $$ = $1;
926                   $$->flags |= IS_DASHED;
927                   $$->dash_width = $3;
928                 }
929         | object_spec FILL
930                 {
931                   $$ = $1;
932                   $$->flags |= IS_DEFAULT_FILLED;
933                 }
934         | object_spec FILL expr
935                 {
936                   $$ = $1;
937                   $$->flags |= IS_FILLED;
938                   $$->fill = $3;
939                 }
940         | object_spec XSLANTED expr
941                 {
942                   $$ = $1;
943                   $$->flags |= IS_XSLANTED;
944                   $$->xslanted = $3;
945                 }
946         | object_spec YSLANTED expr
947                 {
948                   $$ = $1;
949                   $$->flags |= IS_YSLANTED;
950                   $$->yslanted = $3;
951                 }
952         | object_spec SHADED text
953                 {
954                   $$ = $1;
955                   $$->flags |= (IS_SHADED | IS_FILLED);
956                   $$->shaded = new char[strlen($3.str)+1];
957                   strcpy($$->shaded, $3.str);
958                 }
959         | object_spec COLORED text
960                 {
961                   $$ = $1;
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);
967                 }
968         | object_spec OUTLINED text
969                 {
970                   $$ = $1;
971                   $$->flags |= IS_OUTLINED;
972                   $$->outlined = new char[strlen($3.str)+1];
973                   strcpy($$->outlined, $3.str);
974                 }
975         | object_spec CHOP
976                 {
977                   $$ = $1;
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;
983                   }
984                   else if ($$->flags & IS_CHOPPED) {
985                     $$->end_chop = 0.0;
986                   }
987                   else {
988                     $$->flags |= IS_DEFAULT_CHOPPED;
989                   }
990                 }
991         | object_spec CHOP expr
992                 {
993                   $$ = $1;
994                   if ($$->flags & IS_DEFAULT_CHOPPED) {
995                     $$->flags |= IS_CHOPPED;
996                     $$->flags &= ~IS_DEFAULT_CHOPPED;
997                     $$->start_chop = 0.0;
998                     $$->end_chop = $3;
999                   }
1000                   else if ($$->flags & IS_CHOPPED) {
1001                     $$->end_chop = $3;
1002                   }
1003                   else {
1004                     $$->start_chop = $$->end_chop = $3;
1005                     $$->flags |= IS_CHOPPED;
1006                   }
1007                 }
1008         | object_spec SAME
1009                 {
1010                   $$ = $1;
1011                   $$->flags |= IS_SAME;
1012                 }
1013         | object_spec INVISIBLE
1014                 {
1015                   $$ = $1;
1016                   $$->flags |= IS_INVISIBLE;
1017                 }
1018         | object_spec LEFT_ARROW_HEAD
1019                 {
1020                   $$ = $1;
1021                   $$->flags |= HAS_LEFT_ARROW_HEAD;
1022                 }
1023         | object_spec RIGHT_ARROW_HEAD
1024                 {
1025                   $$ = $1;
1026                   $$->flags |= HAS_RIGHT_ARROW_HEAD;
1027                 }
1028         | object_spec DOUBLE_ARROW_HEAD
1029                 {
1030                   $$ = $1;
1031                   $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
1032                 }
1033         | object_spec CW
1034                 {
1035                   $$ = $1;
1036                   $$->flags |= IS_CLOCKWISE;
1037                 }
1038         | object_spec CCW
1039                 {
1040                   $$ = $1;
1041                   $$->flags &= ~IS_CLOCKWISE;
1042                 }
1043         | object_spec text                                      %prec TEXT
1044                 {
1045                   $$ = $1;
1046                   text_item **p;
1047                   for (p = & $$->text; *p; p = &(*p)->next)
1048                     ;
1049                   *p = new text_item($2.str, $2.filename, $2.lineno);
1050                 }
1051         | object_spec LJUST
1052                 {
1053                   $$ = $1;
1054                   if ($$->text) {
1055                     text_item *p;
1056                     for (p = $$->text; p->next; p = p->next)
1057                       ;
1058                     p->adj.h = LEFT_ADJUST;
1059                   }
1060                 }
1061         | object_spec RJUST
1062                 {
1063                   $$ = $1;
1064                   if ($$->text) {
1065                     text_item *p;
1066                     for (p = $$->text; p->next; p = p->next)
1067                       ;
1068                     p->adj.h = RIGHT_ADJUST;
1069                   }
1070                 }
1071         | object_spec ABOVE
1072                 {
1073                   $$ = $1;
1074                   if ($$->text) {
1075                     text_item *p;
1076                     for (p = $$->text; p->next; p = p->next)
1077                       ;
1078                     p->adj.v = ABOVE_ADJUST;
1079                   }
1080                 }
1081         | object_spec BELOW
1082                 {
1083                   $$ = $1;
1084                   if ($$->text) {
1085                     text_item *p;
1086                     for (p = $$->text; p->next; p = p->next)
1087                       ;
1088                     p->adj.v = BELOW_ADJUST;
1089                   }
1090                 }
1091         | object_spec THICKNESS expr
1092                 {
1093                   $$ = $1;
1094                   $$->flags |= HAS_THICKNESS;
1095                   $$->thickness = $3;
1096                 }
1097         | object_spec ALIGNED
1098                 {
1099                   $$ = $1;
1100                   $$->flags |= IS_ALIGNED;
1101                 }
1102         ;
1103
1104 text:
1105         TEXT
1106                 { $$ = $1; }
1107         | SPRINTF '(' TEXT sprintf_args ')'
1108                 {
1109                   $$.filename = $3.filename;
1110                   $$.lineno = $3.lineno;
1111                   $$.str = do_sprintf($3.str, $4.v, $4.nv);
1112                   a_delete $4.v;
1113                   a_delete $3.str;
1114                 }
1115         ;
1116
1117 sprintf_args:
1118         /* empty */
1119                 {
1120                   $$.v = 0;
1121                   $$.nv = 0;
1122                   $$.maxv = 0;
1123                 }
1124         | sprintf_args ',' expr
1125                 {
1126                   $$ = $1;
1127                   if ($$.nv >= $$.maxv) {
1128                     if ($$.nv == 0) {
1129                       $$.v = new double[4];
1130                       $$.maxv = 4;
1131                     }
1132                     else {
1133                       double *oldv = $$.v;
1134                       $$.maxv *= 2;
1135 #if 0
1136                       $$.v = new double[$$.maxv];
1137                       memcpy($$.v, oldv, $$.nv*sizeof(double));
1138 #else
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));
1143                       $$.v = foo;
1144 #endif
1145                       a_delete oldv;
1146                     }
1147                   }
1148                   $$.v[$$.nv] = $3;
1149                   $$.nv += 1;
1150                 }
1151         ;
1152
1153 position:
1154         position_not_place
1155                 { $$ = $1; }
1156         | place
1157                 {
1158                   position pos = $1;
1159                   $$.x = pos.x;
1160                   $$.y = pos.y;
1161                 }
1162         | '(' place ')'
1163                 {
1164                   position pos = $2;
1165                   $$.x = pos.x;
1166                   $$.y = pos.y;
1167                 }
1168         ;
1169
1170 position_not_place:
1171         expr_pair
1172                 { $$ = $1; }
1173         | position '+' expr_pair
1174                 {
1175                   $$.x = $1.x + $3.x;
1176                   $$.y = $1.y + $3.y;
1177                 }
1178         | '(' position '+' expr_pair ')'
1179                 {
1180                   $$.x = $2.x + $4.x;
1181                   $$.y = $2.y + $4.y;
1182                 }
1183         | position '-' expr_pair
1184                 {
1185                   $$.x = $1.x - $3.x;
1186                   $$.y = $1.y - $3.y;
1187                 }
1188         | '(' position '-' expr_pair ')'
1189                 {
1190                   $$.x = $2.x - $4.x;
1191                   $$.y = $2.y - $4.y;
1192                 }
1193         | '(' position ',' position ')'
1194                 {
1195                   $$.x = $2.x;
1196                   $$.y = $4.y;
1197                 }
1198         | expr between position AND position
1199                 {
1200                   $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1201                   $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1202                 }
1203         | '(' expr between position AND position ')'
1204                 {
1205                   $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1206                   $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1207                 }
1208         | expr '<' position ',' position '>'
1209                 {
1210                   $$.x = (1.0 - $1)*$3.x + $1*$5.x;
1211                   $$.y = (1.0 - $1)*$3.y + $1*$5.y;
1212                 }
1213         | '(' expr '<' position ',' position '>' ')'
1214                 {
1215                   $$.x = (1.0 - $2)*$4.x + $2*$6.x;
1216                   $$.y = (1.0 - $2)*$4.y + $2*$6.y;
1217                 }
1218         ;
1219
1220 between:
1221         BETWEEN
1222         | OF THE WAY BETWEEN
1223         ;
1224
1225 expr_pair:
1226         expr ',' expr
1227                 {
1228                   $$.x = $1;
1229                   $$.y = $3;
1230                 }
1231         | '(' expr_pair ')'
1232                 { $$ = $2; }
1233         ;
1234
1235 place:
1236         /* line at A left == line (at A) left */
1237         label                                                   %prec CHOP
1238                 { $$ = $1; }
1239         | label corner
1240                 {
1241                   path pth($2);
1242                   if (!pth.follow($1, & $$))
1243                     YYABORT;
1244                 }
1245         | corner label
1246                 {
1247                   path pth($1);
1248                   if (!pth.follow($2, & $$))
1249                     YYABORT;
1250                 }
1251         | corner OF label
1252                 {
1253                   path pth($1);
1254                   if (!pth.follow($3, & $$))
1255                     YYABORT;
1256                 }
1257         | HERE
1258                 {
1259                   $$.x = current_position.x;
1260                   $$.y = current_position.y;
1261                   $$.obj = 0;
1262                 }
1263         ;
1264
1265 label:
1266         LABEL
1267                 {
1268                   place *p = lookup_label($1);
1269                   if (!p) {
1270                     lex_error("there is no place `%1'", $1);
1271                     YYABORT;
1272                   }
1273                   $$ = *p;
1274                   a_delete $1;
1275                 }
1276         | nth_primitive
1277                 { $$.obj = $1; }
1278         | label '.' LABEL
1279                 {
1280                   path pth($3);
1281                   if (!pth.follow($1, & $$))
1282                     YYABORT;
1283                 }
1284         ;
1285
1286 ordinal:
1287         ORDINAL
1288                 { $$ = $1; }
1289         | '`' any_expr TH
1290                 {
1291                   // XXX Check for overflow (and non-integers?).
1292                   $$ = (int)$2;
1293                 }
1294         ;
1295
1296 optional_ordinal_last:
1297         LAST
1298                 { $$ = 1; }
1299         | ordinal LAST
1300                 { $$ = $1; }
1301         ;
1302
1303 nth_primitive:
1304         ordinal object_type
1305                 {
1306                   int count = 0;
1307                   object *p;
1308                   for (p = olist.head; p != 0; p = p->next)
1309                     if (p->type() == $2 && ++count == $1) {
1310                       $$ = p;
1311                       break;
1312                     }
1313                   if (p == 0) {
1314                     lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
1315                               object_type_name($2));
1316                     YYABORT;
1317                   }
1318                 }
1319         | optional_ordinal_last object_type
1320                 {
1321                   int count = 0;
1322                   object *p;
1323                   for (p = olist.tail; p != 0; p = p->prev)
1324                     if (p->type() == $2 && ++count == $1) {
1325                       $$ = p;
1326                       break;
1327                     }
1328                   if (p == 0) {
1329                     lex_error("there is no %1%2 last %3", $1,
1330                               ordinal_postfix($1), object_type_name($2));
1331                     YYABORT;
1332                   }
1333                 }
1334         ;
1335
1336 object_type:
1337         BOX
1338                 { $$ = BOX_OBJECT; }
1339         | CIRCLE
1340                 { $$ = CIRCLE_OBJECT; }
1341         | ELLIPSE
1342                 { $$ = ELLIPSE_OBJECT; }
1343         | ARC
1344                 { $$ = ARC_OBJECT; }
1345         | LINE
1346                 { $$ = LINE_OBJECT; }
1347         | ARROW
1348                 { $$ = ARROW_OBJECT; }
1349         | SPLINE
1350                 { $$ = SPLINE_OBJECT; }
1351         | '[' ']'
1352                 { $$ = BLOCK_OBJECT; }
1353         | TEXT
1354                 { $$ = TEXT_OBJECT; }
1355         ;
1356
1357 label_path:
1358         '.' LABEL
1359                 { $$ = new path($2); }
1360         | label_path '.' LABEL
1361                 {
1362                   $$ = $1;
1363                   $$->append($3);
1364                 }
1365         ;
1366
1367 relative_path:
1368         corner                                                  %prec CHOP
1369                 { $$ = new path($1); }
1370         /* give this a lower precedence than LEFT and RIGHT so that
1371            [A: box] with .A left == [A: box] with (.A left) */
1372         | label_path                                            %prec TEXT
1373                 { $$ = $1; }
1374         | label_path corner
1375                 {
1376                   $$ = $1;
1377                   $$->append($2);
1378                 }
1379         ;
1380
1381 path:
1382         relative_path
1383                 { $$ = $1; }
1384         | '(' relative_path ',' relative_path ')'
1385                 {
1386                   $$ = $2;
1387                   $$->set_ypath($4);
1388                 }
1389         /* The rest of these rules are a compatibility sop. */
1390         | ORDINAL LAST object_type relative_path
1391                 {
1392                   lex_warning("`%1%2 last %3' in `with' argument ignored",
1393                               $1, ordinal_postfix($1), object_type_name($3));
1394                   $$ = $4;
1395                 }
1396         | LAST object_type relative_path
1397                 {
1398                   lex_warning("`last %1' in `with' argument ignored",
1399                               object_type_name($2));
1400                   $$ = $3;
1401                 }
1402         | ORDINAL object_type relative_path
1403                 {
1404                   lex_warning("`%1%2 %3' in `with' argument ignored",
1405                               $1, ordinal_postfix($1), object_type_name($2));
1406                   $$ = $3;
1407                 }
1408         | LABEL relative_path
1409                 {
1410                   lex_warning("initial `%1' in `with' argument ignored", $1);
1411                   a_delete $1;
1412                   $$ = $2;
1413                 }
1414         ;
1415
1416 corner:
1417         DOT_N
1418                 { $$ = &object::north; }
1419         | DOT_E 
1420                 { $$ = &object::east; }
1421         | DOT_W
1422                 { $$ = &object::west; }
1423         | DOT_S
1424                 { $$ = &object::south; }
1425         | DOT_NE
1426                 { $$ = &object::north_east; }
1427         | DOT_SE
1428                 { $$ = &object:: south_east; }
1429         | DOT_NW
1430                 { $$ = &object::north_west; }
1431         | DOT_SW
1432                 { $$ = &object::south_west; }
1433         | DOT_C
1434                 { $$ = &object::center; }
1435         | DOT_START
1436                 { $$ = &object::start; }
1437         | DOT_END
1438                 { $$ = &object::end; }
1439         | TOP
1440                 { $$ = &object::north; }
1441         | BOTTOM
1442                 { $$ = &object::south; }
1443         | LEFT
1444                 { $$ = &object::west; }
1445         | RIGHT
1446                 { $$ = &object::east; }
1447         | UPPER LEFT
1448                 { $$ = &object::north_west; }
1449         | LOWER LEFT
1450                 { $$ = &object::south_west; }
1451         | UPPER RIGHT
1452                 { $$ = &object::north_east; }
1453         | LOWER RIGHT
1454                 { $$ = &object::south_east; }
1455         | LEFT_CORNER
1456                 { $$ = &object::west; }
1457         | RIGHT_CORNER
1458                 { $$ = &object::east; }
1459         | UPPER LEFT_CORNER
1460                 { $$ = &object::north_west; }
1461         | LOWER LEFT_CORNER
1462                 { $$ = &object::south_west; }
1463         | UPPER RIGHT_CORNER
1464                 { $$ = &object::north_east; }
1465         | LOWER RIGHT_CORNER
1466                 { $$ = &object::south_east; }
1467         | NORTH
1468                 { $$ = &object::north; }
1469         | SOUTH
1470                 { $$ = &object::south; }
1471         | EAST
1472                 { $$ = &object::east; }
1473         | WEST
1474                 { $$ = &object::west; }
1475         | CENTER
1476                 { $$ = &object::center; }
1477         | START
1478                 { $$ = &object::start; }
1479         | END
1480                 { $$ = &object::end; }
1481         ;
1482
1483 expr:
1484         VARIABLE
1485                 {
1486                   if (!lookup_variable($1, & $$)) {
1487                     lex_error("there is no variable `%1'", $1);
1488                     YYABORT;
1489                   }
1490                   a_delete $1;
1491                 }
1492         | NUMBER
1493                 { $$ = $1; }
1494         | place DOT_X
1495                 {
1496                   if ($1.obj != 0)
1497                     $$ = $1.obj->origin().x;
1498                   else
1499                     $$ = $1.x;
1500                 }                       
1501         | place DOT_Y
1502                 {
1503                   if ($1.obj != 0)
1504                     $$ = $1.obj->origin().y;
1505                   else
1506                     $$ = $1.y;
1507                 }
1508         | place DOT_HT
1509                 {
1510                   if ($1.obj != 0)
1511                     $$ = $1.obj->height();
1512                   else
1513                     $$ = 0.0;
1514                 }
1515         | place DOT_WID
1516                 {
1517                   if ($1.obj != 0)
1518                     $$ = $1.obj->width();
1519                   else
1520                     $$ = 0.0;
1521                 }
1522         | place DOT_RAD
1523                 {
1524                   if ($1.obj != 0)
1525                     $$ = $1.obj->radius();
1526                   else
1527                     $$ = 0.0;
1528                 }
1529         | expr '+' expr
1530                 { $$ = $1 + $3; }
1531         | expr '-' expr
1532                 { $$ = $1 - $3; }
1533         | expr '*' expr
1534                 { $$ = $1 * $3; }
1535         | expr '/' expr
1536                 {
1537                   if ($3 == 0.0) {
1538                     lex_error("division by zero");
1539                     YYABORT;
1540                   }
1541                   $$ = $1/$3;
1542                 }
1543         | expr '%' expr
1544                 {
1545                   if ($3 == 0.0) {
1546                     lex_error("modulus by zero");
1547                     YYABORT;
1548                   }
1549                   $$ = fmod($1, $3);
1550                 }
1551         | expr '^' expr
1552                 {
1553                   errno = 0;
1554                   $$ = pow($1, $3);
1555                   if (errno == EDOM) {
1556                     lex_error("arguments to `^' operator out of domain");
1557                     YYABORT;
1558                   }
1559                   if (errno == ERANGE) {
1560                     lex_error("result of `^' operator out of range");
1561                     YYABORT;
1562                   }
1563                 }
1564         | '-' expr                                              %prec '!'
1565                 { $$ = -$2; }
1566         | '(' any_expr ')'
1567                 { $$ = $2; }
1568         | SIN '(' any_expr ')'
1569                 {
1570                   errno = 0;
1571                   $$ = sin($3);
1572                   if (errno == ERANGE) {
1573                     lex_error("sin result out of range");
1574                     YYABORT;
1575                   }
1576                 }
1577         | COS '(' any_expr ')'
1578                 {
1579                   errno = 0;
1580                   $$ = cos($3);
1581                   if (errno == ERANGE) {
1582                     lex_error("cos result out of range");
1583                     YYABORT;
1584                   }
1585                 }
1586         | ATAN2 '(' any_expr ',' any_expr ')'
1587                 {
1588                   errno = 0;
1589                   $$ = atan2($3, $5);
1590                   if (errno == EDOM) {
1591                     lex_error("atan2 argument out of domain");
1592                     YYABORT;
1593                   }
1594                   if (errno == ERANGE) {
1595                     lex_error("atan2 result out of range");
1596                     YYABORT;
1597                   }
1598                 }
1599         | LOG '(' any_expr ')'
1600                 {
1601                   errno = 0;
1602                   $$ = log10($3);
1603                   if (errno == ERANGE) {
1604                     lex_error("log result out of range");
1605                     YYABORT;
1606                   }
1607                 }
1608         | EXP '(' any_expr ')'
1609                 {
1610                   errno = 0;
1611                   $$ = pow(10.0, $3);
1612                   if (errno == ERANGE) {
1613                     lex_error("exp result out of range");
1614                     YYABORT;
1615                   }
1616                 }
1617         | SQRT '(' any_expr ')'
1618                 {
1619                   errno = 0;
1620                   $$ = sqrt($3);
1621                   if (errno == EDOM) {
1622                     lex_error("sqrt argument out of domain");
1623                     YYABORT;
1624                   }
1625                 }
1626         | K_MAX '(' any_expr ',' any_expr ')'
1627                 { $$ = $3 > $5 ? $3 : $5; }
1628         | K_MIN '(' any_expr ',' any_expr ')'
1629                 { $$ = $3 < $5 ? $3 : $5; }
1630         | INT '(' any_expr ')'
1631                 { $$ = $3 < 0 ? -floor(-$3) : floor($3); }
1632         | RAND '(' any_expr ')'
1633                 { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
1634         | RAND '(' ')'
1635                 {
1636                   /* return a random number in the range [0,1) */
1637                   /* portable, but not very random */
1638                   $$ = (rand() & 0x7fff) / double(0x8000);
1639                 }
1640         | SRAND '(' any_expr ')'
1641                 {
1642                   $$ = 0;
1643                   srand((unsigned int)$3);
1644                 }
1645         | expr '<' expr
1646                 { $$ = ($1 < $3); }
1647         | expr LESSEQUAL expr
1648                 { $$ = ($1 <= $3); }
1649         | expr '>' expr
1650                 { $$ = ($1 > $3); }
1651         | expr GREATEREQUAL expr
1652                 { $$ = ($1 >= $3); }
1653         | expr EQUALEQUAL expr
1654                 { $$ = ($1 == $3); }
1655         | expr NOTEQUAL expr
1656                 { $$ = ($1 != $3); }
1657         | expr ANDAND expr
1658                 { $$ = ($1 != 0.0 && $3 != 0.0); }
1659         | expr OROR expr
1660                 { $$ = ($1 != 0.0 || $3 != 0.0); }
1661         | '!' expr
1662                 { $$ = ($2 == 0.0); }
1663
1664         ;
1665
1666 %%
1667
1668 /* bison defines const to be empty unless __STDC__ is defined, which it
1669 isn't under cfront */
1670
1671 #ifdef const
1672 #undef const
1673 #endif
1674
1675 static struct {
1676   const char *name;
1677   double val;
1678   int scaled;                // non-zero if val should be multiplied by scale
1679 } defaults_table[] = {
1680   { "arcrad", .25, 1 },
1681   { "arrowht", .1, 1 },
1682   { "arrowwid", .05, 1 },
1683   { "circlerad", .25, 1 },
1684   { "boxht", .5, 1 },
1685   { "boxwid", .75, 1 },
1686   { "boxrad", 0.0, 1 },
1687   { "dashwid", .05, 1 },
1688   { "ellipseht", .5, 1 },
1689   { "ellipsewid", .75, 1 },
1690   { "moveht", .5, 1 },
1691   { "movewid", .5, 1 },
1692   { "lineht", .5, 1 },
1693   { "linewid", .5, 1 },
1694   { "textht", 0.0, 1 },
1695   { "textwid", 0.0, 1 },
1696   { "scale", 1.0, 0 },
1697   { "linethick", -1.0, 0 },             // in points
1698   { "fillval", .5, 0 },
1699   { "arrowhead", 1.0, 0 },
1700   { "maxpswid", 8.5, 0 },
1701   { "maxpsht", 11.0, 0 },
1702 };
1703
1704 place *lookup_label(const char *label)
1705 {
1706   saved_state *state = current_saved_state;
1707   PTABLE(place) *tbl = current_table;
1708   for (;;) {
1709     place *pl = tbl->lookup(label);
1710     if (pl)
1711       return pl;
1712     if (!state)
1713       return 0;
1714     tbl = state->tbl;
1715     state = state->prev;
1716   }
1717 }
1718
1719 void define_label(const char *label, const place *pl)
1720 {
1721   place *p = new place[1];
1722   *p = *pl;
1723   current_table->define(label, p);
1724 }
1725
1726 int lookup_variable(const char *name, double *val)
1727 {
1728   place *pl = lookup_label(name);
1729   if (pl) {
1730     *val = pl->x;
1731     return 1;
1732   }
1733   return 0;
1734 }
1735
1736 void define_variable(const char *name, double val)
1737 {
1738   place *p = new place[1];
1739   p->obj = 0;
1740   p->x = val;
1741   p->y = 0.0;
1742   current_table->define(name, p);
1743   if (strcmp(name, "scale") == 0) {
1744     // When the scale changes, reset all scaled pre-defined variables to
1745     // their default values.
1746     for (unsigned int i = 0;
1747          i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 
1748       if (defaults_table[i].scaled)
1749         define_variable(defaults_table[i].name, val*defaults_table[i].val);
1750   }
1751 }
1752
1753 // called once only (not once per parse)
1754
1755 void parse_init()
1756 {
1757   current_direction = RIGHT_DIRECTION;
1758   current_position.x = 0.0;
1759   current_position.y = 0.0;
1760   // This resets everything to its default value.
1761   reset_all();
1762 }
1763
1764 void reset(const char *nm)
1765 {
1766   for (unsigned int i = 0;
1767        i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1768     if (strcmp(nm, defaults_table[i].name) == 0) {
1769       double val = defaults_table[i].val;
1770       if (defaults_table[i].scaled) {
1771         double scale;
1772         lookup_variable("scale", &scale);
1773         val *= scale;
1774       }
1775       define_variable(defaults_table[i].name, val);
1776       return;
1777     }
1778   lex_error("`%1' is not a predefined variable", nm);
1779 }
1780
1781 void reset_all()
1782 {
1783   // We only have to explicitly reset the pre-defined variables that
1784   // aren't scaled because `scale' is not scaled, and changing the
1785   // value of `scale' will reset all the pre-defined variables that
1786   // are scaled.
1787   for (unsigned int i = 0;
1788        i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
1789     if (!defaults_table[i].scaled)
1790       define_variable(defaults_table[i].name, defaults_table[i].val);
1791 }
1792
1793 // called after each parse
1794
1795 void parse_cleanup()
1796 {
1797   while (current_saved_state != 0) {
1798     delete current_table;
1799     current_table = current_saved_state->tbl;
1800     saved_state *tem = current_saved_state;
1801     current_saved_state = current_saved_state->prev;
1802     delete tem;
1803   }
1804   assert(current_table == &top_table);
1805   PTABLE_ITERATOR(place) iter(current_table);
1806   const char *key;
1807   place *pl;
1808   while (iter.next(&key, &pl))
1809     if (pl->obj != 0) {
1810       position pos = pl->obj->origin();
1811       pl->obj = 0;
1812       pl->x = pos.x;
1813       pl->y = pos.y;
1814     }
1815   while (olist.head != 0) {
1816     object *tem = olist.head;
1817     olist.head = olist.head->next;
1818     delete tem;
1819   }
1820   olist.tail = 0;
1821   current_direction = RIGHT_DIRECTION;
1822   current_position.x = 0.0;
1823   current_position.y = 0.0;
1824 }
1825
1826 const char *ordinal_postfix(int n)
1827 {
1828   if (n < 10 || n > 20)
1829     switch (n % 10) {
1830     case 1:
1831       return "st";
1832     case 2:
1833       return "nd";
1834     case 3:
1835       return "rd";
1836     }
1837   return "th";
1838 }
1839
1840 const char *object_type_name(object_type type)
1841 {
1842   switch (type) {
1843   case BOX_OBJECT:
1844     return "box";
1845   case CIRCLE_OBJECT:
1846     return "circle";
1847   case ELLIPSE_OBJECT:
1848     return "ellipse";
1849   case ARC_OBJECT:
1850     return "arc";
1851   case SPLINE_OBJECT:
1852     return "spline";
1853   case LINE_OBJECT:
1854     return "line";
1855   case ARROW_OBJECT:
1856     return "arrow";
1857   case MOVE_OBJECT:
1858     return "move";
1859   case TEXT_OBJECT:
1860     return "\"\"";
1861   case BLOCK_OBJECT:
1862     return "[]";
1863   case OTHER_OBJECT:
1864   case MARK_OBJECT:
1865   default:
1866     break;
1867   }
1868   return "object";
1869 }
1870
1871 static char sprintf_buf[1024];
1872
1873 char *format_number(const char *form, double n)
1874 {
1875   if (form == 0)
1876     form = "%g";
1877   return do_sprintf(form, &n, 1);
1878 }
1879
1880 char *do_sprintf(const char *form, const double *v, int nv)
1881 {
1882   string result;
1883   int i = 0;
1884   string one_format;
1885   while (*form) {
1886     if (*form == '%') {
1887       one_format += *form++;
1888       for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
1889         one_format += *form;
1890       if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
1891         lex_error("bad sprintf format");
1892         result += one_format;
1893         result += form;
1894         break;
1895       }
1896       if (*form == '%') {
1897         one_format += *form++;
1898         one_format += '\0';
1899         snprintf(sprintf_buf, sizeof(sprintf_buf),
1900                  "%s", one_format.contents());
1901       }
1902       else {
1903         if (i >= nv) {
1904           lex_error("too few arguments to snprintf");
1905           result += one_format;
1906           result += form;
1907           break;
1908         }
1909         one_format += *form++;
1910         one_format += '\0';
1911         snprintf(sprintf_buf, sizeof(sprintf_buf),
1912                  one_format.contents(), v[i++]);
1913       }
1914       one_format.clear();
1915       result += sprintf_buf;
1916     }
1917     else
1918       result += *form++;
1919   }
1920   result += '\0';
1921   return strsave(result.contents());
1922 }