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