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