PageRenderTime 145ms CodeModel.GetById 12ms app.highlight 121ms RepoModel.GetById 2ms app.codeStats 0ms

/contrib/groff/src/preproc/pic/lex.cpp

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