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