bug-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

problem with groff's pic.y grammar


From: Werner LEMBERG
Subject: problem with groff's pic.y grammar
Date: Thu, 17 Apr 2003 08:27:59 +0200 (CEST)

Please check the attached pic.y grammar.  With byacc, I get a clean
run.  With bison 1.875, I get 8 shift/reduce conflicts, and the
resulting output doesn't work at all.  How is this possible?


    Werner
/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
   Free Software Foundation, Inc.
     Written by James Clark (address@hidden)

This file is part of groff.

groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with groff; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
%{
#include "pic.h"
#include "ptable.h"
#include "object.h"

extern int delim_flag;
extern void do_copy(const char *);
extern void copy_rest_thru(const char *, const char *);
extern void copy_file_thru(const char *, const char *, const char *);
extern void push_body(const char *);
extern void do_for(char *var, double from, double to,
                   int by_is_multiplicative, double by, char *body);
extern void do_lookahead();

/* Maximum number of characters produced by printf("%g") */
#define GDIGITS 14

int yylex();
void yyerror(const char *);

void reset(const char *nm);
void reset_all();

place *lookup_label(const char *);
void define_label(const char *label, const place *pl);

direction current_direction;
position current_position;

implement_ptable(place)

PTABLE(place) top_table;

PTABLE(place) *current_table = &top_table;
saved_state *current_saved_state = 0;

object_list olist;

const char *ordinal_postfix(int n);
const char *object_type_name(object_type type);
char *format_number(const char *form, double n);
char *do_sprintf(const char *form, const double *v, int nv);

%}


%union {
        char *str;
        int n;
        double x;
        struct { double x, y; } pair;
        struct { double x; char *body; } if_data;
        struct { char *str; const char *filename; int lineno; } lstr;
        struct { double *v; int nv; int maxv; } dv;
        struct { double val; int is_multiplicative; } by;
        place pl;
        object *obj;
        corner crn;
        path *pth;
        object_spec *spec;
        saved_state *pstate;
        graphics_state state;
        object_type obtype;
}

%token <str> LABEL
%token <str> VARIABLE
%token <x> NUMBER
%token <lstr> TEXT
%token <lstr> COMMAND_LINE
%token <str> DELIMITED
%token <n> ORDINAL
%token TH
%token LEFT_ARROW_HEAD
%token RIGHT_ARROW_HEAD
%token DOUBLE_ARROW_HEAD
%token LAST
%token UP
%token DOWN
%token LEFT
%token RIGHT
%token BOX
%token CIRCLE
%token ELLIPSE
%token ARC
%token LINE
%token ARROW
%token MOVE
%token SPLINE
%token HEIGHT
%token RADIUS
%token WIDTH
%token DIAMETER
%token UP
%token DOWN
%token RIGHT
%token LEFT
%token FROM
%token TO
%token AT
%token WITH
%token BY
%token THEN
%token SOLID
%token DOTTED
%token DASHED
%token CHOP
%token SAME
%token INVISIBLE
%token LJUST
%token RJUST
%token ABOVE
%token BELOW
%token OF
%token THE
%token WAY
%token BETWEEN
%token AND
%token HERE
%token DOT_N
%token DOT_E    
%token DOT_W
%token DOT_S
%token DOT_NE
%token DOT_SE
%token DOT_NW
%token DOT_SW
%token DOT_C
%token DOT_START
%token DOT_END
%token DOT_X
%token DOT_Y
%token DOT_HT
%token DOT_WID
%token DOT_RAD
%token SIN
%token COS
%token ATAN2
%token LOG
%token EXP
%token SQRT
%token K_MAX
%token K_MIN
%token INT
%token RAND
%token SRAND
%token COPY
%token THRU
%token TOP
%token BOTTOM
%token UPPER
%token LOWER
%token SH
%token PRINT
%token CW
%token CCW
%token FOR
%token DO
%token IF
%token ELSE
%token ANDAND
%token OROR
%token NOTEQUAL
%token EQUALEQUAL
%token LESSEQUAL
%token GREATEREQUAL
%token LEFT_CORNER
%token RIGHT_CORNER
%token NORTH
%token SOUTH
%token EAST
%token WEST
%token CENTER
%token END
%token START
%token RESET
%token UNTIL
%token PLOT
%token THICKNESS
%token FILL
%token COLORED
%token OUTLINED
%token SHADED
%token ALIGNED
%token SPRINTF
%token COMMAND

%token DEFINE
%token UNDEF

%left '.'

/* this ensures that plot 17 "%g" parses as (plot 17 "%g") */
%left PLOT
%left TEXT SPRINTF

/* give text adjustments higher precedence than TEXT, so that
box "foo" above ljust == box ("foo" above ljust)
*/

%left LJUST RJUST ABOVE BELOW

%left LEFT RIGHT
/* Give attributes that take an optional expression a higher
precedence than left and right, so that eg `line chop left'
parses properly. */
%left CHOP SOLID DASHED DOTTED UP DOWN FILL COLORED OUTLINED
%left LABEL

%left VARIABLE NUMBER '(' SIN COS ATAN2 LOG EXP SQRT K_MAX K_MIN INT RAND SRAND 
LAST 
%left ORDINAL HERE '`'

%left BOX CIRCLE ELLIPSE ARC LINE ARROW SPLINE '['

/* these need to be lower than '-' */
%left HEIGHT RADIUS WIDTH DIAMETER FROM TO AT THICKNESS

/* these must have higher precedence than CHOP so that `label %prec CHOP'
works */
%left DOT_N DOT_E DOT_W DOT_S DOT_NE DOT_SE DOT_NW DOT_SW DOT_C
%left DOT_START DOT_END TOP BOTTOM LEFT_CORNER RIGHT_CORNER
%left UPPER LOWER NORTH SOUTH EAST WEST CENTER START END

%left ','
%left OROR
%left ANDAND
%left EQUALEQUAL NOTEQUAL
%left '<' '>' LESSEQUAL GREATEREQUAL

