LCOV - code coverage report
Current view: top level - preproc/pic - pic.ypp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 472 851 55.5 %
Date: 2026-01-16 17:51:41 Functions: 15 20 75.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14