Remove no longer needed catman periodic via 'make upgrade'.
[dragonfly.git] / contrib / groff / src / preproc / pic / lex.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004, 2006,
3                  2007, 2009
4      Free Software Foundation, Inc.
5      Written by James Clark (jjc@jclark.com)
6
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
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 // 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
42 class macro_input : public input {
43   char *s;
44   char *p;
45 public:
46   macro_input(const char *);
47   ~macro_input();
48   int get();
49   int peek();
50 };
51
52 class argument_macro_input : public input {
53   char *s;
54   char *p;
55   char *ap;
56   int argc;
57   char *argv[MAX_ARG];
58 public:
59   argument_macro_input(const char *, int, char **);
60   ~argument_macro_input();
61   int get();
62   int peek();
63 };
64
65 input::input() : next(0)
66 {
67 }
68
69 input::~input()
70 {
71 }
72
73 int input::get_location(const char **, int *)
74 {
75   return 0;
76 }
77
78 file_input::file_input(FILE *f, const char *fn)
79 : fp(f), filename(fn), lineno(0), ptr("")
80 {
81 }
82
83 file_input::~file_input()
84 {
85   fclose(fp);
86 }
87
88 int 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
118 int file_input::get()
119 {
120   if (*ptr != '\0' || read_line())
121     return (unsigned char)*ptr++;
122   else
123     return EOF;
124 }
125
126 int file_input::peek()
127 {
128   if (*ptr != '\0' || read_line())
129     return (unsigned char)*ptr;
130   else
131     return EOF;
132 }
133
134 int file_input::get_location(const char **fnp, int *lnp)
135 {
136   *fnp = filename;
137   *lnp = lineno;
138   return 1;
139 }
140
141 macro_input::macro_input(const char *str)
142 {
143   p = s = strsave(str);
144 }
145
146 macro_input::~macro_input()
147 {
148   a_delete s;
149 }
150
151 int macro_input::get()
152 {
153   if (p == 0 || *p == '\0')
154     return EOF;
155   else
156     return (unsigned char)*p++;
157 }
158
159 int macro_input::peek()
160 {
161   if (p == 0 || *p == '\0')
162     return EOF;
163   else
164     return (unsigned char)*p;
165 }
166
167 char *process_body(const char *body)
168 {
169   char *s = strsave(body);
170   int j = 0;
171   for (int i = 0; s[i] != '\0'; i++)
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--;
190     }
191     else
192       s[j++] = s[i];
193   s[j] = '\0';
194   return s;
195 }
196
197 argument_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
205 argument_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
212 int 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;
221   while ((unsigned char)*p >= ARG1 
222          && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
223     int i = (unsigned char)*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 int 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;
243   while ((unsigned char)*p >= ARG1
244          && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
245     int i = (unsigned char)*p++ - ARG1;
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
256 class input_stack {
257   static input *current_input;
258   static int bol_flag;
259 public:
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
269 input *input_stack::current_input = 0;
270 int input_stack::bol_flag = 0;
271
272 inline int input_stack::bol()
273 {
274   return bol_flag;
275 }
276
277 void 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
287 void input_stack::push(input *in)
288 {
289   in->next = current_input;
290   current_input = in;
291 }
292
293 void lex_init(input *top)
294 {
295   input_stack::clear();
296   input_stack::push(top);
297 }
298
299 void lex_cleanup()
300 {
301   while (input_stack::get_char() != EOF)
302     ;
303 }
304
305 int 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
323 int 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
338 class char_input : public input {
339   int c;
340 public:
341   char_input(int);
342   int get();
343   int peek();
344 };
345
346 char_input::char_input(int n) : c((unsigned char)n)
347 {
348 }
349
350 int char_input::get()
351 {
352   int n = c;
353   c = EOF;
354   return n;
355 }
356
357 int char_input::peek()
358 {
359   return c;
360 }
361
362 void input_stack::push_back(unsigned char c, int was_bol)
363 {
364   push(new char_input(c));
365   bol_flag = was_bol;
366 }
367
368 int 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
376 string context_buffer;
377
378 string token_buffer;
379 double token_double;
380 int token_int;
381
382 void interpolate_macro_with_args(const char *body)
383 {
384   char *argv[MAX_ARG];
385   int argc = 0;
386   int ignore = 0;
387   int i;
388   for (i = 0; i < MAX_ARG; i++)
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';
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           }
412         }
413         // for `foo()', argc = 0
414         if (argc > 0 || c != ')' || i > 0)
415           if (!ignore)
416             argc++;
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
444 static 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
458 int 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 },
560     { "xslanted", XSLANTED },
561     { "yslanted", YSLANTED },
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
581 int 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
864 int 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
1263 int 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
1334 void 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
1350 void 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
1362 class for_input : public input {
1363   char *var;
1364   char *body;
1365   double from;
1366   double to;
1367   int by_is_multiplicative;
1368   double by;
1369   const char *p;
1370   int done_newline;
1371 public:
1372   for_input(char *, double, double, int, double, char *);
1373   ~for_input();
1374   int get();
1375   int peek();
1376 };
1377
1378 for_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)
1382 {
1383 }
1384
1385 for_input::~for_input()
1386 {
1387   a_delete var;
1388   a_delete body;
1389 }
1390
1391 int 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);
1412     if ((from <= to && val > to)
1413         || (from >= to && val < to)) {
1414       p = 0;
1415       return EOF;
1416     }
1417     p = body;
1418     done_newline = 0;
1419   }
1420 }
1421
1422 int 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 {
1438     if ((from <= to && val + by > to)
1439         || (from >= to && val + by < to))
1440       return EOF;
1441   }
1442   if (*body == '\0')
1443     return EOF;
1444   return (unsigned char)*body;
1445 }
1446
1447 void do_for(char *var, double from, double to, int by_is_multiplicative,
1448             double by, char *body)
1449 {
1450   define_variable(var, from);
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));
1457 }
1458
1459
1460 void 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
1471 class copy_thru_input : public input {
1472   int done;
1473   char *body;
1474   char *until;
1475   const char *p;
1476   const char *ap;
1477   int argv[MAX_ARG];
1478   int argc;
1479   string line;
1480   int get_line();
1481   virtual int inget() = 0;
1482 public:
1483   copy_thru_input(const char *b, const char *u);
1484   ~copy_thru_input();
1485   int get();
1486   int peek();
1487 };
1488
1489 class copy_file_thru_input : public copy_thru_input {
1490   input *in;
1491 public:
1492   copy_file_thru_input(input *, const char *b, const char *u);
1493   ~copy_file_thru_input();
1494   int inget();
1495 };
1496
1497 copy_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
1503 copy_file_thru_input::~copy_file_thru_input()
1504 {
1505   delete in;
1506 }
1507
1508 int copy_file_thru_input::inget()
1509 {
1510   if (!in)
1511     return EOF;
1512   else
1513     return in->get();
1514 }
1515
1516 class copy_rest_thru_input : public copy_thru_input {
1517 public:
1518   copy_rest_thru_input(const char *, const char *u);
1519   int inget();
1520 };
1521
1522 copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1523 : copy_thru_input(b, u)
1524 {
1525 }
1526
1527 int 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
1543 copy_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
1553 copy_thru_input::~copy_thru_input()
1554 {
1555   a_delete body;
1556   a_delete until;
1557 }
1558
1559 int 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     }
1576     while ((unsigned char)*p >= ARG1
1577            && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1578       int i = (unsigned char)*p++ - ARG1;
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
1590 int 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';
1605     while ((unsigned char)*p >= ARG1
1606            && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1607       int i = (unsigned char)*p++ - ARG1;
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
1619 int 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;
1631     if (argc == MAX_ARG) {
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
1651 class simple_file_input : public input {
1652   const char *filename;
1653   int lineno;
1654   FILE *fp;
1655 public:
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
1663 simple_file_input::simple_file_input(FILE *p, const char *s)
1664 : filename(s), lineno(1), fp(p)
1665 {
1666 }
1667
1668 simple_file_input::~simple_file_input()
1669 {
1670   // don't delete the filename
1671   fclose(fp);
1672 }
1673
1674 int 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
1686 int 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
1698 int simple_file_input::get_location(const char **fnp, int *lnp)
1699 {
1700   *fnp = filename;
1701   *lnp = lineno;
1702   return 1;
1703 }
1704
1705
1706 void 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
1719 void copy_rest_thru(const char *body, const char *until)
1720 {
1721   input_stack::push(new copy_rest_thru_input(body, until));
1722 }
1723
1724 void push_body(const char *s)
1725 {
1726   input_stack::push(new char_input('\n'));
1727   input_stack::push(new macro_input(s));
1728 }
1729
1730 int delim_flag = 0;
1731
1732 char *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
1768 int lookahead_token = -1;
1769 string old_context_buffer;
1770
1771 void do_lookahead()
1772 {
1773   if (lookahead_token == -1) {
1774     old_context_buffer = context_buffer;
1775     lookahead_token = get_token(1);
1776   }
1777 }
1778
1779 int 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
1971 void 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
1984 void 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
1997 void 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