%left BETWEEN OF
%left AND

%left '+' '-'
%left '*' '/' '%'
%right '!'
%right '^'

%type <x> expr any_expr text_expr
%type <by> optional_by
%type <pair> expr_pair position_not_place
%type <if_data> simple_if
%type <obj> nth_primitive
%type <crn> corner
%type <pth> path label_path relative_path
%type <pl> place label element element_list middle_element_list
%type <spec> object_spec
%type <pair> position
%type <obtype> object_type
%type <n> optional_ordinal_last ordinal
%type <str> until
%type <dv> sprintf_args
%type <lstr> text print_args print_arg

%%

top:
        optional_separator
        | element_list
                {
                  if (olist.head)
                    print_picture(olist.head);
                }
        ;


element_list:
        optional_separator middle_element_list optional_separator
                { $$ = $2; }
        ;

middle_element_list:
        element
                { $$ = $1; }
        | middle_element_list separator element
                { $$ = $1; }
        ;

optional_separator:
        /* empty */
        | separator
        ;

separator:
        ';'
        | separator ';'
        ;

placeless_element:
        VARIABLE '=' any_expr
                {
                  define_variable($1, $3);
                  a_delete $1;
                }
        | VARIABLE ':' '=' any_expr
                {
                  place *p = lookup_label($1);
                  if (!p) {
                    lex_error("variable `%1' not defined", $1);
                    YYABORT;
                  }
                  p->obj = 0;
                  p->x = $4;
                  p->y = 0.0;
                  a_delete $1;
                }
        | UP
                { current_direction = UP_DIRECTION; }
        | DOWN
                { current_direction = DOWN_DIRECTION; }
        | LEFT
                { current_direction = LEFT_DIRECTION; }
        | RIGHT
                { current_direction = RIGHT_DIRECTION; }
        | COMMAND_LINE
                {
                  olist.append(make_command_object($1.str, $1.filename,
                                                   $1.lineno));
                }
        | COMMAND print_args
                {
                  olist.append(make_command_object($2.str, $2.filename,
                                                   $2.lineno));
                }
        | PRINT print_args
                {
                  fprintf(stderr, "%s\n", $2.str);
                  a_delete $2.str;
                  fflush(stderr);
                }
        | SH
                { delim_flag = 1; }
          DELIMITED
                {
                  delim_flag = 0;
                  if (safer_flag)
                    lex_error("unsafe to run command `%1'", $3);
                  else
                    system($3);
                  a_delete $3;
                }
        | COPY TEXT
                {
                  if (yychar < 0)
                    do_lookahead();
                  do_copy($2.str);
                  // do not delete the filename
                }
        | COPY TEXT THRU
                { delim_flag = 2; }
          DELIMITED 
                { delim_flag = 0; }
          until
                {
                  if (yychar < 0)
                    do_lookahead();
                  copy_file_thru($2.str, $5, $7);
                  // do not delete the filename
                  a_delete $5;
                  a_delete $7;
                }
        | COPY THRU
                { delim_flag = 2; }
          DELIMITED
                { delim_flag = 0; }
          until
                {
                  if (yychar < 0)
                    do_lookahead();
                  copy_rest_thru($4, $6);
                  a_delete $4;
                  a_delete $6;
                }
        | FOR VARIABLE '=' expr TO expr optional_by DO
                { delim_flag = 1; }
          DELIMITED
                {
                  delim_flag = 0;
                  if (yychar < 0)
                    do_lookahead();
                  do_for($2, $4, $6, $7.is_multiplicative, $7.val, $10); 
                }
        | simple_if
                {
                  if (yychar < 0)
                    do_lookahead();
                  if ($1.x != 0.0)
                    push_body($1.body);
                  a_delete $1.body;
                }
        | simple_if ELSE
                { delim_flag = 1; }
          DELIMITED
                {
                  delim_flag = 0;
                  if (yychar < 0)
                    do_lookahead();
                  if ($1.x != 0.0)
                    push_body($1.body);
                  else
                    push_body($4);
                  a_delete $1.body;
                  a_delete $4;
                }
        | reset_variables
        | RESET
                { define_variable("scale", 1.0); }
        ;

reset_variables:
        RESET VARIABLE
                {
                  reset($2);
                  a_delete $2;
                }
        | reset_variables VARIABLE
                {
                  reset($2);
                  a_delete $2;
                }
        | reset_variables ',' VARIABLE
                {
                  reset($3);
                  a_delete $3;
                }
        ;

print_args:
        print_arg
                { $$ = $1; }
        | print_args print_arg
                {
                  $$.str = new char[strlen($1.str) + strlen($2.str) + 1];
                  strcpy($$.str, $1.str);
                  strcat($$.str, $2.str);
                  a_delete $1.str;
                  a_delete $2.str;
                  if ($1.filename) {
                    $$.filename = $1.filename;
                    $$.lineno = $1.lineno;
                  }
                  else if ($2.filename) {
                    $$.filename = $2.filename;
                    $$.lineno = $2.lineno;
                  }
                }
        ;

print_arg:
        expr                                                    %prec ','
                {
                  $$.str = new char[GDIGITS + 1];
                  sprintf($$.str, "%g", $1);
                  $$.filename = 0;
                  $$.lineno = 0;
                }
        | text
                { $$ = $1; }
        | position                                              %prec ','
                {
                  $$.str = new char[GDIGITS + 2 + GDIGITS + 1];
                  sprintf($$.str, "%g, %g", $1.x, $1.y);
                  $$.filename = 0;
                  $$.lineno = 0;
                }
        ;

simple_if:
        IF any_expr THEN
                { delim_flag = 1; }
        DELIMITED
                {
                  delim_flag = 0;
                  $$.x = $2;
                  $$.body = $5;
                }
        ;

until:
        /* empty */
                { $$ = 0; }
        | UNTIL TEXT
                { $$ = $2.str; }
        ;
        
any_expr:
        expr
                { $$ = $1; }
        | text_expr
                { $$ = $1; }
        ;
        
