home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / groff-1.09-src.lha / src / amiga / groff-1.09 / pic / pic.y < prev    next >
GNU Bison Grammar  |  1994-02-13  |  35KB  |  1,781 lines

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