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