text_expr:
        text EQUALEQUAL text
                {
                  $$ = strcmp($1.str, $3.str) == 0;
                  a_delete $1.str;
                  a_delete $3.str;
                }
        | text NOTEQUAL text
                {
                  $$ = strcmp($1.str, $3.str) != 0;
                  a_delete $1.str;
                  a_delete $3.str;
                }
        | text_expr ANDAND text_expr
                { $$ = ($1 != 0.0 && $3 != 0.0); }
        | text_expr ANDAND expr
                { $$ = ($1 != 0.0 && $3 != 0.0); }
        | expr ANDAND text_expr
                { $$ = ($1 != 0.0 && $3 != 0.0); }
        | text_expr OROR text_expr
                { $$ = ($1 != 0.0 || $3 != 0.0); }
        | text_expr OROR expr
                { $$ = ($1 != 0.0 || $3 != 0.0); }
        | expr OROR text_expr
                { $$ = ($1 != 0.0 || $3 != 0.0); }
        | '!' text_expr
                { $$ = ($2 == 0.0); }
        ;


optional_by:
        /* empty */
                {
                  $$.val = 1.0;
                  $$.is_multiplicative = 0;
                }
        | BY expr
                {
                  $$.val = $2;
                  $$.is_multiplicative = 0;
                }
        | BY '*' expr
                {
                  $$.val = $3;
                  $$.is_multiplicative = 1;
                }
        ;

element:
        object_spec
                {
                  $$.obj = $1->make_object(&current_position,
                                           &current_direction);
                  if ($$.obj == 0)
                    YYABORT;
                  delete $1;
                  if ($$.obj)
                    olist.append($$.obj);
                  else {
                    $$.x = current_position.x;
                    $$.y = current_position.y;
                  }
                }
        | LABEL ':' optional_separator element
                {
                  $$ = $4;
                  define_label($1, & $$);
                  a_delete $1;
                }
        | LABEL ':' optional_separator position_not_place
                {
                  $$.obj = 0;
                  $$.x = $4.x;
                  $$.y = $4.y;
                  define_label($1, & $$);
                  a_delete $1;
                }
        | LABEL ':' optional_separator place
                {
                  $$ = $4;
                  define_label($1, & $$);
                  a_delete $1;
                }
        | '{'
                {
                  $<state>$.x = current_position.x;
                  $<state>$.y = current_position.y;
                  $<state>$.dir = current_direction;
                }
          element_list '}'
                {
                  current_position.x = $<state>2.x;
                  current_position.y = $<state>2.y;
                  current_direction = $<state>2.dir;
                }
          optional_element
                {
                  $$ = $3;
                }
        | placeless_element
                {
                  $$.obj = 0;
                  $$.x = current_position.x;
                  $$.y = current_position.y;
                }
        ;

optional_element:
        /* empty */
                {}
        | element
                {}
        ;

