1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #include "pic.h"
22 #include "ptable.h"
23 #include "object.h"
24 #include "pic.tab.h"
25
26 declare_ptable(char)
27 implement_ptable(char)
28
29 PTABLE(char) macro_table;
30
31 class macro_input : public input {
32 char *s;
33 char *p;
34 public:
35 macro_input(const char *);
36 ~macro_input();
37 int get();
38 int peek();
39 };
40
41 class argument_macro_input : public input {
42 char *s;
43 char *p;
44 char *ap;
45 int argc;
46 char *argv[9];
47 public:
48 argument_macro_input(const char *, int, char **);
49 ~argument_macro_input();
50 int get();
51 int peek();
52 };
53
input()54 input::input() : next(0)
55 {
56 }
57
~input()58 input::~input()
59 {
60 }
61
get_location(const char **,int *)62 int input::get_location(const char **, int *)
63 {
64 return 0;
65 }
66
file_input(FILE * f,const char * fn)67 file_input::file_input(FILE *f, const char *fn)
68 : lineno(0), ptr(""), filename(fn)
69 {
70 fp = f;
71 }
72
~file_input()73 file_input::~file_input()
74 {
75 fclose(fp);
76 }
77
read_line()78 int file_input::read_line()
79 {
80 for (;;) {
81 line.clear();
82 lineno++;
83 for (;;) {
84 int c = getc(fp);
85 if (c == EOF)
86 break;
87 else if (illegal_input_char(c))
88 lex_error("illegal input character code %1", c);
89 else {
90 line += char(c);
91 if (c == '\n')
92 break;
93 }
94 }
95 if (line.length() == 0)
96 return 0;
97 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
98 && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
99 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
100 || compatible_flag))) {
101 line += '\0';
102 ptr = line.contents();
103 return 1;
104 }
105 }
106 }
107
get()108 int file_input::get()
109 {
110 if (*ptr != '\0' || read_line())
111 return (unsigned char)*ptr++;
112 else
113 return EOF;
114 }
115
peek()116 int file_input::peek()
117 {
118 if (*ptr != '\0' || read_line())
119 return (unsigned char)*ptr;
120 else
121 return EOF;
122 }
123
get_location(const char ** fnp,int * lnp)124 int file_input::get_location(const char **fnp, int *lnp)
125 {
126 *fnp = filename;
127 *lnp = lineno;
128 return 1;
129 }
130
macro_input(const char * str)131 macro_input::macro_input(const char *str)
132 {
133 p = s = strsave(str);
134 }
135
~macro_input()136 macro_input::~macro_input()
137 {
138 a_delete s;
139 }
140
get()141 int macro_input::get()
142 {
143 if (p == 0 || *p == '\0')
144 return EOF;
145 else
146 return (unsigned char)*p++;
147 }
148
peek()149 int macro_input::peek()
150 {
151 if (p == 0 || *p == '\0')
152 return EOF;
153 else
154 return (unsigned char)*p;
155 }
156
157 // Character respresenting $1. Must be illegal input character.
158 #define ARG1 14
159
process_body(const char * body)160 char *process_body(const char *body)
161 {
162 char *s = strsave(body);
163 int j = 0;
164 for (int i = 0; s[i] != '\0'; i++)
165 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
166 if (s[i+1] != '0')
167 s[j++] = ARG1 + s[++i] - '1';
168 }
169 else
170 s[j++] = s[i];
171 s[j] = '\0';
172 return s;
173 }
174
175
argument_macro_input(const char * body,int ac,char ** av)176 argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
177 : argc(ac), ap(0)
178 {
179 for (int i = 0; i < argc; i++)
180 argv[i] = av[i];
181 p = s = process_body(body);
182 }
183
184
~argument_macro_input()185 argument_macro_input::~argument_macro_input()
186 {
187 for (int i = 0; i < argc; i++)
188 a_delete argv[i];
189 a_delete s;
190 }
191
get()192 int argument_macro_input::get()
193 {
194 if (ap) {
195 if (*ap != '\0')
196 return (unsigned char)*ap++;
197 ap = 0;
198 }
199 if (p == 0)
200 return EOF;
201 while (*p >= ARG1 && *p <= ARG1 + 8) {
202 int i = *p++ - ARG1;
203 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
204 ap = argv[i];
205 return (unsigned char)*ap++;
206 }
207 }
208 if (*p == '\0')
209 return EOF;
210 return (unsigned char)*p++;
211 }
212
peek()213 int argument_macro_input::peek()
214 {
215 if (ap) {
216 if (*ap != '\0')
217 return (unsigned char)*ap;
218 ap = 0;
219 }
220 if (p == 0)
221 return EOF;
222 while (*p >= ARG1 && *p <= ARG1 + 8) {
223 int i = *p++ - ARG1;
224 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
225 ap = argv[i];
226 return (unsigned char)*ap;
227 }
228 }
229 if (*p == '\0')
230 return EOF;
231 return (unsigned char)*p;
232 }
233
234 class input_stack {
235 static input *current_input;
236 static int bol_flag;
237 public:
238 static void push(input *);
239 static void clear();
240 static int get_char();
241 static int peek_char();
242 static int get_location(const char **fnp, int *lnp);
243 static void push_back(unsigned char c, int was_bol = 0);
244 static int bol();
245 };
246
247 input *input_stack::current_input = 0;
248 int input_stack::bol_flag = 0;
249
bol()250 inline int input_stack::bol()
251 {
252 return bol_flag;
253 }
254
clear()255 void input_stack::clear()
256 {
257 while (current_input != 0) {
258 input *tem = current_input;
259 current_input = current_input->next;
260 delete tem;
261 }
262 bol_flag = 1;
263 }
264
push(input * in)265 void input_stack::push(input *in)
266 {
267 in->next = current_input;
268 current_input = in;
269 }
270
lex_init(input * top)271 void lex_init(input *top)
272 {
273 input_stack::clear();
274 input_stack::push(top);
275 }
276
lex_cleanup()277 void lex_cleanup()
278 {
279 while (input_stack::get_char() != EOF)
280 ;
281 }
282
get_char()283 int input_stack::get_char()
284 {
285 while (current_input != 0) {
286 int c = current_input->get();
287 if (c != EOF) {
288 bol_flag = c == '\n';
289 return c;
290 }
291 // don't pop the top-level input off the stack
292 if (current_input->next == 0)
293 return EOF;
294 input *tem = current_input;
295 current_input = current_input->next;
296 delete tem;
297 }
298 return EOF;
299 }
300
peek_char()301 int input_stack::peek_char()
302 {
303 while (current_input != 0) {
304 int c = current_input->peek();
305 if (c != EOF)
306 return c;
307 if (current_input->next == 0)
308 return EOF;
309 input *tem = current_input;
310 current_input = current_input->next;
311 delete tem;
312 }
313 return EOF;
314 }
315
316 class char_input : public input {
317 int c;
318 public:
319 char_input(int);
320 int get();
321 int peek();
322 };
323
char_input(int n)324 char_input::char_input(int n) : c((unsigned char)n)
325 {
326 }
327
get()328 int char_input::get()
329 {
330 int n = c;
331 c = EOF;
332 return n;
333 }
334
peek()335 int char_input::peek()
336 {
337 return c;
338 }
339
push_back(unsigned char c,int was_bol)340 void input_stack::push_back(unsigned char c, int was_bol)
341 {
342 push(new char_input(c));
343 bol_flag = was_bol;
344 }
345
get_location(const char ** fnp,int * lnp)346 int input_stack::get_location(const char **fnp, int *lnp)
347 {
348 for (input *p = current_input; p; p = p->next)
349 if (p->get_location(fnp, lnp))
350 return 1;
351 return 0;
352 }
353
354 string context_buffer;
355
356 string token_buffer;
357 double token_double;
358 int token_int;
359
interpolate_macro_with_args(const char * body)360 void interpolate_macro_with_args(const char *body)
361 {
362 char *argv[9];
363 int argc = 0;
364 for (int i = 0; i < 9; i++)
365 argv[i] = 0;
366 int level = 0;
367 int c;
368 enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
369 do {
370 token_buffer.clear();
371 for (;;) {
372 c = input_stack::get_char();
373 if (c == EOF) {
374 lex_error("end of input while scanning macro arguments");
375 break;
376 }
377 if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
378 if (token_buffer.length() > 0) {
379 token_buffer += '\0';
380 argv[argc] = strsave(token_buffer.contents());
381 }
382 // for `foo()', argc = 0
383 if (argc > 0 || c != ')' || i > 0)
384 argc++;
385 break;
386 }
387 token_buffer += char(c);
388 switch (state) {
389 case NORMAL:
390 if (c == '"')
391 state = IN_STRING;
392 else if (c == '(')
393 level++;
394 else if (c == ')')
395 level--;
396 break;
397 case IN_STRING:
398 if (c == '"')
399 state = NORMAL;
400 else if (c == '\\')
401 state = IN_STRING_QUOTED;
402 break;
403 case IN_STRING_QUOTED:
404 state = IN_STRING;
405 break;
406 }
407 }
408 } while (c != ')' && c != EOF);
409 input_stack::push(new argument_macro_input(body, argc, argv));
410 }
411
docmp(const char * s1,int n1,const char * s2,int n2)412 static int docmp(const char *s1, int n1, const char *s2, int n2)
413 {
414 if (n1 < n2) {
415 int r = memcmp(s1, s2, n1);
416 return r ? r : -1;
417 }
418 else if (n1 > n2) {
419 int r = memcmp(s1, s2, n2);
420 return r ? r : 1;
421 }
422 else
423 return memcmp(s1, s2, n1);
424 }
425
lookup_keyword(const char * str,int len)426 int lookup_keyword(const char *str, int len)
427 {
428 static struct keyword {
429 const char *name;
430 int token;
431 } table[] = {
432 "Here", HERE,
433 "above", ABOVE,
434 "aligned", ALIGNED,
435 "and", AND,
436 "arc", ARC,
437 "arrow", ARROW,
438 "at", AT,
439 "atan2", ATAN2,
440 "below", BELOW,
441 "between", BETWEEN,
442 "bottom", BOTTOM,
443 "box", BOX,
444 "by", BY,
445 "ccw", CCW,
446 "center", CENTER,
447 "chop", CHOP,
448 "circle", CIRCLE,
449 "command", COMMAND,
450 "copy", COPY,
451 "cos", COS,
452 "cw", CW,
453 "dashed", DASHED,
454 "define", DEFINE,
455 "diam", DIAMETER,
456 "diameter", DIAMETER,
457 "do", DO,
458 "dotted", DOTTED,
459 "down", DOWN,
460 "ellipse", ELLIPSE,
461 "else", ELSE,
462 "end", END,
463 "exp", EXP,
464 "fill", FILL,
465 "filled", FILL,
466 "for", FOR,
467 "from", FROM,
468 "height", HEIGHT,
469 "ht", HEIGHT,
470 "if", IF,
471 "int", INT,
472 "invis", INVISIBLE,
473 "invisible", INVISIBLE,
474 "last", LAST,
475 "left", LEFT,
476 "line", LINE,
477 "ljust", LJUST,
478 "log", LOG,
479 "lower", LOWER,
480 "max", K_MAX,
481 "min", K_MIN,
482 "move", MOVE,
483 "of", OF,
484 "plot", PLOT,
485 "print", PRINT,
486 "rad", RADIUS,
487 "radius", RADIUS,
488 "rand", RAND,
489 "reset", RESET,
490 "right", RIGHT,
491 "rjust", RJUST,
492 "same", SAME,
493 "sh", SH,
494 "sin", SIN,
495 "spline", SPLINE,
496 "sprintf", SPRINTF,
497 "sqrt", SQRT,
498 "start", START,
499 "the", THE,
500 "then", THEN,
501 "thick", THICKNESS,
502 "thickness", THICKNESS,
503 "thru", THRU,
504 "to", TO,
505 "top", TOP,
506 "undef", UNDEF,
507 "until", UNTIL,
508 "up", UP,
509 "upper", UPPER,
510 "way", WAY,
511 "wid", WIDTH,
512 "width", WIDTH,
513 "with", WITH,
514 };
515
516 const keyword *start = table;
517 const keyword *end = table + sizeof(table)/sizeof(table[0]);
518 while (start < end) {
519 // start <= target < end
520 const keyword *mid = start + (end - start)/2;
521
522 int cmp = docmp(str, len, mid->name, strlen(mid->name));
523 if (cmp == 0)
524 return mid->token;
525 if (cmp < 0)
526 end = mid;
527 else
528 start = mid + 1;
529 }
530 return 0;
531 }
532
get_token_after_dot(int c)533 int get_token_after_dot(int c)
534 {
535 // get_token deals with the case where c is a digit
536 switch (c) {
537 case 'h':
538 input_stack::get_char();
539 c = input_stack::peek_char();
540 if (c == 't') {
541 input_stack::get_char();
542 context_buffer = ".ht";
543 return DOT_HT;
544 }
545 else if (c == 'e') {
546 input_stack::get_char();
547 c = input_stack::peek_char();
548 if (c == 'i') {
549 input_stack::get_char();
550 c = input_stack::peek_char();
551 if (c == 'g') {
552 input_stack::get_char();
553 c = input_stack::peek_char();
554 if (c == 'h') {
555 input_stack::get_char();
556 c = input_stack::peek_char();
557 if (c == 't') {
558 input_stack::get_char();
559 context_buffer = ".height";
560 return DOT_HT;
561 }
562 input_stack::push_back('h');
563 }
564 input_stack::push_back('g');
565 }
566 input_stack::push_back('i');
567 }
568 input_stack::push_back('e');
569 }
570 input_stack::push_back('h');
571 return '.';
572 case 'x':
573 input_stack::get_char();
574 context_buffer = ".x";
575 return DOT_X;
576 case 'y':
577 input_stack::get_char();
578 context_buffer = ".y";
579 return DOT_Y;
580 case 'c':
581 input_stack::get_char();
582 c = input_stack::peek_char();
583 if (c == 'e') {
584 input_stack::get_char();
585 c = input_stack::peek_char();
586 if (c == 'n') {
587 input_stack::get_char();
588 c = input_stack::peek_char();
589 if (c == 't') {
590 input_stack::get_char();
591 c = input_stack::peek_char();
592 if (c == 'e') {
593 input_stack::get_char();
594 c = input_stack::peek_char();
595 if (c == 'r') {
596 input_stack::get_char();
597 context_buffer = ".center";
598 return DOT_C;
599 }
600 input_stack::push_back('e');
601 }
602 input_stack::push_back('t');
603 }
604 input_stack::push_back('n');
605 }
606 input_stack::push_back('e');
607 }
608 context_buffer = ".c";
609 return DOT_C;
610 case 'n':
611 input_stack::get_char();
612 c = input_stack::peek_char();
613 if (c == 'e') {
614 input_stack::get_char();
615 context_buffer = ".ne";
616 return DOT_NE;
617 }
618 else if (c == 'w') {
619 input_stack::get_char();
620 context_buffer = ".nw";
621 return DOT_NW;
622 }
623 else {
624 context_buffer = ".n";
625 return DOT_N;
626 }
627 break;
628 case 'e':
629 input_stack::get_char();
630 c = input_stack::peek_char();
631 if (c == 'n') {
632 input_stack::get_char();
633 c = input_stack::peek_char();
634 if (c == 'd') {
635 input_stack::get_char();
636 context_buffer = ".end";
637 return DOT_END;
638 }
639 input_stack::push_back('n');
640 context_buffer = ".e";
641 return DOT_E;
642 }
643 context_buffer = ".e";
644 return DOT_E;
645 case 'w':
646 input_stack::get_char();
647 c = input_stack::peek_char();
648 if (c == 'i') {
649 input_stack::get_char();
650 c = input_stack::peek_char();
651 if (c == 'd') {
652 input_stack::get_char();
653 c = input_stack::peek_char();
654 if (c == 't') {
655 input_stack::get_char();
656 c = input_stack::peek_char();
657 if (c == 'h') {
658 input_stack::get_char();
659 context_buffer = ".width";
660 return DOT_WID;
661 }
662 input_stack::push_back('t');
663 }
664 context_buffer = ".wid";
665 return DOT_WID;
666 }
667 input_stack::push_back('i');
668 }
669 context_buffer = ".w";
670 return DOT_W;
671 case 's':
672 input_stack::get_char();
673 c = input_stack::peek_char();
674 if (c == 'e') {
675 input_stack::get_char();
676 context_buffer = ".se";
677 return DOT_SE;
678 }
679 else if (c == 'w') {
680 input_stack::get_char();
681 context_buffer = ".sw";
682 return DOT_SW;
683 }
684 else {
685 if (c == 't') {
686 input_stack::get_char();
687 c = input_stack::peek_char();
688 if (c == 'a') {
689 input_stack::get_char();
690 c = input_stack::peek_char();
691 if (c == 'r') {
692 input_stack::get_char();
693 c = input_stack::peek_char();
694 if (c == 't') {
695 input_stack::get_char();
696 context_buffer = ".start";
697 return DOT_START;
698 }
699 input_stack::push_back('r');
700 }
701 input_stack::push_back('a');
702 }
703 input_stack::push_back('t');
704 }
705 context_buffer = ".s";
706 return DOT_S;
707 }
708 break;
709 case 't':
710 input_stack::get_char();
711 c = input_stack::peek_char();
712 if (c == 'o') {
713 input_stack::get_char();
714 c = input_stack::peek_char();
715 if (c == 'p') {
716 input_stack::get_char();
717 context_buffer = ".top";
718 return DOT_N;
719 }
720 input_stack::push_back('o');
721 }
722 context_buffer = ".t";
723 return DOT_N;
724 case 'l':
725 input_stack::get_char();
726 c = input_stack::peek_char();
727 if (c == 'e') {
728 input_stack::get_char();
729 c = input_stack::peek_char();
730 if (c == 'f') {
731 input_stack::get_char();
732 c = input_stack::peek_char();
733 if (c == 't') {
734 input_stack::get_char();
735 context_buffer = ".left";
736 return DOT_W;
737 }
738 input_stack::push_back('f');
739 }
740 input_stack::push_back('e');
741 }
742 context_buffer = ".l";
743 return DOT_W;
744 case 'r':
745 input_stack::get_char();
746 c = input_stack::peek_char();
747 if (c == 'a') {
748 input_stack::get_char();
749 c = input_stack::peek_char();
750 if (c == 'd') {
751 input_stack::get_char();
752 context_buffer = ".rad";
753 return DOT_RAD;
754 }
755 input_stack::push_back('a');
756 }
757 else if (c == 'i') {
758 input_stack::get_char();
759 c = input_stack::peek_char();
760 if (c == 'g') {
761 input_stack::get_char();
762 c = input_stack::peek_char();
763 if (c == 'h') {
764 input_stack::get_char();
765 c = input_stack::peek_char();
766 if (c == 't') {
767 input_stack::get_char();
768 context_buffer = ".right";
769 return DOT_E;
770 }
771 input_stack::push_back('h');
772 }
773 input_stack::push_back('g');
774 }
775 input_stack::push_back('i');
776 }
777 context_buffer = ".r";
778 return DOT_E;
779 case 'b':
780 input_stack::get_char();
781 c = input_stack::peek_char();
782 if (c == 'o') {
783 input_stack::get_char();
784 c = input_stack::peek_char();
785 if (c == 't') {
786 input_stack::get_char();
787 c = input_stack::peek_char();
788 if (c == 't') {
789 input_stack::get_char();
790 c = input_stack::peek_char();
791 if (c == 'o') {
792 input_stack::get_char();
793 c = input_stack::peek_char();
794 if (c == 'm') {
795 input_stack::get_char();
796 context_buffer = ".bottom";
797 return DOT_S;
798 }
799 input_stack::push_back('o');
800 }
801 input_stack::push_back('t');
802 }
803 context_buffer = ".bot";
804 return DOT_S;
805 }
806 input_stack::push_back('o');
807 }
808 context_buffer = ".b";
809 return DOT_S;
810 default:
811 context_buffer = '.';
812 return '.';
813 }
814 }
815
get_token(int lookup_flag)816 int get_token(int lookup_flag)
817 {
818 context_buffer.clear();
819 for (;;) {
820 int n = 0;
821 int bol = input_stack::bol();
822 int c = input_stack::get_char();
823 if (bol && c == command_char) {
824 token_buffer.clear();
825 token_buffer += c;
826 // the newline is not part of the token
827 for (;;) {
828 c = input_stack::peek_char();
829 if (c == EOF || c == '\n')
830 break;
831 input_stack::get_char();
832 token_buffer += char(c);
833 }
834 context_buffer = token_buffer;
835 return COMMAND_LINE;
836 }
837 switch (c) {
838 case EOF:
839 return EOF;
840 case ' ':
841 case '\t':
842 break;
843 case '\\':
844 {
845 int d = input_stack::peek_char();
846 if (d != '\n') {
847 context_buffer = '\\';
848 return '\\';
849 }
850 input_stack::get_char();
851 break;
852 }
853 case '#':
854 do {
855 c = input_stack::get_char();
856 } while (c != '\n' && c != EOF);
857 if (c == '\n')
858 context_buffer = '\n';
859 return c;
860 case '"':
861 context_buffer = '"';
862 token_buffer.clear();
863 for (;;) {
864 c = input_stack::get_char();
865 if (c == '\\') {
866 context_buffer += '\\';
867 c = input_stack::peek_char();
868 if (c == '"') {
869 input_stack::get_char();
870 token_buffer += '"';
871 context_buffer += '"';
872 }
873 else
874 token_buffer += '\\';
875 }
876 else if (c == '\n') {
877 error("newline in string");
878 break;
879 }
880 else if (c == EOF) {
881 error("missing `\"'");
882 break;
883 }
884 else if (c == '"') {
885 context_buffer += '"';
886 break;
887 }
888 else {
889 context_buffer += char(c);
890 token_buffer += char(c);
891 }
892 }
893 return TEXT;
894 case '0':
895 case '1':
896 case '2':
897 case '3':
898 case '4':
899 case '5':
900 case '6':
901 case '7':
902 case '8':
903 case '9':
904 {
905 int overflow = 0;
906 n = 0;
907 for (;;) {
908 if (n > (INT_MAX - 9)/10) {
909 overflow = 1;
910 break;
911 }
912 n *= 10;
913 n += c - '0';
914 context_buffer += char(c);
915 c = input_stack::peek_char();
916 if (c == EOF || !csdigit(c))
917 break;
918 c = input_stack::get_char();
919 }
920 token_double = n;
921 if (overflow) {
922 for (;;) {
923 token_double *= 10.0;
924 token_double += c - '0';
925 context_buffer += char(c);
926 c = input_stack::peek_char();
927 if (c == EOF || !csdigit(c))
928 break;
929 c = input_stack::get_char();
930 }
931 // if somebody asks for 1000000000000th, we will silently
932 // give them INT_MAXth
933 double temp = token_double; // work around gas 1.34/sparc bug
934 if (token_double > INT_MAX)
935 n = INT_MAX;
936 else
937 n = int(temp);
938 }
939 }
940 switch (c) {
941 case 'i':
942 case 'I':
943 context_buffer += char(c);
944 input_stack::get_char();
945 return NUMBER;
946 case '.':
947 {
948 context_buffer += '.';
949 input_stack::get_char();
950 got_dot:
951 double factor = 1.0;
952 for (;;) {
953 c = input_stack::peek_char();
954 if (!c == EOF || !csdigit(c))
955 break;
956 input_stack::get_char();
957 context_buffer += char(c);
958 factor /= 10.0;
959 if (c != '0')
960 token_double += factor*(c - '0');
961 }
962 if (c != 'e' && c != 'E') {
963 if (c == 'i' || c == 'I') {
964 context_buffer += char(c);
965 input_stack::get_char();
966 }
967 return NUMBER;
968 }
969 }
970 // fall through
971 case 'e':
972 case 'E':
973 {
974 int echar = c;
975 input_stack::get_char();
976 c = input_stack::peek_char();
977 int sign = '+';
978 if (c == '+' || c == '-') {
979 sign = c;
980 input_stack::get_char();
981 c = input_stack::peek_char();
982 if (c == EOF || !csdigit(c)) {
983 input_stack::push_back(sign);
984 input_stack::push_back(echar);
985 return NUMBER;
986 }
987 context_buffer += char(echar);
988 context_buffer += char(sign);
989 }
990 else {
991 if (c == EOF || !csdigit(c)) {
992 input_stack::push_back(echar);
993 return NUMBER;
994 }
995 context_buffer += char(echar);
996 }
997 input_stack::get_char();
998 context_buffer += char(c);
999 n = c - '0';
1000 for (;;) {
1001 c = input_stack::peek_char();
1002 if (c == EOF || !csdigit(c))
1003 break;
1004 input_stack::get_char();
1005 context_buffer += char(c);
1006 n = n*10 + (c - '0');
1007 }
1008 if (sign == '-')
1009 n = -n;
1010 if (c == 'i' || c == 'I') {
1011 context_buffer += char(c);
1012 input_stack::get_char();
1013 }
1014 token_double *= pow(10.0, n);
1015 return NUMBER;
1016 }
1017 case 'n':
1018 input_stack::get_char();
1019 c = input_stack::peek_char();
1020 if (c == 'd') {
1021 input_stack::get_char();
1022 token_int = n;
1023 context_buffer += "nd";
1024 return ORDINAL;
1025 }
1026 input_stack::push_back('n');
1027 return NUMBER;
1028 case 'r':
1029 input_stack::get_char();
1030 c = input_stack::peek_char();
1031 if (c == 'd') {
1032 input_stack::get_char();
1033 token_int = n;
1034 context_buffer += "rd";
1035 return ORDINAL;
1036 }
1037 input_stack::push_back('r');
1038 return NUMBER;
1039 case 't':
1040 input_stack::get_char();
1041 c = input_stack::peek_char();
1042 if (c == 'h') {
1043 input_stack::get_char();
1044 token_int = n;
1045 context_buffer += "th";
1046 return ORDINAL;
1047 }
1048 input_stack::push_back('t');
1049 return NUMBER;
1050 case 's':
1051 input_stack::get_char();
1052 c = input_stack::peek_char();
1053 if (c == 't') {
1054 input_stack::get_char();
1055 token_int = n;
1056 context_buffer += "st";
1057 return ORDINAL;
1058 }
1059 input_stack::push_back('s');
1060 return NUMBER;
1061 default:
1062 return NUMBER;
1063 }
1064 break;
1065 case '\'':
1066 {
1067 c = input_stack::peek_char();
1068 if (c == 't') {
1069 input_stack::get_char();
1070 c = input_stack::peek_char();
1071 if (c == 'h') {
1072 input_stack::get_char();
1073 context_buffer = "'th";
1074 return TH;
1075 }
1076 else
1077 input_stack::push_back('t');
1078 }
1079 context_buffer = "'";
1080 return '\'';
1081 }
1082 case '.':
1083 {
1084 c = input_stack::peek_char();
1085 if (c != EOF && csdigit(c)) {
1086 n = 0;
1087 token_double = 0.0;
1088 context_buffer = '.';
1089 goto got_dot;
1090 }
1091 return get_token_after_dot(c);
1092 }
1093 case '<':
1094 c = input_stack::peek_char();
1095 if (c == '-') {
1096 input_stack::get_char();
1097 c = input_stack::peek_char();
1098 if (c == '>') {
1099 input_stack::get_char();
1100 context_buffer = "<->";
1101 return DOUBLE_ARROW_HEAD;
1102 }
1103 context_buffer = "<-";
1104 return LEFT_ARROW_HEAD;
1105 }
1106 else if (c == '=') {
1107 input_stack::get_char();
1108 context_buffer = "<=";
1109 return LESSEQUAL;
1110 }
1111 context_buffer = "<";
1112 return '<';
1113 case '-':
1114 c = input_stack::peek_char();
1115 if (c == '>') {
1116 input_stack::get_char();
1117 context_buffer = "->";
1118 return RIGHT_ARROW_HEAD;
1119 }
1120 context_buffer = "-";
1121 return '-';
1122 case '!':
1123 c = input_stack::peek_char();
1124 if (c == '=') {
1125 input_stack::get_char();
1126 context_buffer = "!=";
1127 return NOTEQUAL;
1128 }
1129 context_buffer = "!";
1130 return '!';
1131 case '>':
1132 c = input_stack::peek_char();
1133 if (c == '=') {
1134 input_stack::get_char();
1135 context_buffer = ">=";
1136 return GREATEREQUAL;
1137 }
1138 context_buffer = ">";
1139 return '>';
1140 case '=':
1141 c = input_stack::peek_char();
1142 if (c == '=') {
1143 input_stack::get_char();
1144 context_buffer = "==";
1145 return EQUALEQUAL;
1146 }
1147 context_buffer = "=";
1148 return '=';
1149 case '&':
1150 c = input_stack::peek_char();
1151 if (c == '&') {
1152 input_stack::get_char();
1153 context_buffer = "&&";
1154 return ANDAND;
1155 }
1156 context_buffer = "&";
1157 return '&';
1158 case '|':
1159 c = input_stack::peek_char();
1160 if (c == '|') {
1161 input_stack::get_char();
1162 context_buffer = "||";
1163 return OROR;
1164 }
1165 context_buffer = "|";
1166 return '|';
1167 default:
1168 if (c != EOF && csalpha(c)) {
1169 token_buffer.clear();
1170 token_buffer = c;
1171 for (;;) {
1172 c = input_stack::peek_char();
1173 if (c == EOF || (!csalnum(c) && c != '_'))
1174 break;
1175 input_stack::get_char();
1176 token_buffer += char(c);
1177 }
1178 int tok = lookup_keyword(token_buffer.contents(),
1179 token_buffer.length());
1180 if (tok != 0) {
1181 context_buffer = token_buffer;
1182 return tok;
1183 }
1184 char *def = 0;
1185 if (lookup_flag) {
1186 token_buffer += '\0';
1187 def = macro_table.lookup(token_buffer.contents());
1188 token_buffer.set_length(token_buffer.length() - 1);
1189 if (def) {
1190 if (c == '(') {
1191 input_stack::get_char();
1192 interpolate_macro_with_args(def);
1193 }
1194 else
1195 input_stack::push(new macro_input(def));
1196 }
1197 }
1198 if (!def) {
1199 context_buffer = token_buffer;
1200 if (csupper(token_buffer[0]))
1201 return LABEL;
1202 else
1203 return VARIABLE;
1204 }
1205 }
1206 else {
1207 context_buffer = char(c);
1208 return (unsigned char)c;
1209 }
1210 break;
1211 }
1212 }
1213 }
1214
get_delimited()1215 int get_delimited()
1216 {
1217 token_buffer.clear();
1218 int c = input_stack::get_char();
1219 while (c == ' ' || c == '\t' || c == '\n')
1220 c = input_stack::get_char();
1221 if (c == EOF) {
1222 lex_error("missing delimiter");
1223 return 0;
1224 }
1225 context_buffer = char(c);
1226 int had_newline = 0;
1227 int start = c;
1228 int level = 0;
1229 enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1230 for (;;) {
1231 c = input_stack::get_char();
1232 if (c == EOF) {
1233 lex_error("missing closing delimiter");
1234 return 0;
1235 }
1236 if (c == '\n')
1237 had_newline = 1;
1238 else if (!had_newline)
1239 context_buffer += char(c);
1240 switch (state) {
1241 case NORMAL:
1242 if (start == '{') {
1243 if (c == '{') {
1244 level++;
1245 break;
1246 }
1247 if (c == '}') {
1248 if (--level < 0)
1249 state = DELIM_END;
1250 break;
1251 }
1252 }
1253 else {
1254 if (c == start) {
1255 state = DELIM_END;
1256 break;
1257 }
1258 }
1259 if (c == '"')
1260 state = IN_STRING;
1261 break;
1262 case IN_STRING_QUOTED:
1263 if (c == '\n')
1264 state = NORMAL;
1265 else
1266 state = IN_STRING;
1267 break;
1268 case IN_STRING:
1269 if (c == '"' || c == '\n')
1270 state = NORMAL;
1271 else if (c == '\\')
1272 state = IN_STRING_QUOTED;
1273 break;
1274 case DELIM_END:
1275 // This case it just to shut cfront 2.0 up.
1276 default:
1277 assert(0);
1278 }
1279 if (state == DELIM_END)
1280 break;
1281 token_buffer += c;
1282 }
1283 return 1;
1284 }
1285
do_define()1286 void do_define()
1287 {
1288 int t = get_token(0); // do not expand what we are defining
1289 if (t != VARIABLE && t != LABEL) {
1290 lex_error("can only define variable or placename");
1291 return;
1292 }
1293 token_buffer += '\0';
1294 string nm = token_buffer;
1295 const char *name = nm.contents();
1296 if (!get_delimited())
1297 return;
1298 token_buffer += '\0';
1299 macro_table.define(name, strsave(token_buffer.contents()));
1300 }
1301
do_undef()1302 void do_undef()
1303 {
1304 int t = get_token(0); // do not expand what we are undefining
1305 if (t != VARIABLE && t != LABEL) {
1306 lex_error("can only define variable or placename");
1307 return;
1308 }
1309 token_buffer += '\0';
1310 macro_table.define(token_buffer.contents(), 0);
1311 }
1312
1313
1314 class for_input : public input {
1315 char *var;
1316 char *body;
1317 double to;
1318 int by_is_multiplicative;
1319 double by;
1320 const char *p;
1321 int done_newline;
1322 public:
1323 for_input(char *, double, int, double, char *);
1324 ~for_input();
1325 int get();
1326 int peek();
1327 };
1328
for_input(char * vr,double t,int bim,double b,char * bd)1329 for_input::for_input(char *vr, double t, int bim, double b, char *bd)
1330 : var(vr), to(t), by_is_multiplicative(bim), by(b), body(bd), p(body),
1331 done_newline(0)
1332 {
1333 }
1334
~for_input()1335 for_input::~for_input()
1336 {
1337 a_delete var;
1338 a_delete body;
1339 }
1340
get()1341 int for_input::get()
1342 {
1343 if (p == 0)
1344 return EOF;
1345 for (;;) {
1346 if (*p != '\0')
1347 return (unsigned char)*p++;
1348 if (!done_newline) {
1349 done_newline = 1;
1350 return '\n';
1351 }
1352 double val;
1353 if (!lookup_variable(var, &val)) {
1354 lex_error("body of `for' terminated enclosing block");
1355 return EOF;
1356 }
1357 if (by_is_multiplicative)
1358 val *= by;
1359 else
1360 val += by;
1361 define_variable(var, val);
1362 if (val > to) {
1363 p = 0;
1364 return EOF;
1365 }
1366 p = body;
1367 done_newline = 0;
1368 }
1369 }
1370
peek()1371 int for_input::peek()
1372 {
1373 if (p == 0)
1374 return EOF;
1375 if (*p != '\0')
1376 return (unsigned char)*p;
1377 if (!done_newline)
1378 return '\n';
1379 double val;
1380 if (!lookup_variable(var, &val))
1381 return EOF;
1382 if (by_is_multiplicative) {
1383 if (val * by > to)
1384 return EOF;
1385 }
1386 else {
1387 if (val + by > to)
1388 return EOF;
1389 }
1390 if (*body == '\0')
1391 return EOF;
1392 return (unsigned char)*body;
1393 }
1394
do_for(char * var,double from,double to,int by_is_multiplicative,double by,char * body)1395 void do_for(char *var, double from, double to, int by_is_multiplicative,
1396 double by, char *body)
1397 {
1398 define_variable(var, from);
1399 if (from <= to)
1400 input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
1401 }
1402
1403
do_copy(const char * filename)1404 void do_copy(const char *filename)
1405 {
1406 errno = 0;
1407 FILE *fp = fopen(filename, "r");
1408 if (fp == 0) {
1409 lex_error("can't open `%1': %2", filename, strerror(errno));
1410 return;
1411 }
1412 input_stack::push(new file_input(fp, filename));
1413 }
1414
1415 class copy_thru_input : public input {
1416 int done;
1417 char *body;
1418 char *until;
1419 const char *p;
1420 const char *ap;
1421 int argv[9];
1422 int argc;
1423 string line;
1424 int get_line();
1425 virtual int inget() = 0;
1426 public:
1427 copy_thru_input(const char *b, const char *u);
1428 ~copy_thru_input();
1429 int get();
1430 int peek();
1431 };
1432
1433 class copy_file_thru_input : public copy_thru_input {
1434 input *in;
1435 public:
1436 copy_file_thru_input(input *, const char *b, const char *u);
1437 ~copy_file_thru_input();
1438 int inget();
1439 };
1440
copy_file_thru_input(input * i,const char * b,const char * u)1441 copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1442 const char *u)
1443 : in(i), copy_thru_input(b, u)
1444 {
1445 }
1446
~copy_file_thru_input()1447 copy_file_thru_input::~copy_file_thru_input()
1448 {
1449 delete in;
1450 }
1451
inget()1452 int copy_file_thru_input::inget()
1453 {
1454 if (!in)
1455 return EOF;
1456 else
1457 return in->get();
1458 }
1459
1460 class copy_rest_thru_input : public copy_thru_input {
1461 public:
1462 copy_rest_thru_input(const char *, const char *u);
1463 int inget();
1464 };
1465
copy_rest_thru_input(const char * b,const char * u)1466 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1467 : copy_thru_input(b, u)
1468 {
1469 }
1470
inget()1471 int copy_rest_thru_input::inget()
1472 {
1473 while (next != 0) {
1474 int c = next->get();
1475 if (c != EOF)
1476 return c;
1477 if (next->next == 0)
1478 return EOF;
1479 input *tem = next;
1480 next = next->next;
1481 delete tem;
1482 }
1483 return EOF;
1484
1485 }
1486
copy_thru_input(const char * b,const char * u)1487 copy_thru_input::copy_thru_input(const char *b, const char *u)
1488 : done(0)
1489 {
1490 ap = 0;
1491 body = process_body(b);
1492 p = 0;
1493 until = strsave(u);
1494 }
1495
1496
~copy_thru_input()1497 copy_thru_input::~copy_thru_input()
1498 {
1499 a_delete body;
1500 a_delete until;
1501 }
1502
get()1503 int copy_thru_input::get()
1504 {
1505 if (ap) {
1506 if (*ap != '\0')
1507 return (unsigned char)*ap++;
1508 ap = 0;
1509 }
1510 for (;;) {
1511 if (p == 0) {
1512 if (!get_line())
1513 break;
1514 p = body;
1515 }
1516 if (*p == '\0') {
1517 p = 0;
1518 return '\n';
1519 }
1520 while (*p >= ARG1 && *p <= ARG1 + 8) {
1521 int i = *p++ - ARG1;
1522 if (i < argc && line[argv[i]] != '\0') {
1523 ap = line.contents() + argv[i];
1524 return (unsigned char)*ap++;
1525 }
1526 }
1527 if (*p != '\0')
1528 return (unsigned char)*p++;
1529 }
1530 return EOF;
1531 }
1532
peek()1533 int copy_thru_input::peek()
1534 {
1535 if (ap) {
1536 if (*ap != '\0')
1537 return (unsigned char)*ap;
1538 ap = 0;
1539 }
1540 for (;;) {
1541 if (p == 0) {
1542 if (!get_line())
1543 break;
1544 p = body;
1545 }
1546 if (*p == '\0')
1547 return '\n';
1548 while (*p >= ARG1 && *p <= ARG1 + 8) {
1549 int i = *p++ - ARG1;
1550 if (i < argc && line[argv[i]] != '\0') {
1551 ap = line.contents() + argv[i];
1552 return (unsigned char)*ap;
1553 }
1554 }
1555 if (*p != '\0')
1556 return (unsigned char)*p;
1557 }
1558 return EOF;
1559 }
1560
get_line()1561 int copy_thru_input::get_line()
1562 {
1563 if (done)
1564 return 0;
1565 line.clear();
1566 argc = 0;
1567 int c = inget();
1568 for (;;) {
1569 while (c == ' ')
1570 c = inget();
1571 if (c == EOF || c == '\n')
1572 break;
1573 if (argc == 9) {
1574 do {
1575 c = inget();
1576 } while (c != '\n' && c != EOF);
1577 break;
1578 }
1579 argv[argc++] = line.length();
1580 do {
1581 line += char(c);
1582 c = inget();
1583 } while (c != ' ' && c != '\n');
1584 line += '\0';
1585 }
1586 if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1587 done = 1;
1588 return 0;
1589 }
1590 return argc > 0 || c == '\n';
1591 }
1592
1593 class simple_file_input : public input {
1594 const char *filename;
1595 int lineno;
1596 FILE *fp;
1597 public:
1598 simple_file_input(FILE *, const char *);
1599 ~simple_file_input();
1600 int get();
1601 int peek();
1602 int get_location(const char **, int *);
1603 };
1604
simple_file_input(FILE * p,const char * s)1605 simple_file_input::simple_file_input(FILE *p, const char *s)
1606 : filename(s), fp(p), lineno(1)
1607 {
1608 }
1609
~simple_file_input()1610 simple_file_input::~simple_file_input()
1611 {
1612 // don't delete the filename
1613 fclose(fp);
1614 }
1615
get()1616 int simple_file_input::get()
1617 {
1618 int c = getc(fp);
1619 while (illegal_input_char(c)) {
1620 error("illegal input character code %1", c);
1621 c = getc(fp);
1622 }
1623 if (c == '\n')
1624 lineno++;
1625 return c;
1626 }
1627
peek()1628 int simple_file_input::peek()
1629 {
1630 int c = getc(fp);
1631 while (illegal_input_char(c)) {
1632 error("illegal input character code %1", c);
1633 c = getc(fp);
1634 }
1635 if (c != EOF)
1636 ungetc(c, fp);
1637 return c;
1638 }
1639
get_location(const char ** fnp,int * lnp)1640 int simple_file_input::get_location(const char **fnp, int *lnp)
1641 {
1642 *fnp = filename;
1643 *lnp = lineno;
1644 return 1;
1645 }
1646
1647
copy_file_thru(const char * filename,const char * body,const char * until)1648 void copy_file_thru(const char *filename, const char *body, const char *until)
1649 {
1650 errno = 0;
1651 FILE *fp = fopen(filename, "r");
1652 if (fp == 0) {
1653 lex_error("can't open `%1': %2", filename, strerror(errno));
1654 return;
1655 }
1656 input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1657 body, until);
1658 input_stack::push(in);
1659 }
1660
copy_rest_thru(const char * body,const char * until)1661 void copy_rest_thru(const char *body, const char *until)
1662 {
1663 input_stack::push(new copy_rest_thru_input(body, until));
1664 }
1665
push_body(const char * s)1666 void push_body(const char *s)
1667 {
1668 input_stack::push(new char_input('\n'));
1669 input_stack::push(new macro_input(s));
1670 }
1671
1672 int delim_flag = 0;
1673
get_thru_arg()1674 char *get_thru_arg()
1675 {
1676 int c = input_stack::peek_char();
1677 while (c == ' ') {
1678 input_stack::get_char();
1679 c = input_stack::peek_char();
1680 }
1681 if (c != EOF && csalpha(c)) {
1682 // looks like a macro
1683 input_stack::get_char();
1684 token_buffer = c;
1685 for (;;) {
1686 c = input_stack::peek_char();
1687 if (c == EOF || (!csalnum(c) && c != '_'))
1688 break;
1689 input_stack::get_char();
1690 token_buffer += char(c);
1691 }
1692 context_buffer = token_buffer;
1693 token_buffer += '\0';
1694 char *def = macro_table.lookup(token_buffer.contents());
1695 if (def)
1696 return strsave(def);
1697 // I guess it wasn't a macro after all; so push the macro name back.
1698 // -2 because we added a '\0'
1699 for (int i = token_buffer.length() - 2; i >= 0; i--)
1700 input_stack::push_back(token_buffer[i]);
1701 }
1702 if (get_delimited()) {
1703 token_buffer += '\0';
1704 return strsave(token_buffer.contents());
1705 }
1706 else
1707 return 0;
1708 }
1709
1710 int lookahead_token = -1;
1711 string old_context_buffer;
1712
do_lookahead()1713 void do_lookahead()
1714 {
1715 if (lookahead_token == -1) {
1716 old_context_buffer = context_buffer;
1717 lookahead_token = get_token(1);
1718 }
1719 }
1720
yylex()1721 int yylex()
1722 {
1723 if (delim_flag) {
1724 assert(lookahead_token == -1);
1725 if (delim_flag == 2) {
1726 if ((yylval.str = get_thru_arg()) != 0)
1727 return DELIMITED;
1728 else
1729 return 0;
1730 }
1731 else {
1732 if (get_delimited()) {
1733 token_buffer += '\0';
1734 yylval.str = strsave(token_buffer.contents());
1735 return DELIMITED;
1736 }
1737 else
1738 return 0;
1739 }
1740 }
1741 for (;;) {
1742 int t;
1743 if (lookahead_token >= 0) {
1744 t = lookahead_token;
1745 lookahead_token = -1;
1746 }
1747 else
1748 t = get_token(1);
1749 switch (t) {
1750 case '\n':
1751 return ';';
1752 case EOF:
1753 return 0;
1754 case DEFINE:
1755 do_define();
1756 break;
1757 case UNDEF:
1758 do_undef();
1759 break;
1760 case ORDINAL:
1761 yylval.n = token_int;
1762 return t;
1763 case NUMBER:
1764 yylval.x = token_double;
1765 return t;
1766 case COMMAND_LINE:
1767 case TEXT:
1768 token_buffer += '\0';
1769 if (!input_stack::get_location(&yylval.lstr.filename,
1770 &yylval.lstr.lineno)) {
1771 yylval.lstr.filename = 0;
1772 yylval.lstr.lineno = -1;
1773 }
1774 yylval.lstr.str = strsave(token_buffer.contents());
1775 return t;
1776 case LABEL:
1777 case VARIABLE:
1778 token_buffer += '\0';
1779 yylval.str = strsave(token_buffer.contents());
1780 return t;
1781 case LEFT:
1782 // change LEFT to LEFT_CORNER when followed by OF
1783 old_context_buffer = context_buffer;
1784 lookahead_token = get_token(1);
1785 if (lookahead_token == OF)
1786 return LEFT_CORNER;
1787 else
1788 return t;
1789 case RIGHT:
1790 // change RIGHT to RIGHT_CORNER when followed by OF
1791 old_context_buffer = context_buffer;
1792 lookahead_token = get_token(1);
1793 if (lookahead_token == OF)
1794 return RIGHT_CORNER;
1795 else
1796 return t;
1797 case UPPER:
1798 // recognise UPPER only before LEFT or RIGHT
1799 old_context_buffer = context_buffer;
1800 lookahead_token = get_token(1);
1801 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1802 yylval.str = strsave("upper");
1803 return VARIABLE;
1804 }
1805 else
1806 return t;
1807 case LOWER:
1808 // recognise LOWER only before LEFT or RIGHT
1809 old_context_buffer = context_buffer;
1810 lookahead_token = get_token(1);
1811 if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1812 yylval.str = strsave("lower");
1813 return VARIABLE;
1814 }
1815 else
1816 return t;
1817 case TOP:
1818 // recognise TOP only before OF
1819 old_context_buffer = context_buffer;
1820 lookahead_token = get_token(1);
1821 if (lookahead_token != OF) {
1822 yylval.str = strsave("top");
1823 return VARIABLE;
1824 }
1825 else
1826 return t;
1827 case BOTTOM:
1828 // recognise BOTTOM only before OF
1829 old_context_buffer = context_buffer;
1830 lookahead_token = get_token(1);
1831 if (lookahead_token != OF) {
1832 yylval.str = strsave("bottom");
1833 return VARIABLE;
1834 }
1835 else
1836 return t;
1837 case CENTER:
1838 // recognise CENTER only before OF
1839 old_context_buffer = context_buffer;
1840 lookahead_token = get_token(1);
1841 if (lookahead_token != OF) {
1842 yylval.str = strsave("center");
1843 return VARIABLE;
1844 }
1845 else
1846 return t;
1847 case START:
1848 // recognise START only before OF
1849 old_context_buffer = context_buffer;
1850 lookahead_token = get_token(1);
1851 if (lookahead_token != OF) {
1852 yylval.str = strsave("start");
1853 return VARIABLE;
1854 }
1855 else
1856 return t;
1857 case END:
1858 // recognise END only before OF
1859 old_context_buffer = context_buffer;
1860 lookahead_token = get_token(1);
1861 if (lookahead_token != OF) {
1862 yylval.str = strsave("end");
1863 return VARIABLE;
1864 }
1865 else
1866 return t;
1867 default:
1868 return t;
1869 }
1870 }
1871 }
1872
lex_error(const char * message,const errarg & arg1,const errarg & arg2,const errarg & arg3)1873 void lex_error(const char *message,
1874 const errarg &arg1,
1875 const errarg &arg2,
1876 const errarg &arg3)
1877 {
1878 const char *filename;
1879 int lineno;
1880 if (!input_stack::get_location(&filename, &lineno))
1881 error(message, arg1, arg2, arg3);
1882 else
1883 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1884 }
1885
lex_warning(const char * message,const errarg & arg1,const errarg & arg2,const errarg & arg3)1886 void lex_warning(const char *message,
1887 const errarg &arg1,
1888 const errarg &arg2,
1889 const errarg &arg3)
1890 {
1891 const char *filename;
1892 int lineno;
1893 if (!input_stack::get_location(&filename, &lineno))
1894 warning(message, arg1, arg2, arg3);
1895 else
1896 warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1897 }
1898
yyerror(const char * s)1899 void yyerror(const char *s)
1900 {
1901 const char *filename;
1902 int lineno;
1903 const char *context = 0;
1904 if (lookahead_token == -1) {
1905 if (context_buffer.length() > 0) {
1906 context_buffer += '\0';
1907 context = context_buffer.contents();
1908 }
1909 }
1910 else {
1911 if (old_context_buffer.length() > 0) {
1912 old_context_buffer += '\0';
1913 context = old_context_buffer.contents();
1914 }
1915 }
1916 if (!input_stack::get_location(&filename, &lineno)) {
1917 if (context) {
1918 if (context[0] == '\n' && context[1] == '\0')
1919 error("%1 before newline", s);
1920 else
1921 error("%1 before `%2'", s, context);
1922 }
1923 else
1924 error("%1 at end of picture", s);
1925 }
1926 else {
1927 if (context) {
1928 if (context[0] == '\n' && context[1] == '\0')
1929 error_with_file_and_line(filename, lineno, "%1 before newline", s);
1930 else
1931 error_with_file_and_line(filename, lineno, "%1 before `%2'",
1932 s, context);
1933 }
1934 else
1935 error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
1936 }
1937 }
1938
1939