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