object_spec:
        BOX
                { $$ = new object_spec(BOX_OBJECT); }
        | CIRCLE
                { $$ = new object_spec(CIRCLE_OBJECT); }
        | ELLIPSE
                { $$ = new object_spec(ELLIPSE_OBJECT); }
        | ARC
                {
                  $$ = new object_spec(ARC_OBJECT);
                  $$->dir = current_direction;
                }
        | LINE
                {
                  $$ = new object_spec(LINE_OBJECT);
                  lookup_variable("lineht", & $$->segment_height);
                  lookup_variable("linewid", & $$->segment_width);
                  $$->dir = current_direction;
                }
        | ARROW
                {
                  $$ = new object_spec(ARROW_OBJECT);
                  lookup_variable("lineht", & $$->segment_height);
                  lookup_variable("linewid", & $$->segment_width);
                  $$->dir = current_direction;
                }
        | MOVE
                {
                  $$ = new object_spec(MOVE_OBJECT);
                  lookup_variable("moveht", & $$->segment_height);
                  lookup_variable("movewid", & $$->segment_width);
                  $$->dir = current_direction;
                }
        | SPLINE
                {
                  $$ = new object_spec(SPLINE_OBJECT);
                  lookup_variable("lineht", & $$->segment_height);
                  lookup_variable("linewid", & $$->segment_width);
                  $$->dir = current_direction;
                }
        | text                                                  %prec TEXT
                {
                  $$ = new object_spec(TEXT_OBJECT);
                  $$->text = new text_item($1.str, $1.filename, $1.lineno);
                }
        | PLOT expr
                {
                  $$ = new object_spec(TEXT_OBJECT);
                  $$->text = new text_item(format_number(0, $2), 0, -1);
                }
        | PLOT expr text
                {
                  $$ = new object_spec(TEXT_OBJECT);
                  $$->text = new text_item(format_number($3.str, $2),
                                           $3.filename, $3.lineno);
                  a_delete $3.str;
                }
        | '[' 
                {
                  saved_state *p = new saved_state;
                  $<pstate>$ = p;
                  p->x = current_position.x;
                  p->y = current_position.y;
                  p->dir = current_direction;
                  p->tbl = current_table;
                  p->prev = current_saved_state;
                  current_position.x = 0.0;
                  current_position.y = 0.0;
                  current_table = new PTABLE(place);
                  current_saved_state = p;
                  olist.append(make_mark_object());
                }
          element_list ']'
                {
                  current_position.x = $<pstate>2->x;
                  current_position.y = $<pstate>2->y;
                  current_direction = $<pstate>2->dir;
                  $$ = new object_spec(BLOCK_OBJECT);
                  olist.wrap_up_block(& $$->oblist);
                  $$->tbl = current_table;
                  current_table = $<pstate>2->tbl;
                  current_saved_state = $<pstate>2->prev;
                  delete $<pstate>2;
                }
        | object_spec HEIGHT expr
                {
                  $$ = $1;
                  $$->height = $3;
                  $$->flags |= HAS_HEIGHT;
                }
        | object_spec RADIUS expr
                {
                  $$ = $1;
                  $$->radius = $3;
                  $$->flags |= HAS_RADIUS;
                }
        | object_spec WIDTH expr
                {
                  $$ = $1;
                  $$->width = $3;
                  $$->flags |= HAS_WIDTH;
                }
        | object_spec DIAMETER expr
                {
                  $$ = $1;
                  $$->radius = $3/2.0;
                  $$->flags |= HAS_RADIUS;
                }
        | object_spec expr                                      %prec HEIGHT
                {
                  $$ = $1;
                  $$->flags |= HAS_SEGMENT;
                  switch ($$->dir) {
                  case UP_DIRECTION:
                    $$->segment_pos.y += $2;
                    break;
                  case DOWN_DIRECTION:
                    $$->segment_pos.y -= $2;
                    break;
                  case RIGHT_DIRECTION:
                    $$->segment_pos.x += $2;
                    break;
                  case LEFT_DIRECTION:
                    $$->segment_pos.x -= $2;
                    break;
                  }
                }
        | object_spec UP
                {
                  $$ = $1;
                  $$->dir = UP_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.y += $$->segment_height;
                }
        | object_spec UP expr
                {
                  $$ = $1;
                  $$->dir = UP_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.y += $3;
                }
        | object_spec DOWN
                {
                  $$ = $1;
                  $$->dir = DOWN_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.y -= $$->segment_height;
                }
        | object_spec DOWN expr
                {
                  $$ = $1;
                  $$->dir = DOWN_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.y -= $3;
                }
        | object_spec RIGHT
                {
                  $$ = $1;
                  $$->dir = RIGHT_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x += $$->segment_width;
                }
        | object_spec RIGHT expr
                {
                  $$ = $1;
                  $$->dir = RIGHT_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x += $3;
                }
        | object_spec LEFT
                {
                  $$ = $1;
                  $$->dir = LEFT_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x -= $$->segment_width;
                }
        | object_spec LEFT expr
                {
                  $$ = $1;
                  $$->dir = LEFT_DIRECTION;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x -= $3;
                }
        | object_spec FROM position
                {
                  $$ = $1;
                  $$->flags |= HAS_FROM;
                  $$->from.x = $3.x;
                  $$->from.y = $3.y;
                }
        | object_spec TO position
                {
                  $$ = $1;
                  if ($$->flags & HAS_SEGMENT)
                    $$->segment_list = new segment($$->segment_pos,
                                                   $$->segment_is_absolute,
                                                   $$->segment_list);
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x = $3.x;
                  $$->segment_pos.y = $3.y;
                  $$->segment_is_absolute = 1;
                  $$->flags |= HAS_TO;
                  $$->to.x = $3.x;
                  $$->to.y = $3.y;
                }
        | object_spec AT position
                {
                  $$ = $1;
                  $$->flags |= HAS_AT;
                  $$->at.x = $3.x;
                  $$->at.y = $3.y;
                  if ($$->type != ARC_OBJECT) {
                    $$->flags |= HAS_FROM;
                    $$->from.x = $3.x;
                    $$->from.y = $3.y;
                  }
                }
        | object_spec WITH path
                {
                  $$ = $1;
                  $$->flags |= HAS_WITH;
                  $$->with = $3;
                }
        | object_spec WITH position                             %prec ','
                {
                  $$ = $1;
                  $$->flags |= HAS_WITH;
                  position pos;
                  pos.x = $3.x;
                  pos.y = $3.y;
                  $$->with = new path(pos);
                }
        | object_spec BY expr_pair
                {
                  $$ = $1;
                  $$->flags |= HAS_SEGMENT;
                  $$->segment_pos.x += $3.x;
                  $$->segment_pos.y += $3.y;
                }
        | object_spec THEN
                {
                  $$ = $1;
                  if ($$->flags & HAS_SEGMENT) {
                    $$->segment_list = new segment($$->segment_pos,
                                                   $$->segment_is_absolute,
                                                   $$->segment_list);
                    $$->flags &= ~HAS_SEGMENT;
                    $$->segment_pos.x = $$->segment_pos.y = 0.0;
                    $$->segment_is_absolute = 0;
                  }
                }
        | object_spec SOLID
                {
                  $$ = $1;      // nothing
                }
        | object_spec DOTTED
                {
                  $$ = $1;
                  $$->flags |= IS_DOTTED;
                  lookup_variable("dashwid", & $$->dash_width);
                }
        | object_spec DOTTED expr
                {
                  $$ = $1;
                  $$->flags |= IS_DOTTED;
                  $$->dash_width = $3;
                }
        | object_spec DASHED
                {
                  $$ = $1;
                  $$->flags |= IS_DASHED;
                  lookup_variable("dashwid", & $$->dash_width);
                }
        | object_spec DASHED expr
                {
                  $$ = $1;
                  $$->flags |= IS_DASHED;
                  $$->dash_width = $3;
                }
        | object_spec FILL
                {
                  $$ = $1;
                  $$->flags |= IS_DEFAULT_FILLED;
                }
        | object_spec FILL expr
                {
                  $$ = $1;
                  $$->flags |= IS_FILLED;
                  $$->fill = $3;
                }
        | object_spec SHADED text
                {
                  $$ = $1;
                  $$->flags |= (IS_SHADED | IS_FILLED);
                  $$->shaded = new char[strlen($3.str)+1];
                  strcpy($$->shaded, $3.str);
                }
        | object_spec COLORED text
                {
                  $$ = $1;
                  $$->flags |= (IS_SHADED | IS_OUTLINED | IS_FILLED);
                  $$->shaded = new char[strlen($3.str)+1];
                  strcpy($$->shaded, $3.str);
                  $$->outlined = new char[strlen($3.str)+1];
                  strcpy($$->outlined, $3.str);
                }
        | object_spec OUTLINED text
                {
                  $$ = $1;
                  $$->flags |= IS_OUTLINED;
                  $$->outlined = new char[strlen($3.str)+1];
                  strcpy($$->outlined, $3.str);
                }
        | object_spec CHOP
                {
                  $$ = $1;
                  // line chop chop means line chop 0 chop 0
                  if ($$->flags & IS_DEFAULT_CHOPPED) {
                    $$->flags |= IS_CHOPPED;
                    $$->flags &= ~IS_DEFAULT_CHOPPED;
                    $$->start_chop = $$->end_chop = 0.0;
                  }
                  else if ($$->flags & IS_CHOPPED) {
                    $$->end_chop = 0.0;
                  }
                  else {
                    $$->flags |= IS_DEFAULT_CHOPPED;
                  }
                }
        | object_spec CHOP expr
                {
                  $$ = $1;
                  if ($$->flags & IS_DEFAULT_CHOPPED) {
                    $$->flags |= IS_CHOPPED;
                    $$->flags &= ~IS_DEFAULT_CHOPPED;
                    $$->start_chop = 0.0;
                    $$->end_chop = $3;
                  }
                  else if ($$->flags & IS_CHOPPED) {
                    $$->end_chop = $3;
                  }
                  else {
                    $$->start_chop = $$->end_chop = $3;
                    $$->flags |= IS_CHOPPED;
                  }
                }
        | object_spec SAME
                {
                  $$ = $1;
                  $$->flags |= IS_SAME;
                }
        | object_spec INVISIBLE
                {
                  $$ = $1;
                  $$->flags |= IS_INVISIBLE;
                }
        | object_spec LEFT_ARROW_HEAD
                {
                  $$ = $1;
                  $$->flags |= HAS_LEFT_ARROW_HEAD;
                }
        | object_spec RIGHT_ARROW_HEAD
                {
                  $$ = $1;
                  $$->flags |= HAS_RIGHT_ARROW_HEAD;
                }
        | object_spec DOUBLE_ARROW_HEAD
                {
                  $$ = $1;
                  $$->flags |= (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD);
                }
        | object_spec CW
                {
                  $$ = $1;
                  $$->flags |= IS_CLOCKWISE;
                }
        | object_spec CCW
                {
                  $$ = $1;
                  $$->flags &= ~IS_CLOCKWISE;
                }
        | object_spec text                                      %prec TEXT
                {
                  $$ = $1;
                  text_item **p;
                  for (p = & $$->text; *p; p = &(*p)->next)
                    ;
                  *p = new text_item($2.str, $2.filename, $2.lineno);
                }
        | object_spec LJUST
                {
                  $$ = $1;
                  if ($$->text) {
                    text_item *p;
                    for (p = $$->text; p->next; p = p->next)
                      ;
                    p->adj.h = LEFT_ADJUST;
                  }
                }
        | object_spec RJUST
                {
                  $$ = $1;
                  if ($$->text) {
                    text_item *p;
                    for (p = $$->text; p->next; p = p->next)
                      ;
                    p->adj.h = RIGHT_ADJUST;
                  }
                }
        | object_spec ABOVE
                {
                  $$ = $1;
                  if ($$->text) {
                    text_item *p;
                    for (p = $$->text; p->next; p = p->next)
                      ;
                    p->adj.v = ABOVE_ADJUST;
                  }
                }
        | object_spec BELOW
                {
                  $$ = $1;
                  if ($$->text) {
                    text_item *p;
                    for (p = $$->text; p->next; p = p->next)
                      ;
                    p->adj.v = BELOW_ADJUST;
                  }
                }
        | object_spec THICKNESS expr
                {
                  $$ = $1;
                  $$->flags |= HAS_THICKNESS;
                  $$->thickness = $3;
                }
        | object_spec ALIGNED
                {
                  $$ = $1;
                  $$->flags |= IS_ALIGNED;
                }
        ;

