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