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