text:
        TEXT
                { $$ = $1; }
        | SPRINTF '(' TEXT sprintf_args ')'
                {
                  $$.filename = $3.filename;
                  $$.lineno = $3.lineno;
                  $$.str = do_sprintf($3.str, $4.v, $4.nv);
                  a_delete $4.v;
                  a_delete $3.str;
                }
        ;

sprintf_args:
        /* empty */
                {
                  $$.v = 0;
                  $$.nv = 0;
                  $$.maxv = 0;
                }
        | sprintf_args ',' expr
                {
                  $$ = $1;
                  if ($$.nv >= $$.maxv) {
                    if ($$.nv == 0) {
                      $$.v = new double[4];
                      $$.maxv = 4;
                    }
                    else {
                      double *oldv = $$.v;
                      $$.maxv *= 2;
                      $$.v = new double[$$.maxv];
                      memcpy($$.v, oldv, $$.nv*sizeof(double));
                      a_delete oldv;
                    }
                  }
                  $$.v[$$.nv] = $3;
                  $$.nv += 1;
                }
        ;

position:
        position_not_place
                { $$ = $1; }
        | place
                {
                  position pos = $1;
                  $$.x = pos.x;
                  $$.y = pos.y;
                }
        ;

position_not_place:
        expr_pair
                { $$ = $1; }
        | position '+' expr_pair
                {
                  $$.x = $1.x + $3.x;
                  $$.y = $1.y + $3.y;
                }
        | position '-' expr_pair
                {
                  $$.x = $1.x - $3.x;
                  $$.y = $1.y - $3.y;
                }
        | '(' position ',' position ')'
                {
                  $$.x = $2.x;
                  $$.y = $4.y;
                }
        | expr between position AND position
                {
                  $$.x = (1.0 - $1)*$3.x + $1*$5.x;
                  $$.y = (1.0 - $1)*$3.y + $1*$5.y;
                }
        | expr '<' position ',' position '>'
                {
                  $$.x = (1.0 - $1)*$3.x + $1*$5.x;
                  $$.y = (1.0 - $1)*$3.y + $1*$5.y;
                }
        ;

between:
        BETWEEN
        | OF THE WAY BETWEEN
        ;

expr_pair:
        expr ',' expr
                {
                  $$.x = $1;
                  $$.y = $3;
                }
        | '(' expr_pair ')'
                { $$ = $2; }
        ;

