lex.cpp revision 114402
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 to;
1333  int by_is_multiplicative;
1334  double by;
1335  const char *p;
1336  int done_newline;
1337public:
1338  for_input(char *, double, int, double, char *);
1339  ~for_input();
1340  int get();
1341  int peek();
1342};
1343
1344for_input::for_input(char *vr, double t, int bim, double b, char *bd)
1345: var(vr), body(bd), to(t), by_is_multiplicative(bim), by(b), p(body),
1346  done_newline(0)
1347{
1348}
1349
1350for_input::~for_input()
1351{
1352  a_delete var;
1353  a_delete body;
1354}
1355
1356int for_input::get()
1357{
1358  if (p == 0)
1359    return EOF;
1360  for (;;) {
1361    if (*p != '\0')
1362      return (unsigned char)*p++;
1363    if (!done_newline) {
1364      done_newline = 1;
1365      return '\n';
1366    }
1367    double val;
1368    if (!lookup_variable(var, &val)) {
1369      lex_error("body of `for' terminated enclosing block");
1370      return EOF;
1371    }
1372    if (by_is_multiplicative)
1373      val *= by;
1374    else
1375      val += by;
1376    define_variable(var, val);
1377    if (val > to) {
1378      p = 0;
1379      return EOF;
1380    }
1381    p = body;
1382    done_newline = 0;
1383  }
1384}
1385
1386int for_input::peek()
1387{
1388  if (p == 0)
1389    return EOF;
1390  if (*p != '\0')
1391    return (unsigned char)*p;
1392  if (!done_newline)
1393    return '\n';
1394  double val;
1395  if (!lookup_variable(var, &val))
1396    return EOF;
1397  if (by_is_multiplicative) {
1398    if (val * by > to)
1399      return EOF;
1400  }
1401  else {
1402    if (val + by > to)
1403      return EOF;
1404  }
1405  if (*body == '\0')
1406    return EOF;
1407  return (unsigned char)*body;
1408}
1409
1410void do_for(char *var, double from, double to, int by_is_multiplicative,
1411	    double by, char *body)
1412{
1413  define_variable(var, from);
1414  if (from <= to)
1415    input_stack::push(new for_input(var, to, by_is_multiplicative, by, body));
1416}
1417
1418
1419void do_copy(const char *filename)
1420{
1421  errno = 0;
1422  FILE *fp = fopen(filename, "r");
1423  if (fp == 0) {
1424    lex_error("can't open `%1': %2", filename, strerror(errno));
1425    return;
1426  }
1427  input_stack::push(new file_input(fp, filename));
1428}
1429
1430class copy_thru_input : public input {
1431  int done;
1432  char *body;
1433  char *until;
1434  const char *p;
1435  const char *ap;
1436  int argv[9];
1437  int argc;
1438  string line;
1439  int get_line();
1440  virtual int inget() = 0;
1441public:
1442  copy_thru_input(const char *b, const char *u);
1443  ~copy_thru_input();
1444  int get();
1445  int peek();
1446};
1447
1448class copy_file_thru_input : public copy_thru_input {
1449  input *in;
1450public:
1451  copy_file_thru_input(input *, const char *b, const char *u);
1452  ~copy_file_thru_input();
1453  int inget();
1454};
1455
1456copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1457					   const char *u)
1458: copy_thru_input(b, u), in(i)
1459{
1460}
1461
1462copy_file_thru_input::~copy_file_thru_input()
1463{
1464  delete in;
1465}
1466
1467int copy_file_thru_input::inget()
1468{
1469  if (!in)
1470    return EOF;
1471  else
1472    return in->get();
1473}
1474
1475class copy_rest_thru_input : public copy_thru_input {
1476public:
1477  copy_rest_thru_input(const char *, const char *u);
1478  int inget();
1479};
1480
1481copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1482: copy_thru_input(b, u)
1483{
1484}
1485
1486int copy_rest_thru_input::inget()
1487{
1488  while (next != 0) {
1489    int c = next->get();
1490    if (c != EOF)
1491      return c;
1492    if (next->next == 0)
1493      return EOF;
1494    input *tem = next;
1495    next = next->next;
1496    delete tem;
1497  }
1498  return EOF;
1499
1500}
1501
1502copy_thru_input::copy_thru_input(const char *b, const char *u)
1503: done(0)
1504{
1505  ap = 0;
1506  body = process_body(b);
1507  p = 0;
1508  until = strsave(u);
1509}
1510
1511
1512copy_thru_input::~copy_thru_input()
1513{
1514  a_delete body;
1515  a_delete until;
1516}
1517
1518int copy_thru_input::get()
1519{
1520  if (ap) {
1521    if (*ap != '\0')
1522      return (unsigned char)*ap++;
1523    ap = 0;
1524  }
1525  for (;;) {
1526    if (p == 0) {
1527      if (!get_line())
1528	break;
1529      p = body;
1530    }
1531    if (*p == '\0') {
1532      p = 0;
1533      return '\n';
1534    }
1535    while (*p >= ARG1 && *p <= ARG1 + 8) {
1536      int i = *p++ - ARG1;
1537      if (i < argc && line[argv[i]] != '\0') {
1538	ap = line.contents() + argv[i];
1539	return (unsigned char)*ap++;
1540      }
1541    }
1542    if (*p != '\0')
1543      return (unsigned char)*p++;
1544  }
1545  return EOF;
1546}
1547
1548int copy_thru_input::peek()
1549{
1550  if (ap) {
1551    if (*ap != '\0')
1552      return (unsigned char)*ap;
1553    ap = 0;
1554  }
1555  for (;;) {
1556    if (p == 0) {
1557      if (!get_line())
1558	break;
1559      p = body;
1560    }
1561    if (*p == '\0')
1562      return '\n';
1563    while (*p >= ARG1 && *p <= ARG1 + 8) {
1564      int i = *p++ - ARG1;
1565      if (i < argc && line[argv[i]] != '\0') {
1566	ap = line.contents() + argv[i];
1567	return (unsigned char)*ap;
1568      }
1569    }
1570    if (*p != '\0')
1571      return (unsigned char)*p;
1572  }
1573  return EOF;
1574}
1575
1576int copy_thru_input::get_line()
1577{
1578  if (done)
1579    return 0;
1580  line.clear();
1581  argc = 0;
1582  int c = inget();
1583  for (;;) {
1584    while (c == ' ')
1585      c = inget();
1586    if (c == EOF || c == '\n')
1587      break;
1588    if (argc == 9) {
1589      do {
1590	c = inget();
1591      } while (c != '\n' && c != EOF);
1592      break;
1593    }
1594    argv[argc++] = line.length();
1595    do {
1596      line += char(c);
1597      c = inget();
1598    } while (c != ' ' && c != '\n');
1599    line += '\0';
1600  }
1601  if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1602    done = 1;
1603    return 0;
1604  }
1605  return argc > 0 || c == '\n';
1606}
1607
1608class simple_file_input : public input {
1609  const char *filename;
1610  int lineno;
1611  FILE *fp;
1612public:
1613  simple_file_input(FILE *, const char *);
1614  ~simple_file_input();
1615  int get();
1616  int peek();
1617  int get_location(const char **, int *);
1618};
1619
1620simple_file_input::simple_file_input(FILE *p, const char *s)
1621: filename(s), lineno(1), fp(p)
1622{
1623}
1624
1625simple_file_input::~simple_file_input()
1626{
1627  // don't delete the filename
1628  fclose(fp);
1629}
1630
1631int simple_file_input::get()
1632{
1633  int c = getc(fp);
1634  while (invalid_input_char(c)) {
1635    error("invalid input character code %1", c);
1636    c = getc(fp);
1637  }
1638  if (c == '\n')
1639    lineno++;
1640  return c;
1641}
1642
1643int simple_file_input::peek()
1644{
1645  int c = getc(fp);
1646  while (invalid_input_char(c)) {
1647    error("invalid input character code %1", c);
1648    c = getc(fp);
1649  }
1650  if (c != EOF)
1651    ungetc(c, fp);
1652  return c;
1653}
1654
1655int simple_file_input::get_location(const char **fnp, int *lnp)
1656{
1657  *fnp = filename;
1658  *lnp = lineno;
1659  return 1;
1660}
1661
1662
1663void copy_file_thru(const char *filename, const char *body, const char *until)
1664{
1665  errno = 0;
1666  FILE *fp = fopen(filename, "r");
1667  if (fp == 0) {
1668    lex_error("can't open `%1': %2", filename, strerror(errno));
1669    return;
1670  }
1671  input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1672				       body, until);
1673  input_stack::push(in);
1674}
1675
1676void copy_rest_thru(const char *body, const char *until)
1677{
1678  input_stack::push(new copy_rest_thru_input(body, until));
1679}
1680
1681void push_body(const char *s)
1682{
1683  input_stack::push(new char_input('\n'));
1684  input_stack::push(new macro_input(s));
1685}
1686
1687int delim_flag = 0;
1688
1689char *get_thru_arg()
1690{
1691  int c = input_stack::peek_char();
1692  while (c == ' ') {
1693    input_stack::get_char();
1694    c = input_stack::peek_char();
1695  }
1696  if (c != EOF && csalpha(c)) {
1697    // looks like a macro
1698    input_stack::get_char();
1699    token_buffer = c;
1700    for (;;) {
1701      c = input_stack::peek_char();
1702      if (c == EOF || (!csalnum(c) && c != '_'))
1703	break;
1704      input_stack::get_char();
1705      token_buffer += char(c);
1706    }
1707    context_buffer = token_buffer;
1708    token_buffer += '\0';
1709    char *def = macro_table.lookup(token_buffer.contents());
1710    if (def)
1711      return strsave(def);
1712    // I guess it wasn't a macro after all; so push the macro name back.
1713    // -2 because we added a '\0'
1714    for (int i = token_buffer.length() - 2; i >= 0; i--)
1715      input_stack::push_back(token_buffer[i]);
1716  }
1717  if (get_delimited()) {
1718    token_buffer += '\0';
1719    return strsave(token_buffer.contents());
1720  }
1721  else
1722    return 0;
1723}
1724
1725int lookahead_token = -1;
1726string old_context_buffer;
1727
1728void do_lookahead()
1729{
1730  if (lookahead_token == -1) {
1731    old_context_buffer = context_buffer;
1732    lookahead_token = get_token(1);
1733  }
1734}
1735
1736int yylex()
1737{
1738  if (delim_flag) {
1739    assert(lookahead_token == -1);
1740    if (delim_flag == 2) {
1741      if ((yylval.str = get_thru_arg()) != 0)
1742	return DELIMITED;
1743      else
1744	return 0;
1745    }
1746    else {
1747      if (get_delimited()) {
1748	token_buffer += '\0';
1749	yylval.str = strsave(token_buffer.contents());
1750	return DELIMITED;
1751      }
1752      else
1753	return 0;
1754    }
1755  }
1756  for (;;) {
1757    int t;
1758    if (lookahead_token >= 0) {
1759      t = lookahead_token;
1760      lookahead_token = -1;
1761    }
1762    else
1763      t = get_token(1);
1764    switch (t) {
1765    case '\n':
1766      return ';';
1767    case EOF:
1768      return 0;
1769    case DEFINE:
1770      do_define();
1771      break;
1772    case UNDEF:
1773      do_undef();
1774      break;
1775    case ORDINAL:
1776      yylval.n = token_int;
1777      return t;
1778    case NUMBER:
1779      yylval.x = token_double;
1780      return t;
1781    case COMMAND_LINE:
1782    case TEXT:
1783      token_buffer += '\0';
1784      if (!input_stack::get_location(&yylval.lstr.filename,
1785				     &yylval.lstr.lineno)) {
1786	yylval.lstr.filename = 0;
1787	yylval.lstr.lineno = -1;
1788      }
1789      yylval.lstr.str = strsave(token_buffer.contents());
1790      return t;
1791    case LABEL:
1792    case VARIABLE:
1793      token_buffer += '\0';
1794      yylval.str = strsave(token_buffer.contents());
1795      return t;
1796    case LEFT:
1797      // change LEFT to LEFT_CORNER when followed by OF
1798      old_context_buffer = context_buffer;
1799      lookahead_token = get_token(1);
1800      if (lookahead_token == OF)
1801	return LEFT_CORNER;
1802      else
1803	return t;
1804    case RIGHT:
1805      // change RIGHT to RIGHT_CORNER when followed by OF
1806      old_context_buffer = context_buffer;
1807      lookahead_token = get_token(1);
1808      if (lookahead_token == OF)
1809	return RIGHT_CORNER;
1810      else
1811	return t;
1812    case UPPER:
1813      // recognise UPPER only before LEFT or RIGHT
1814      old_context_buffer = context_buffer;
1815      lookahead_token = get_token(1);
1816      if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1817	yylval.str = strsave("upper");
1818	return VARIABLE;
1819      }
1820      else
1821	return t;
1822    case LOWER:
1823      // recognise LOWER only before LEFT or RIGHT
1824      old_context_buffer = context_buffer;
1825      lookahead_token = get_token(1);
1826      if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1827	yylval.str = strsave("lower");
1828	return VARIABLE;
1829      }
1830      else
1831	return t;
1832    case NORTH:
1833      // recognise NORTH only before OF
1834      old_context_buffer = context_buffer;
1835      lookahead_token = get_token(1);
1836      if (lookahead_token != OF) {
1837	yylval.str = strsave("north");
1838	return VARIABLE;
1839      }
1840      else
1841	return t;
1842    case SOUTH:
1843      // recognise SOUTH only before OF
1844      old_context_buffer = context_buffer;
1845      lookahead_token = get_token(1);
1846      if (lookahead_token != OF) {
1847	yylval.str = strsave("south");
1848	return VARIABLE;
1849      }
1850      else
1851	return t;
1852    case EAST:
1853      // recognise EAST only before OF
1854      old_context_buffer = context_buffer;
1855      lookahead_token = get_token(1);
1856      if (lookahead_token != OF) {
1857	yylval.str = strsave("east");
1858	return VARIABLE;
1859      }
1860      else
1861	return t;
1862    case WEST:
1863      // recognise WEST only before OF
1864      old_context_buffer = context_buffer;
1865      lookahead_token = get_token(1);
1866      if (lookahead_token != OF) {
1867	yylval.str = strsave("west");
1868	return VARIABLE;
1869      }
1870      else
1871	return t;
1872    case TOP:
1873      // recognise TOP only before OF
1874      old_context_buffer = context_buffer;
1875      lookahead_token = get_token(1);
1876      if (lookahead_token != OF) {
1877	yylval.str = strsave("top");
1878	return VARIABLE;
1879      }
1880      else
1881	return t;
1882    case BOTTOM:
1883      // recognise BOTTOM only before OF
1884      old_context_buffer = context_buffer;
1885      lookahead_token = get_token(1);
1886      if (lookahead_token != OF) {
1887	yylval.str = strsave("bottom");
1888	return VARIABLE;
1889      }
1890      else
1891	return t;
1892    case CENTER:
1893      // recognise CENTER only before OF
1894      old_context_buffer = context_buffer;
1895      lookahead_token = get_token(1);
1896      if (lookahead_token != OF) {
1897	yylval.str = strsave("center");
1898	return VARIABLE;
1899      }
1900      else
1901	return t;
1902    case START:
1903      // recognise START only before OF
1904      old_context_buffer = context_buffer;
1905      lookahead_token = get_token(1);
1906      if (lookahead_token != OF) {
1907	yylval.str = strsave("start");
1908	return VARIABLE;
1909      }
1910      else
1911	return t;
1912    case END:
1913      // recognise END only before OF
1914      old_context_buffer = context_buffer;
1915      lookahead_token = get_token(1);
1916      if (lookahead_token != OF) {
1917	yylval.str = strsave("end");
1918	return VARIABLE;
1919      }
1920      else
1921	return t;
1922    default:
1923      return t;
1924    }
1925  }
1926}
1927
1928void lex_error(const char *message,
1929	       const errarg &arg1,
1930	       const errarg &arg2,
1931	       const errarg &arg3)
1932{
1933  const char *filename;
1934  int lineno;
1935  if (!input_stack::get_location(&filename, &lineno))
1936    error(message, arg1, arg2, arg3);
1937  else
1938    error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1939}
1940
1941void lex_warning(const char *message,
1942		 const errarg &arg1,
1943		 const errarg &arg2,
1944		 const errarg &arg3)
1945{
1946  const char *filename;
1947  int lineno;
1948  if (!input_stack::get_location(&filename, &lineno))
1949    warning(message, arg1, arg2, arg3);
1950  else
1951    warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1952}
1953
1954void yyerror(const char *s)
1955{
1956  const char *filename;
1957  int lineno;
1958  const char *context = 0;
1959  if (lookahead_token == -1) {
1960    if (context_buffer.length() > 0) {
1961      context_buffer += '\0';
1962      context = context_buffer.contents();
1963    }
1964  }
1965  else {
1966    if (old_context_buffer.length() > 0) {
1967      old_context_buffer += '\0';
1968      context = old_context_buffer.contents();
1969    }
1970  }
1971  if (!input_stack::get_location(&filename, &lineno)) {
1972    if (context) {
1973      if (context[0] == '\n' && context[1] == '\0')
1974	error("%1 before newline", s);
1975      else
1976	error("%1 before `%2'", s, context);
1977    }
1978    else
1979      error("%1 at end of picture", s);
1980  }
1981  else {
1982    if (context) {
1983      if (context[0] == '\n' && context[1] == '\0')
1984	error_with_file_and_line(filename, lineno, "%1 before newline", s);
1985      else
1986	error_with_file_and_line(filename, lineno, "%1 before `%2'",
1987				 s, context);
1988    }
1989    else
1990      error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
1991  }
1992}
1993
1994