PageRenderTime 33ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/groff/src/preproc/pic/pic.y

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