place:
        /* line at A left == line (at A) left */
        label                                                   %prec CHOP
                { $$ = $1; }
        | label corner
                {
                  path pth($2);
                  if (!pth.follow($1, & $$))
                    YYABORT;
                }
        | corner label
                {
                  path pth($1);
                  if (!pth.follow($2, & $$))
                    YYABORT;
                }
        | corner OF label
                {
                  path pth($1);
                  if (!pth.follow($3, & $$))
                    YYABORT;
                }
        | HERE
                {
                  $$.x = current_position.x;
                  $$.y = current_position.y;
                  $$.obj = 0;
                }
        ;

label:
        LABEL
                {
                  place *p = lookup_label($1);
                  if (!p) {
                    lex_error("there is no place `%1'", $1);
                    YYABORT;
                  }
                  $$ = *p;
                  a_delete $1;
                }
        | nth_primitive
                { $$.obj = $1; }
        | label '.' LABEL
                {
                  path pth($3);
                  if (!pth.follow($1, & $$))
                    YYABORT;
                }
        ;

ordinal:
        ORDINAL
                { $$ = $1; }
        | '`' any_expr TH
                {
                  // XXX Check for overflow (and non-integers?).
                  $$ = (int)$2;
                }
        ;

optional_ordinal_last:
        LAST
                { $$ = 1; }
        | ordinal LAST
                { $$ = $1; }
        ;

nth_primitive:
        ordinal object_type
                {
                  int count = 0;
                  object *p;
                  for (p = olist.head; p != 0; p = p->next)
                    if (p->type() == $2 && ++count == $1) {
                      $$ = p;
                      break;
                    }
                  if (p == 0) {
                    lex_error("there is no %1%2 %3", $1, ordinal_postfix($1),
                              object_type_name($2));
                    YYABORT;
                  }
                }
        | optional_ordinal_last object_type
                {
                  int count = 0;
                  object *p;
                  for (p = olist.tail; p != 0; p = p->prev)
                    if (p->type() == $2 && ++count == $1) {
                      $$ = p;
                      break;
                    }
                  if (p == 0) {
                    lex_error("there is no %1%2 last %3", $1,
                              ordinal_postfix($1), object_type_name($2));
                    YYABORT;
                  }
                }
        ;

object_type:
        BOX
                { $$ = BOX_OBJECT; }
        | CIRCLE
                { $$ = CIRCLE_OBJECT; }
        | ELLIPSE
                { $$ = ELLIPSE_OBJECT; }
        | ARC
                { $$ = ARC_OBJECT; }
        | LINE
                { $$ = LINE_OBJECT; }
        | ARROW
                { $$ = ARROW_OBJECT; }
        | SPLINE
                { $$ = SPLINE_OBJECT; }
        | '[' ']'
                { $$ = BLOCK_OBJECT; }
        | TEXT
                { $$ = TEXT_OBJECT; }
        ;

label_path:
        '.' LABEL
                { $$ = new path($2); }
        | label_path '.' LABEL
                {
                  $$ = $1;
                  $$->append($3);
                }
        ;

relative_path:
        corner                                                  %prec CHOP
                { $$ = new path($1); }
        /* give this a lower precedence than LEFT and RIGHT so that
           [A: box] with .A left == [A: box] with (.A left) */
        | label_path                                            %prec TEXT
                { $$ = $1; }
        | label_path corner
                {
                  $$ = $1;
                  $$->append($2);
                }
        ;

path:
        relative_path
                { $$ = $1; }
        | '(' relative_path ',' relative_path ')'
                {
                  $$ = $2;
                  $$->set_ypath($4);
                }
        /* The rest of these rules are a compatibility sop. */
        | ORDINAL LAST object_type relative_path
                {
                  lex_warning("`%1%2 last %3' in `with' argument ignored",
                              $1, ordinal_postfix($1), object_type_name($3));
                  $$ = $4;
                }
        | LAST object_type relative_path
                {
                  lex_warning("`last %1' in `with' argument ignored",
                              object_type_name($2));
                  $$ = $3;
                }
        | ORDINAL object_type relative_path
                {
                  lex_warning("`%1%2 %3' in `with' argument ignored",
                              $1, ordinal_postfix($1), object_type_name($2));
                  $$ = $3;
                }
        | LABEL relative_path
                {
                  lex_warning("initial `%1' in `with' argument ignored", $1);
                  a_delete $1;
                  $$ = $2;
                }
        ;

corner:
        DOT_N
                { $$ = &object::north; }
        | DOT_E 
                { $$ = &object::east; }
        | DOT_W
                { $$ = &object::west; }
        | DOT_S
                { $$ = &object::south; }
        | DOT_NE
                { $$ = &object::north_east; }
        | DOT_SE
                { $$ = &object:: south_east; }
        | DOT_NW
                { $$ = &object::north_west; }
        | DOT_SW
                { $$ = &object::south_west; }
        | DOT_C
                { $$ = &object::center; }
        | DOT_START
                { $$ = &object::start; }
        | DOT_END
                { $$ = &object::end; }
        | TOP
                { $$ = &object::north; }
        | BOTTOM
                { $$ = &object::south; }
        | LEFT
                { $$ = &object::west; }
        | RIGHT
                { $$ = &object::east; }
        | UPPER LEFT
                { $$ = &object::north_west; }
        | LOWER LEFT
                { $$ = &object::south_west; }
        | UPPER RIGHT
                { $$ = &object::north_east; }
        | LOWER RIGHT
                { $$ = &object::south_east; }
        | LEFT_CORNER
                { $$ = &object::west; }
        | RIGHT_CORNER
                { $$ = &object::east; }
        | UPPER LEFT_CORNER
                { $$ = &object::north_west; }
        | LOWER LEFT_CORNER
                { $$ = &object::south_west; }
        | UPPER RIGHT_CORNER
                { $$ = &object::north_east; }
        | LOWER RIGHT_CORNER
                { $$ = &object::south_east; }
        | NORTH
                { $$ = &object::north; }
        | SOUTH
                { $$ = &object::south; }
        | EAST
                { $$ = &object::east; }
        | WEST
                { $$ = &object::west; }
        | CENTER
                { $$ = &object::center; }
        | START
                { $$ = &object::start; }
        | END
                { $$ = &object::end; }
        ;

