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
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
27declare_ptable(char)
28implement_ptable(char)
29
30PTABLE(char) macro_table;
31
32class macro_input : public input {
33  char *s;
34  char *p;
35public:
36  macro_input(const char *);
37  ~macro_input();
38  int get();
39  int peek();
40};
41
42class argument_macro_input : public input {
43  char *s;
44  char *p;
45  char *ap;
46  int argc;
47  char *argv[9];
48public:
49  argument_macro_input(const char *, int, char **);
50  ~argument_macro_input();
51  int get();
52  int peek();
53};
54
55input::input() : next(0)
56{
57}
58
59input::~input()
60{
61}
62
63int input::get_location(const char **, int *)
64{
65  return 0;
66}
67
68file_input::file_input(FILE *f, const char *fn)
69: fp(f), filename(fn), lineno(0), ptr("")
70{
71}
72
73file_input::~file_input()
74{
75  fclose(fp);
76}
77
78int 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
108int file_input::get()
109{
110  if (*ptr != '\0' || read_line())
111    return (unsigned char)*ptr++;
112  else
113    return EOF;
114}
115
116int file_input::peek()
117{
118  if (*ptr != '\0' || read_line())
119    return (unsigned char)*ptr;
120  else
121    return EOF;
122}
123
124int file_input::get_location(const char **fnp, int *lnp)
125{
126  *fnp = filename;
127  *lnp = lineno;
128  return 1;
129}
130
131macro_input::macro_input(const char *str)
132{
133  p = s = strsave(str);
134}
135
136macro_input::~macro_input()
137{
138  a_delete s;
139}
140
141int macro_input::get()
142{
143  if (p == 0 || *p == '\0')
144    return EOF;
145  else
146    return (unsigned char)*p++;
147}
148
149int 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
160char *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
176argument_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
185argument_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
192int 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
213int 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
234class input_stack {
235  static input *current_input;
236  static int bol_flag;
237public:
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
247input *input_stack::current_input = 0;
248int input_stack::bol_flag = 0;
249
250inline int input_stack::bol()
251{
252  return bol_flag;
253}
254
255void 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
265void input_stack::push(input *in)
266{
267  in->next = current_input;
268  current_input = in;
269}
270
271void lex_init(input *top)
272{
273  input_stack::clear();
274  input_stack::push(top);
275}
276
277void lex_cleanup()
278{
279  while (input_stack::get_char() != EOF)
280    ;
281}
282
283int 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
301int 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
316class char_input : public input {
317  int c;
318public:
319  char_input(int);
320  int get();
321  int peek();
322};
323
324char_input::char_input(int n) : c((unsigned char)n)
325{
326}
327
328int char_input::get()
329{
330  int n = c;
331  c = EOF;
332  return n;
333}
334
335int char_input::peek()
336{
337  return c;
338}
339
340void input_stack::push_back(unsigned char c, int was_bol)
341{
342  push(new char_input(c));
343  bol_flag = was_bol;
344}
345
346int 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
354string context_buffer;
355
356string token_buffer;
357double token_double;
358int token_int;
359
360void 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
413static 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
427int 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
548int 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
831int 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
1230int 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
1301void 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
1317void 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
1329class 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;
1338public:
1339  for_input(char *, double, double, int, double, char *);
1340  ~for_input();
1341  int get();
1342  int peek();
1343};
1344
1345for_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
1352for_input::~for_input()
1353{
1354  a_delete var;
1355  a_delete body;
1356}
1357
1358int 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
1389int 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
1414void 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
1427void 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
1438class 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;
1449public:
1450  copy_thru_input(const char *b, const char *u);
1451  ~copy_thru_input();
1452  int get();
1453  int peek();
1454};
1455
1456class copy_file_thru_input : public copy_thru_input {
1457  input *in;
1458public:
1459  copy_file_thru_input(input *, const char *b, const char *u);
1460  ~copy_file_thru_input();
1461  int inget();
1462};
1463
1464copy_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
1470copy_file_thru_input::~copy_file_thru_input()
1471{
1472  delete in;
1473}
1474
1475int copy_file_thru_input::inget()
1476{
1477  if (!in)
1478    return EOF;
1479  else
1480    return in->get();
1481}
1482
1483class copy_rest_thru_input : public copy_thru_input {
1484public:
1485  copy_rest_thru_input(const char *, const char *u);
1486  int inget();
1487};
1488
1489copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1490: copy_thru_input(b, u)
1491{
1492}
1493
1494int 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
1510copy_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
1520copy_thru_input::~copy_thru_input()
1521{
1522  a_delete body;
1523  a_delete until;
1524}
1525
1526int 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
1556int 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
1584int 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
1616class simple_file_input : public input {
1617  const char *filename;
1618  int lineno;
1619  FILE *fp;
1620public:
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
1628simple_file_input::simple_file_input(FILE *p, const char *s)
1629: filename(s), lineno(1), fp(p)
1630{
1631}
1632
1633simple_file_input::~simple_file_input()
1634{
1635  // don't delete the filename
1636  fclose(fp);
1637}
1638
1639int 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
1651int 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
1663int simple_file_input::get_location(const char **fnp, int *lnp)
1664{
1665  *fnp = filename;
1666  *lnp = lineno;
1667  return 1;
1668}
1669
1670
1671void 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
1684void copy_rest_thru(const char *body, const char *until)
1685{
1686  input_stack::push(new copy_rest_thru_input(body, until));
1687}
1688
1689void push_body(const char *s)
1690{
1691  input_stack::push(new char_input('\n'));
1692  input_stack::push(new macro_input(s));
1693}
1694
1695int delim_flag = 0;
1696
1697char *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
1733int lookahead_token = -1;
1734string old_context_buffer;
1735
1736void do_lookahead()
1737{
1738  if (lookahead_token == -1) {
1739    old_context_buffer = context_buffer;
1740    lookahead_token = get_token(1);
1741  }
1742}
1743
1744int 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
1936void 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
1949void 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
1962void 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