PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/App/UnxUtilsSrc/unxutils/groff-1.11/pic/pic.y

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