expr:
        VARIABLE
                {
                  if (!lookup_variable($1, & $$)) {
                    lex_error("there is no variable `%1'", $1);
                    YYABORT;
                  }
                  a_delete $1;
                }
        | NUMBER
                { $$ = $1; }
        | place DOT_X
                {
                  if ($1.obj != 0)
                    $$ = $1.obj->origin().x;
                  else
                    $$ = $1.x;
                }                       
        | place DOT_Y
                {
                  if ($1.obj != 0)
                    $$ = $1.obj->origin().y;
                  else
                    $$ = $1.y;
                }
        | place DOT_HT
                {
                  if ($1.obj != 0)
                    $$ = $1.obj->height();
                  else
                    $$ = 0.0;
                }
        | place DOT_WID
                {
                  if ($1.obj != 0)
                    $$ = $1.obj->width();
                  else
                    $$ = 0.0;
                }
        | place DOT_RAD
                {
                  if ($1.obj != 0)
                    $$ = $1.obj->radius();
                  else
                    $$ = 0.0;
                }
        | expr '+' expr
                { $$ = $1 + $3; }
        | expr '-' expr
                { $$ = $1 - $3; }
        | expr '*' expr
                { $$ = $1 * $3; }
        | expr '/' expr
                {
                  if ($3 == 0.0) {
                    lex_error("division by zero");
                    YYABORT;
                  }
                  $$ = $1/$3;
                }
        | expr '%' expr
                {
                  if ($3 == 0.0) {
                    lex_error("modulus by zero");
                    YYABORT;
                  }
                  $$ = fmod($1, $3);
                }
        | expr '^' expr
                {
                  errno = 0;
                  $$ = pow($1, $3);
                  if (errno == EDOM) {
                    lex_error("arguments to `^' operator out of domain");
                    YYABORT;
                  }
                  if (errno == ERANGE) {
                    lex_error("result of `^' operator out of range");
                    YYABORT;
                  }
                }
        | '-' expr                                              %prec '!'
                { $$ = -$2; }
        | '(' any_expr ')'
                { $$ = $2; }
        | SIN '(' any_expr ')'
                {
                  errno = 0;
                  $$ = sin($3);
                  if (errno == ERANGE) {
                    lex_error("sin result out of range");
                    YYABORT;
                  }
                }
        | COS '(' any_expr ')'
                {
                  errno = 0;
                  $$ = cos($3);
                  if (errno == ERANGE) {
                    lex_error("cos result out of range");
                    YYABORT;
                  }
                }
        | ATAN2 '(' any_expr ',' any_expr ')'
                {
                  errno = 0;
                  $$ = atan2($3, $5);
                  if (errno == EDOM) {
                    lex_error("atan2 argument out of domain");
                    YYABORT;
                  }
                  if (errno == ERANGE) {
                    lex_error("atan2 result out of range");
                    YYABORT;
                  }
                }
        | LOG '(' any_expr ')'
                {
                  errno = 0;
                  $$ = log10($3);
                  if (errno == ERANGE) {
                    lex_error("log result out of range");
                    YYABORT;
                  }
                }
        | EXP '(' any_expr ')'
                {
                  errno = 0;
                  $$ = pow(10.0, $3);
                  if (errno == ERANGE) {
                    lex_error("exp result out of range");
                    YYABORT;
                  }
                }
        | SQRT '(' any_expr ')'
                {
                  errno = 0;
                  $$ = sqrt($3);
                  if (errno == EDOM) {
                    lex_error("sqrt argument out of domain");
                    YYABORT;
                  }
                }
        | K_MAX '(' any_expr ',' any_expr ')'
                { $$ = $3 > $5 ? $3 : $5; }
        | K_MIN '(' any_expr ',' any_expr ')'
                { $$ = $3 < $5 ? $3 : $5; }
        | INT '(' any_expr ')'
                { $$ = floor($3); }
        | RAND '(' any_expr ')'
                { $$ = 1.0 + floor(((rand()&0x7fff)/double(0x7fff))*$3); }
        | RAND '(' ')'
                {
                  /* return a random number in the range [0,1) */
                  /* portable, but not very random */
                  $$ = (rand() & 0x7fff) / double(0x8000);
                }
        | SRAND '(' any_expr ')'
                {
                  $$ = 0;
                  srand((unsigned int)$3);
                }
        | expr '<' expr
                { $$ = ($1 < $3); }
        | expr LESSEQUAL expr
                { $$ = ($1 <= $3); }
        | expr '>' expr
                { $$ = ($1 > $3); }
        | expr GREATEREQUAL expr
                { $$ = ($1 >= $3); }
        | expr EQUALEQUAL expr
                { $$ = ($1 == $3); }
        | expr NOTEQUAL expr
                { $$ = ($1 != $3); }
        | expr ANDAND expr
                { $$ = ($1 != 0.0 && $3 != 0.0); }
        | expr OROR expr
                { $$ = ($1 != 0.0 || $3 != 0.0); }
        | '!' expr
                { $$ = ($2 == 0.0); }

        ;

%%

/* bison defines const to be empty unless __STDC__ is defined, which it
isn't under cfront */

#ifdef const
#undef const
#endif

