PageRenderTime 66ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/groff-1.21/src/preproc/pic/pic.y

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