home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / pic / pic.y < prev    next >
Encoding:
GNU Bison Grammar  |  1991-04-30  |  31.9 KB  |  1,689 lines

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