static struct {
  const char *name;
  double val;
  int scaled;                // non-zero if val should be multiplied by scale
} defaults_table[] = {
  { "arcrad", .25, 1 },
  { "arrowht", .1, 1 },
  { "arrowwid", .05, 1 },
  { "circlerad", .25, 1 },
  { "boxht", .5, 1 },
  { "boxwid", .75, 1 },
  { "boxrad", 0.0, 1 },
  { "dashwid", .05, 1 },
  { "ellipseht", .5, 1 },
  { "ellipsewid", .75, 1 },
  { "moveht", .5, 1 },
  { "movewid", .5, 1 },
  { "lineht", .5, 1 },
  { "linewid", .5, 1 },
  { "textht", 0.0, 1 },
  { "textwid", 0.0, 1 },
  { "scale", 1.0, 0 },
  { "linethick", -1.0, 0 },             // in points
  { "fillval", .5, 0 },
  { "arrowhead", 1.0, 0 },
  { "maxpswid", 8.5, 0 },
  { "maxpsht", 11.0, 0 },
};

place *lookup_label(const char *label)
{
  saved_state *state = current_saved_state;
  PTABLE(place) *tbl = current_table;
  for (;;) {
    place *pl = tbl->lookup(label);
    if (pl)
      return pl;
    if (!state)
      return 0;
    tbl = state->tbl;
    state = state->prev;
  }
}

void define_label(const char *label, const place *pl)
{
  place *p = new place[1];
  *p = *pl;
  current_table->define(label, p);
}

int lookup_variable(const char *name, double *val)
{
  place *pl = lookup_label(name);
  if (pl) {
    *val = pl->x;
    return 1;
  }
  return 0;
}

void define_variable(const char *name, double val)
{
  place *p = new place[1];
  p->obj = 0;
  p->x = val;
  p->y = 0.0;
  current_table->define(name, p);
  if (strcmp(name, "scale") == 0) {
    // When the scale changes, reset all scaled pre-defined variables to
    // their default values.
    for (unsigned int i = 0;
         i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++) 
      if (defaults_table[i].scaled)
        define_variable(defaults_table[i].name, val*defaults_table[i].val);
  }
}

// called once only (not once per parse)

void parse_init()
{
  current_direction = RIGHT_DIRECTION;
  current_position.x = 0.0;
  current_position.y = 0.0;
  // This resets everything to its default value.
  reset_all();
}

void reset(const char *nm)
{
  for (unsigned int i = 0;
       i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
    if (strcmp(nm, defaults_table[i].name) == 0) {
      double val = defaults_table[i].val;
      if (defaults_table[i].scaled) {
        double scale;
        lookup_variable("scale", &scale);
        val *= scale;
      }
      define_variable(defaults_table[i].name, val);
      return;
    }
  lex_error("`%1' is not a predefined variable", nm);
}

void reset_all()
{
  // We only have to explicitly reset the pre-defined variables that
  // aren't scaled because `scale' is not scaled, and changing the
  // value of `scale' will reset all the pre-defined variables that
  // are scaled.
  for (unsigned int i = 0;
       i < sizeof(defaults_table)/sizeof(defaults_table[0]); i++)
    if (!defaults_table[i].scaled)
      define_variable(defaults_table[i].name, defaults_table[i].val);
}

// called after each parse

void parse_cleanup()
{
  while (current_saved_state != 0) {
    delete current_table;
    current_table = current_saved_state->tbl;
    saved_state *tem = current_saved_state;
    current_saved_state = current_saved_state->prev;
    delete tem;
  }
  assert(current_table == &top_table);
  PTABLE_ITERATOR(place) iter(current_table);
  const char *key;
  place *pl;
  while (iter.next(&key, &pl))
    if (pl->obj != 0) {
      position pos = pl->obj->origin();
      pl->obj = 0;
      pl->x = pos.x;
      pl->y = pos.y;
    }
  while (olist.head != 0) {
    object *tem = olist.head;
    olist.head = olist.head->next;
    delete tem;
  }
  olist.tail = 0;
  current_direction = RIGHT_DIRECTION;
  current_position.x = 0.0;
  current_position.y = 0.0;
}

const char *ordinal_postfix(int n)
{
  if (n < 10 || n > 20)
    switch (n % 10) {
    case 1:
      return "st";
    case 2:
      return "nd";
    case 3:
      return "rd";
    }
  return "th";
}

const char *object_type_name(object_type type)
{
  switch (type) {
  case BOX_OBJECT:
    return "box";
  case CIRCLE_OBJECT:
    return "circle";
  case ELLIPSE_OBJECT:
    return "ellipse";
  case ARC_OBJECT:
    return "arc";
  case SPLINE_OBJECT:
    return "spline";
  case LINE_OBJECT:
    return "line";
  case ARROW_OBJECT:
    return "arrow";
  case MOVE_OBJECT:
    return "move";
  case TEXT_OBJECT:
    return "\"\"";
  case BLOCK_OBJECT:
    return "[]";
  case OTHER_OBJECT:
  case MARK_OBJECT:
  default:
    break;
  }
  return "object";
}

static char sprintf_buf[1024];

char *format_number(const char *form, double n)
{
  if (form == 0)
    form = "%g";
  return do_sprintf(form, &n, 1);
}

char *do_sprintf(const char *form, const double *v, int nv)
{
  string result;
  int i = 0;
  string one_format;
  while (*form) {
    if (*form == '%') {
      one_format += *form++;
      for (; *form != '\0' && strchr("#-+ 0123456789.", *form) != 0; form++)
        one_format += *form;
      if (*form == '\0' || strchr("eEfgG%", *form) == 0) {
        lex_error("bad sprintf format");
        result += one_format;
        result += form;
        break;
      }
      if (*form == '%') {
        one_format += *form++;
        one_format += '\0';
        snprintf(sprintf_buf, sizeof(sprintf_buf),
                 "%s", one_format.contents());
      }
      else {
        if (i >= nv) {
          lex_error("too few arguments to snprintf");
          result += one_format;
          result += form;
          break;
        }
        one_format += *form++;
        one_format += '\0';
        snprintf(sprintf_buf, sizeof(sprintf_buf),
                 one_format.contents(), v[i++]);
      }
      one_format.clear();
      result += sprintf_buf;
    }
    else
      result += *form++;
  }
  result += '\0';
  return strsave(result.contents());
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]