Home | History | Annotate | Line # | Download | only in eqn
      1 /*	$NetBSD: lex.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $	*/
      2 
      3 // -*- C++ -*-
      4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
      5    Free Software Foundation, Inc.
      6      Written by James Clark (jjc (at) jclark.com)
      7 
      8 This file is part of groff.
      9 
     10 groff is free software; you can redistribute it and/or modify it under
     11 the terms of the GNU General Public License as published by the Free
     12 Software Foundation; either version 2, or (at your option) any later
     13 version.
     14 
     15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
     16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     18 for more details.
     19 
     20 You should have received a copy of the GNU General Public License along
     21 with groff; see the file COPYING.  If not, write to the Free Software
     22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
     23 
     24 #include "eqn.h"
     25 #include "eqn_tab.h"
     26 #include "stringclass.h"
     27 #include "ptable.h"
     28 
     29 
     30 // declarations to avoid friend name injection problems
     31 int get_char();
     32 int peek_char();
     33 int get_location(char **, int *);
     34 
     35 struct definition {
     36   char is_macro;
     37   char is_simple;
     38   union {
     39     int tok;
     40     char *contents;
     41   };
     42   definition();
     43   ~definition();
     44 };
     45 
     46 definition::definition() : is_macro(1), is_simple(0)
     47 {
     48   contents = 0;
     49 }
     50 
     51 definition::~definition()
     52 {
     53   if (is_macro)
     54     a_delete contents;
     55 }
     56 
     57 declare_ptable(definition)
     58 implement_ptable(definition)
     59 
     60 PTABLE(definition) macro_table;
     61 
     62 static struct {
     63   const char *name;
     64   int token;
     65 } token_table[] = {
     66   { "over", OVER },
     67   { "smallover", SMALLOVER },
     68   { "sqrt", SQRT },
     69   { "sub", SUB },
     70   { "sup", SUP },
     71   { "lpile", LPILE },
     72   { "rpile", RPILE },
     73   { "cpile", CPILE },
     74   { "pile", PILE },
     75   { "left", LEFT },
     76   { "right", RIGHT },
     77   { "to", TO },
     78   { "from", FROM },
     79   { "size", SIZE },
     80   { "font", FONT },
     81   { "roman", ROMAN },
     82   { "bold", BOLD },
     83   { "italic", ITALIC },
     84   { "fat", FAT },
     85   { "bar", BAR },
     86   { "under", UNDER },
     87   { "accent", ACCENT },
     88   { "uaccent", UACCENT },
     89   { "above", ABOVE },
     90   { "fwd", FWD },
     91   { "back", BACK },
     92   { "down", DOWN },
     93   { "up", UP },
     94   { "matrix", MATRIX },
     95   { "col", COL },
     96   { "lcol", LCOL },
     97   { "rcol", RCOL },
     98   { "ccol", CCOL },
     99   { "mark", MARK },
    100   { "lineup", LINEUP },
    101   { "space", SPACE },
    102   { "gfont", GFONT },
    103   { "gsize", GSIZE },
    104   { "define", DEFINE },
    105   { "sdefine", SDEFINE },
    106   { "ndefine", NDEFINE },
    107   { "tdefine", TDEFINE },
    108   { "undef", UNDEF },
    109   { "ifdef", IFDEF },
    110   { "include", INCLUDE },
    111   { "copy", INCLUDE },
    112   { "delim", DELIM },
    113   { "chartype", CHARTYPE },
    114   { "type", TYPE },
    115   { "vcenter", VCENTER },
    116   { "set", SET },
    117   { "opprime", PRIME },
    118   { "grfont", GRFONT },
    119   { "gbfont", GBFONT },
    120   { "split", SPLIT },
    121   { "nosplit", NOSPLIT },
    122   { "special", SPECIAL },
    123 };
    124 
    125 static struct {
    126   const char *name;
    127   const char *def;
    128 } def_table[] = {
    129   { "ALPHA", "\\(*A" },
    130   { "BETA", "\\(*B" },
    131   { "CHI", "\\(*X" },
    132   { "DELTA", "\\(*D" },
    133   { "EPSILON", "\\(*E" },
    134   { "ETA", "\\(*Y" },
    135   { "GAMMA", "\\(*G" },
    136   { "IOTA", "\\(*I" },
    137   { "KAPPA", "\\(*K" },
    138   { "LAMBDA", "\\(*L" },
    139   { "MU", "\\(*M" },
    140   { "NU", "\\(*N" },
    141   { "OMEGA", "\\(*W" },
    142   { "OMICRON", "\\(*O" },
    143   { "PHI", "\\(*F" },
    144   { "PI", "\\(*P" },
    145   { "PSI", "\\(*Q" },
    146   { "RHO", "\\(*R" },
    147   { "SIGMA", "\\(*S" },
    148   { "TAU", "\\(*T" },
    149   { "THETA", "\\(*H" },
    150   { "UPSILON", "\\(*U" },
    151   { "XI", "\\(*C" },
    152   { "ZETA", "\\(*Z" },
    153   { "Alpha", "\\(*A" },
    154   { "Beta", "\\(*B" },
    155   { "Chi", "\\(*X" },
    156   { "Delta", "\\(*D" },
    157   { "Epsilon", "\\(*E" },
    158   { "Eta", "\\(*Y" },
    159   { "Gamma", "\\(*G" },
    160   { "Iota", "\\(*I" },
    161   { "Kappa", "\\(*K" },
    162   { "Lambda", "\\(*L" },
    163   { "Mu", "\\(*M" },
    164   { "Nu", "\\(*N" },
    165   { "Omega", "\\(*W" },
    166   { "Omicron", "\\(*O" },
    167   { "Phi", "\\(*F" },
    168   { "Pi", "\\(*P" },
    169   { "Psi", "\\(*Q" },
    170   { "Rho", "\\(*R" },
    171   { "Sigma", "\\(*S" },
    172   { "Tau", "\\(*T" },
    173   { "Theta", "\\(*H" },
    174   { "Upsilon", "\\(*U" },
    175   { "Xi", "\\(*C" },
    176   { "Zeta", "\\(*Z" },
    177   { "alpha", "\\(*a" },
    178   { "beta", "\\(*b" },
    179   { "chi", "\\(*x" },
    180   { "delta", "\\(*d" },
    181   { "epsilon", "\\(*e" },
    182   { "eta", "\\(*y" },
    183   { "gamma", "\\(*g" },
    184   { "iota", "\\(*i" },
    185   { "kappa", "\\(*k" },
    186   { "lambda", "\\(*l" },
    187   { "mu", "\\(*m" },
    188   { "nu", "\\(*n" },
    189   { "omega", "\\(*w" },
    190   { "omicron", "\\(*o" },
    191   { "phi", "\\(*f" },
    192   { "pi", "\\(*p" },
    193   { "psi", "\\(*q" },
    194   { "rho", "\\(*r" },
    195   { "sigma", "\\(*s" },
    196   { "tau", "\\(*t" },
    197   { "theta", "\\(*h" },
    198   { "upsilon", "\\(*u" },
    199   { "xi", "\\(*c" },
    200   { "zeta", "\\(*z" },
    201   { "max", "{type \"operator\" roman \"max\"}" },
    202   { "min", "{type \"operator\" roman \"min\"}" },
    203   { "lim", "{type \"operator\" roman \"lim\"}" },
    204   { "sin", "{type \"operator\" roman \"sin\"}" },
    205   { "cos", "{type \"operator\" roman \"cos\"}" },
    206   { "tan", "{type \"operator\" roman \"tan\"}" },
    207   { "sinh", "{type \"operator\" roman \"sinh\"}" },
    208   { "cosh", "{type \"operator\" roman \"cosh\"}" },
    209   { "tanh", "{type \"operator\" roman \"tanh\"}" },
    210   { "arc", "{type \"operator\" roman \"arc\"}" },
    211   { "log", "{type \"operator\" roman \"log\"}" },
    212   { "ln", "{type \"operator\" roman \"ln\"}" },
    213   { "exp", "{type \"operator\" roman \"exp\"}" },
    214   { "Re", "{type \"operator\" roman \"Re\"}" },
    215   { "Im", "{type \"operator\" roman \"Im\"}" },
    216   { "det", "{type \"operator\" roman \"det\"}" },
    217   { "and", "{roman \"and\"}" },
    218   { "if", "{roman \"if\"}" },
    219   { "for", "{roman \"for\"}" },
    220   { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
    221   { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
    222   { "int", "{type \"operator\" vcenter size +8 \\(is}" },
    223   { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
    224   { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
    225   { "times", "type \"binary\" \\(mu" },
    226   { "ldots", "type \"inner\" { . . . }" },
    227   { "inf", "\\(if" },
    228   { "partial", "\\(pd" },
    229   { "nothing", "\"\"" },
    230   { "half", "{1 smallover 2}" },
    231   { "hat_def", "roman \"^\"" },
    232   { "hat", "accent { hat_def }" },
    233   { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" },
    234   { "dot", "accent { dot_def }" },
    235   { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" },
    236   { "dotdot", "accent { dotdot_def }" },
    237   { "tilde_def", "\"~\"" },
    238   { "tilde", "accent { tilde_def }" },
    239   { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" },
    240   { "utilde", "uaccent { utilde_def }" },
    241   { "vec_def", "up 52 size -5 \\(->" },
    242   { "vec", "accent { vec_def }" },
    243   { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" },
    244   { "dyad", "accent { dyad_def }" },
    245   { "==", "type \"relation\" \\(==" },
    246   { "!=", "type \"relation\" \\(!=" },
    247   { "+-", "type \"binary\" \\(+-" },
    248   { "->", "type \"relation\" \\(->" },
    249   { "<-", "type \"relation\" \\(<-" },
    250   { "<<", "{ < back 20 < }" },
    251   { ">>", "{ > back 20 > }" },
    252   { "...", "type \"inner\" vcenter { . . . }" },
    253   { "prime", "'" },
    254   { "approx", "type \"relation\" \"\\(~=\"" },
    255   { "grad", "\\(gr" },
    256   { "del", "\\(gr" },
    257   { "cdot", "type \"binary\" vcenter ." },
    258   { "dollar", "$" },
    259 };
    260 
    261 void init_table(const char *device)
    262 {
    263   unsigned int i;
    264   for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
    265     definition *def = new definition[1];
    266     def->is_macro = 0;
    267     def->tok = token_table[i].token;
    268     macro_table.define(token_table[i].name, def);
    269   }
    270   for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) {
    271     definition *def = new definition[1];
    272     def->is_macro = 1;
    273     def->contents = strsave(def_table[i].def);
    274     def->is_simple = 1;
    275     macro_table.define(def_table[i].name, def);
    276   }
    277   definition *def = new definition[1];
    278   def->is_macro = 1;
    279   def->contents = strsave("1");
    280   macro_table.define(device, def);
    281 }
    282 
    283 class input {
    284   input *next;
    285 public:
    286   input(input *p);
    287   virtual ~input();
    288   virtual int get() = 0;
    289   virtual int peek() = 0;
    290   virtual int get_location(char **, int *);
    291 
    292   friend int get_char();
    293   friend int peek_char();
    294   friend int get_location(char **, int *);
    295   friend void init_lex(const char *str, const char *filename, int lineno);
    296 };
    297 
    298 class file_input : public input {
    299   FILE *fp;
    300   char *filename;
    301   int lineno;
    302   string line;
    303   const char *ptr;
    304   int read_line();
    305 public:
    306   file_input(FILE *, const char *, input *);
    307   ~file_input();
    308   int get();
    309   int peek();
    310   int get_location(char **, int *);
    311 };
    312 
    313 
    314 class macro_input : public input {
    315   char *s;
    316   char *p;
    317 public:
    318   macro_input(const char *, input *);
    319   ~macro_input();
    320   int get();
    321   int peek();
    322 };
    323 
    324 class top_input : public macro_input {
    325   char *filename;
    326   int lineno;
    327  public:
    328   top_input(const char *, const char *, int, input *);
    329   ~top_input();
    330   int get();
    331   int get_location(char **, int *);
    332 };
    333 
    334 class argument_macro_input: public input {
    335   char *s;
    336   char *p;
    337   char *ap;
    338   int argc;
    339   char *argv[9];
    340 public:
    341   argument_macro_input(const char *, int, char **, input *);
    342   ~argument_macro_input();
    343   int get();
    344   int peek();
    345 };
    346 
    347 input::input(input *x) : next(x)
    348 {
    349 }
    350 
    351 input::~input()
    352 {
    353 }
    354 
    355 int input::get_location(char **, int *)
    356 {
    357   return 0;
    358 }
    359 
    360 file_input::file_input(FILE *f, const char *fn, input *p)
    361 : input(p), lineno(0), ptr("")
    362 {
    363   fp = f;
    364   filename = strsave(fn);
    365 }
    366 
    367 file_input::~file_input()
    368 {
    369   a_delete filename;
    370   fclose(fp);
    371 }
    372 
    373 int file_input::read_line()
    374 {
    375   for (;;) {
    376     line.clear();
    377     lineno++;
    378     for (;;) {
    379       int c = getc(fp);
    380       if (c == EOF)
    381 	break;
    382       else if (invalid_input_char(c))
    383 	lex_error("invalid input character code %1", c);
    384       else {
    385 	line += char(c);
    386 	if (c == '\n')
    387 	  break;
    388       }
    389     }
    390     if (line.length() == 0)
    391       return 0;
    392     if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
    393 	  && (line[2] == 'Q' || line[2] == 'N')
    394 	  && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
    395 	      || compatible_flag))) {
    396       line += '\0';
    397       ptr = line.contents();
    398       return 1;
    399     }
    400   }
    401 }
    402 
    403 int file_input::get()
    404 {
    405   if (*ptr != '\0' || read_line())
    406     return *ptr++ & 0377;
    407   else
    408     return EOF;
    409 }
    410 
    411 int file_input::peek()
    412 {
    413   if (*ptr != '\0' || read_line())
    414     return *ptr;
    415   else
    416     return EOF;
    417 }
    418 
    419 int file_input::get_location(char **fnp, int *lnp)
    420 {
    421   *fnp = filename;
    422   *lnp = lineno;
    423   return 1;
    424 }
    425 
    426 macro_input::macro_input(const char *str, input *x) : input(x)
    427 {
    428   p = s = strsave(str);
    429 }
    430 
    431 macro_input::~macro_input()
    432 {
    433   a_delete s;
    434 }
    435 
    436 int macro_input::get()
    437 {
    438   if (p == 0 || *p == '\0')
    439     return EOF;
    440   else
    441     return *p++ & 0377;
    442 }
    443 
    444 int macro_input::peek()
    445 {
    446   if (p == 0 || *p == '\0')
    447     return EOF;
    448   else
    449     return *p & 0377;
    450 }
    451 
    452 top_input::top_input(const char *str, const char *fn, int ln, input *x)
    453 : macro_input(str, x), lineno(ln)
    454 {
    455   filename = strsave(fn);
    456 }
    457 
    458 top_input::~top_input()
    459 {
    460   a_delete filename;
    461 }
    462 
    463 int top_input::get()
    464 {
    465   int c = macro_input::get();
    466   if (c == '\n')
    467     lineno++;
    468   return c;
    469 }
    470 
    471 int top_input::get_location(char **fnp, int *lnp)
    472 {
    473   *fnp = filename;
    474   *lnp = lineno;
    475   return 1;
    476 }
    477 
    478 // Character representing $1.  Must be invalid input character.
    479 #define ARG1 14
    480 
    481 argument_macro_input::argument_macro_input(const char *body, int ac,
    482 					   char **av, input *x)
    483 : input(x), ap(0), argc(ac)
    484 {
    485   int i;
    486   for (i = 0; i < argc; i++)
    487     argv[i] = av[i];
    488   p = s = strsave(body);
    489   int j = 0;
    490   for (i = 0; s[i] != '\0'; i++)
    491     if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
    492       if (s[i+1] != '0')
    493 	s[j++] = ARG1 + s[++i] - '1';
    494     }
    495     else
    496       s[j++] = s[i];
    497   s[j] = '\0';
    498 }
    499 
    500 
    501 argument_macro_input::~argument_macro_input()
    502 {
    503   for (int i = 0; i < argc; i++)
    504     a_delete argv[i];
    505   a_delete s;
    506 }
    507 
    508 int argument_macro_input::get()
    509 {
    510   if (ap) {
    511     if (*ap != '\0')
    512       return *ap++ & 0377;
    513     ap = 0;
    514   }
    515   if (p == 0)
    516     return EOF;
    517   while (*p >= ARG1 && *p <= ARG1 + 8) {
    518     int i = *p++ - ARG1;
    519     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
    520       ap = argv[i];
    521       return *ap++ & 0377;
    522     }
    523   }
    524   if (*p == '\0')
    525     return EOF;
    526   return *p++ & 0377;
    527 }
    528 
    529 int argument_macro_input::peek()
    530 {
    531   if (ap) {
    532     if (*ap != '\0')
    533       return *ap & 0377;
    534     ap = 0;
    535   }
    536   if (p == 0)
    537     return EOF;
    538   while (*p >= ARG1 && *p <= ARG1 + 8) {
    539     int i = *p++ - ARG1;
    540     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
    541       ap = argv[i];
    542       return *ap & 0377;
    543     }
    544   }
    545   if (*p == '\0')
    546     return EOF;
    547   return *p & 0377;
    548 }
    549 
    550 static input *current_input = 0;
    551 
    552 /* we insert a newline between input from different levels */
    553 
    554 int get_char()
    555 {
    556   if (current_input == 0)
    557     return EOF;
    558   else {
    559     int c = current_input->get();
    560     if (c != EOF)
    561       return c;
    562     else {
    563       input *tem = current_input;
    564       current_input = current_input->next;
    565       delete tem;
    566       return '\n';
    567     }
    568   }
    569 }
    570 
    571 int peek_char()
    572 {
    573   if (current_input == 0)
    574     return EOF;
    575   else {
    576     int c = current_input->peek();
    577     if (c != EOF)
    578       return c;
    579     else
    580       return '\n';
    581   }
    582 }
    583 
    584 int get_location(char **fnp, int *lnp)
    585 {
    586   for (input *p = current_input; p; p = p->next)
    587     if (p->get_location(fnp, lnp))
    588       return 1;
    589   return 0;
    590 }
    591 
    592 string token_buffer;
    593 const int NCONTEXT = 4;
    594 string context_ring[NCONTEXT];
    595 int context_index = 0;
    596 
    597 void flush_context()
    598 {
    599   for (int i = 0; i < NCONTEXT; i++)
    600     context_ring[i] = "";
    601   context_index = 0;
    602 }
    603 
    604 void show_context()
    605 {
    606   int i = context_index;
    607   fputs(" context is\n\t", stderr);
    608   for (;;) {
    609     int j = (i + 1) % NCONTEXT;
    610     if (j == context_index) {
    611       fputs(">>> ", stderr);
    612       put_string(context_ring[i], stderr);
    613       fputs(" <<<", stderr);
    614       break;
    615     }
    616     else if (context_ring[i].length() > 0) {
    617       put_string(context_ring[i], stderr);
    618       putc(' ', stderr);
    619     }
    620     i = j;
    621   }
    622   putc('\n', stderr);
    623 }
    624 
    625 void add_context(const string &s)
    626 {
    627   context_ring[context_index] = s;
    628   context_index = (context_index + 1) % NCONTEXT;
    629 }
    630 
    631 void add_context(char c)
    632 {
    633   context_ring[context_index] = c;
    634   context_index = (context_index + 1) % NCONTEXT;
    635 }
    636 
    637 void add_quoted_context(const string &s)
    638 {
    639   string &r = context_ring[context_index];
    640   r = '"';
    641   for (int i = 0; i < s.length(); i++)
    642     if (s[i] == '"')
    643       r += "\\\"";
    644     else
    645       r += s[i];
    646   r += '"';
    647   context_index = (context_index + 1) % NCONTEXT;
    648 }
    649 
    650 void init_lex(const char *str, const char *filename, int lineno)
    651 {
    652  while (current_input != 0) {
    653     input *tem = current_input;
    654     current_input = current_input->next;
    655     delete tem;
    656   }
    657   current_input = new top_input(str, filename, lineno, 0);
    658   flush_context();
    659 }
    660 
    661 
    662 void get_delimited_text()
    663 {
    664   char *filename;
    665   int lineno;
    666   int got_location = get_location(&filename, &lineno);
    667   int start = get_char();
    668   while (start == ' ' || start == '\t' || start == '\n')
    669     start = get_char();
    670   token_buffer.clear();
    671   if (start == EOF) {
    672     if (got_location)
    673       error_with_file_and_line(filename, lineno,
    674 			       "end of input while defining macro");
    675     else
    676       error("end of input while defining macro");
    677     return;
    678   }
    679   for (;;) {
    680     int c = get_char();
    681     if (c == EOF) {
    682       if (got_location)
    683 	error_with_file_and_line(filename, lineno,
    684 				 "end of input while defining macro");
    685       else
    686 	error("end of input while defining macro");
    687       add_context(start + token_buffer);
    688       return;
    689     }
    690     if (c == start)
    691       break;
    692     token_buffer += char(c);
    693   }
    694   add_context(start + token_buffer + start);
    695 }
    696 
    697 void interpolate_macro_with_args(const char *body)
    698 {
    699   char *argv[9];
    700   int argc = 0;
    701   int i;
    702   for (i = 0; i < 9; i++)
    703     argv[i] = 0;
    704   int level = 0;
    705   int c;
    706   do {
    707     token_buffer.clear();
    708     for (;;) {
    709       c = get_char();
    710       if (c == EOF) {
    711 	lex_error("end of input while scanning macro arguments");
    712 	break;
    713       }
    714       if (level == 0 && (c == ',' || c == ')')) {
    715 	if (token_buffer.length() > 0) {
    716 	  token_buffer +=  '\0';
    717 	  argv[argc] = strsave(token_buffer.contents());
    718 	}
    719 	// for `foo()', argc = 0
    720 	if (argc > 0 || c != ')' || i > 0)
    721 	  argc++;
    722 	break;
    723       }
    724       token_buffer += char(c);
    725       if (c == '(')
    726 	level++;
    727       else if (c == ')')
    728 	level--;
    729     }
    730   } while (c != ')' && c != EOF);
    731   current_input = new argument_macro_input(body, argc, argv, current_input);
    732 }
    733 
    734 /* If lookup flag is non-zero the token will be looked up to see
    735 if it is macro. If it's 1, it will looked up to see if it's a token.
    736 */
    737 
    738 int get_token(int lookup_flag = 0)
    739 {
    740   for (;;) {
    741     int c = get_char();
    742     while (c == ' ' || c == '\n')
    743       c = get_char();
    744     switch (c) {
    745     case EOF:
    746       {
    747 	add_context("end of input");
    748       }
    749       return 0;
    750     case '"':
    751       {
    752 	int quoted = 0;
    753 	token_buffer.clear();
    754 	for (;;) {
    755 	  c = get_char();
    756 	  if (c == EOF) {
    757 	    lex_error("missing \"");
    758 	    break;
    759 	  }
    760 	  else if (c == '\n') {
    761 	    lex_error("newline before end of quoted text");
    762 	    break;
    763 	  }
    764 	  else if (c == '"') {
    765 	    if (!quoted)
    766 	      break;
    767 	    token_buffer[token_buffer.length() - 1] = '"';
    768 	    quoted = 0;
    769 	  }
    770 	  else {
    771 	    token_buffer += c;
    772 	    quoted = quoted ? 0 : c == '\\';
    773 	  }
    774 	}
    775       }
    776       add_quoted_context(token_buffer);
    777       return QUOTED_TEXT;
    778     case '{':
    779     case '}':
    780     case '^':
    781     case '~':
    782     case '\t':
    783       add_context(c);
    784       return c;
    785     default:
    786       {
    787 	int break_flag = 0;
    788 	int quoted = 0;
    789 	token_buffer.clear();
    790 	if (c == '\\')
    791 	  quoted = 1;
    792 	else
    793 	  token_buffer += c;
    794 	int done = 0;
    795 	while (!done) {
    796 	  c = peek_char();
    797 	  if (!quoted && lookup_flag != 0 && c == '(') {
    798 	    token_buffer += '\0';
    799 	    definition *def = macro_table.lookup(token_buffer.contents());
    800 	    if (def && def->is_macro && !def->is_simple) {
    801 	      (void)get_char();	// skip initial '('
    802 	      interpolate_macro_with_args(def->contents);
    803 	      break_flag = 1;
    804 	      break;
    805 	    }
    806 	    token_buffer.set_length(token_buffer.length() - 1);
    807 	  }
    808 	  if (quoted) {
    809 	    quoted = 0;
    810 	    switch (c) {
    811 	    case EOF:
    812 	      lex_error("`\\' ignored at end of equation");
    813 	      done = 1;
    814 	      break;
    815 	    case '\n':
    816 	      lex_error("`\\' ignored because followed by newline");
    817 	      done = 1;
    818 	      break;
    819 	    case '\t':
    820 	      lex_error("`\\' ignored because followed by tab");
    821 	      done = 1;
    822 	      break;
    823 	    case '"':
    824 	      (void)get_char();
    825 	      token_buffer += '"';
    826 	      break;
    827 	    default:
    828 	      (void)get_char();
    829 	      token_buffer += '\\';
    830 	      token_buffer += c;
    831 	      break;
    832 	    }
    833 	  }
    834 	  else {
    835 	    switch (c) {
    836 	    case EOF:
    837 	    case '{':
    838 	    case '}':
    839 	    case '^':
    840 	    case '~':
    841 	    case '"':
    842 	    case ' ':
    843 	    case '\t':
    844 	    case '\n':
    845 	      done = 1;
    846 	      break;
    847 	    case '\\':
    848 	      (void)get_char();
    849 	      quoted = 1;
    850 	      break;
    851 	    default:
    852 	      (void)get_char();
    853 	      token_buffer += char(c);
    854 	      break;
    855 	    }
    856 	  }
    857 	}
    858 	if (break_flag || token_buffer.length() == 0)
    859 	  break;
    860 	if (lookup_flag != 0) {
    861 	  token_buffer += '\0';
    862 	  definition *def = macro_table.lookup(token_buffer.contents());
    863 	  token_buffer.set_length(token_buffer.length() - 1);
    864 	  if (def) {
    865 	    if (def->is_macro) {
    866 	      current_input = new macro_input(def->contents, current_input);
    867 	      break;
    868 	    }
    869 	    else if (lookup_flag == 1) {
    870 	      add_context(token_buffer);
    871 	      return def->tok;
    872 	    }
    873 	  }
    874 	}
    875 	add_context(token_buffer);
    876 	return TEXT;
    877       }
    878     }
    879   }
    880 }
    881 
    882 void do_include()
    883 {
    884   int t = get_token(2);
    885   if (t != TEXT && t != QUOTED_TEXT) {
    886     lex_error("bad filename for include");
    887     return;
    888   }
    889   token_buffer += '\0';
    890   const char *filename = token_buffer.contents();
    891   errno = 0;
    892   FILE *fp = fopen(filename, "r");
    893   if (fp == 0) {
    894     lex_error("can't open included file `%1'", filename);
    895     return;
    896   }
    897   current_input = new file_input(fp, filename, current_input);
    898 }
    899 
    900 void ignore_definition()
    901 {
    902   int t = get_token();
    903   if (t != TEXT) {
    904     lex_error("bad definition");
    905     return;
    906   }
    907   get_delimited_text();
    908 }
    909 
    910 void do_definition(int is_simple)
    911 {
    912   int t = get_token();
    913   if (t != TEXT) {
    914     lex_error("bad definition");
    915     return;
    916   }
    917   token_buffer += '\0';
    918   const char *name = token_buffer.contents();
    919   definition *def = macro_table.lookup(name);
    920   if (def == 0) {
    921     def = new definition[1];
    922     macro_table.define(name, def);
    923   }
    924   else if (def->is_macro) {
    925     a_delete def->contents;
    926   }
    927   get_delimited_text();
    928   token_buffer += '\0';
    929   def->is_macro = 1;
    930   def->contents = strsave(token_buffer.contents());
    931   def->is_simple = is_simple;
    932 }
    933 
    934 void do_undef()
    935 {
    936   int t = get_token();
    937   if (t != TEXT) {
    938     lex_error("bad undef command");
    939     return;
    940   }
    941   token_buffer += '\0';
    942   macro_table.define(token_buffer.contents(), 0);
    943 }
    944 
    945 void do_gsize()
    946 {
    947   int t = get_token(2);
    948   if (t != TEXT && t != QUOTED_TEXT) {
    949     lex_error("bad argument to gsize command");
    950     return;
    951   }
    952   token_buffer += '\0';
    953   if (!set_gsize(token_buffer.contents()))
    954     lex_error("invalid size `%1'", token_buffer.contents());
    955 }
    956 
    957 void do_gfont()
    958 {
    959   int t = get_token(2);
    960   if (t != TEXT && t != QUOTED_TEXT) {
    961     lex_error("bad argument to gfont command");
    962     return;
    963   }
    964   token_buffer += '\0';
    965   set_gfont(token_buffer.contents());
    966 }
    967 
    968 void do_grfont()
    969 {
    970   int t = get_token(2);
    971   if (t != TEXT && t != QUOTED_TEXT) {
    972     lex_error("bad argument to grfont command");
    973     return;
    974   }
    975   token_buffer += '\0';
    976   set_grfont(token_buffer.contents());
    977 }
    978 
    979 void do_gbfont()
    980 {
    981   int t = get_token(2);
    982   if (t != TEXT && t != QUOTED_TEXT) {
    983     lex_error("bad argument to gbfont command");
    984     return;
    985   }
    986   token_buffer += '\0';
    987   set_gbfont(token_buffer.contents());
    988 }
    989 
    990 void do_space()
    991 {
    992   int t = get_token(2);
    993   if (t != TEXT && t != QUOTED_TEXT) {
    994     lex_error("bad argument to space command");
    995     return;
    996   }
    997   token_buffer += '\0';
    998   char *ptr;
    999   long n = strtol(token_buffer.contents(), &ptr, 10);
   1000   if (n == 0 && ptr == token_buffer.contents())
   1001     lex_error("bad argument `%1' to space command", token_buffer.contents());
   1002   else
   1003     set_space(int(n));
   1004 }
   1005 
   1006 void do_ifdef()
   1007 {
   1008   int t = get_token();
   1009   if (t != TEXT) {
   1010     lex_error("bad ifdef");
   1011     return;
   1012   }
   1013   token_buffer += '\0';
   1014   definition *def = macro_table.lookup(token_buffer.contents());
   1015   int result = def && def->is_macro && !def->is_simple;
   1016   get_delimited_text();
   1017   if (result) {
   1018     token_buffer += '\0';
   1019     current_input = new macro_input(token_buffer.contents(), current_input);
   1020   }
   1021 }
   1022 
   1023 void do_delim()
   1024 {
   1025   int c = get_char();
   1026   while (c == ' ' || c == '\n')
   1027     c = get_char();
   1028   int d;
   1029   if (c == EOF || (d = get_char()) == EOF)
   1030     lex_error("end of file while reading argument to `delim'");
   1031   else {
   1032     if (c == 'o' && d == 'f' && peek_char() == 'f') {
   1033       (void)get_char();
   1034       start_delim = end_delim = '\0';
   1035     }
   1036     else {
   1037       start_delim = c;
   1038       end_delim = d;
   1039     }
   1040   }
   1041 }
   1042 
   1043 void do_chartype()
   1044 {
   1045   int t = get_token(2);
   1046   if (t != TEXT && t != QUOTED_TEXT) {
   1047     lex_error("bad chartype");
   1048     return;
   1049   }
   1050   token_buffer += '\0';
   1051   string type = token_buffer;
   1052   t = get_token();
   1053   if (t != TEXT && t != QUOTED_TEXT) {
   1054     lex_error("bad chartype");
   1055     return;
   1056   }
   1057   token_buffer += '\0';
   1058   set_char_type(type.contents(), strsave(token_buffer.contents()));
   1059 }
   1060 
   1061 void do_set()
   1062 {
   1063   int t = get_token(2);
   1064   if (t != TEXT && t != QUOTED_TEXT) {
   1065     lex_error("bad set");
   1066     return;
   1067   }
   1068   token_buffer += '\0';
   1069   string param = token_buffer;
   1070   t = get_token();
   1071   if (t != TEXT && t != QUOTED_TEXT) {
   1072     lex_error("bad set");
   1073     return;
   1074   }
   1075   token_buffer += '\0';
   1076   int n;
   1077   if (sscanf(&token_buffer[0], "%d", &n) != 1) {
   1078     lex_error("bad number `%1'", token_buffer.contents());
   1079     return;
   1080   }
   1081   set_param(param.contents(), n);
   1082 }
   1083 
   1084 int yylex()
   1085 {
   1086   for (;;) {
   1087     int tk = get_token(1);
   1088     switch(tk) {
   1089     case UNDEF:
   1090       do_undef();
   1091       break;
   1092     case SDEFINE:
   1093       do_definition(1);
   1094       break;
   1095     case DEFINE:
   1096       do_definition(0);
   1097       break;
   1098     case TDEFINE:
   1099       if (!nroff)
   1100 	do_definition(0);
   1101       else
   1102 	ignore_definition();
   1103       break;
   1104     case NDEFINE:
   1105       if (nroff)
   1106 	do_definition(0);
   1107       else
   1108 	ignore_definition();
   1109       break;
   1110     case GSIZE:
   1111       do_gsize();
   1112       break;
   1113     case GFONT:
   1114       do_gfont();
   1115       break;
   1116     case GRFONT:
   1117       do_grfont();
   1118       break;
   1119     case GBFONT:
   1120       do_gbfont();
   1121       break;
   1122     case SPACE:
   1123       do_space();
   1124       break;
   1125     case INCLUDE:
   1126       do_include();
   1127       break;
   1128     case IFDEF:
   1129       do_ifdef();
   1130       break;
   1131     case DELIM:
   1132       do_delim();
   1133       break;
   1134     case CHARTYPE:
   1135       do_chartype();
   1136       break;
   1137     case SET:
   1138       do_set();
   1139       break;
   1140     case QUOTED_TEXT:
   1141     case TEXT:
   1142       token_buffer += '\0';
   1143       yylval.str = strsave(token_buffer.contents());
   1144       // fall through
   1145     default:
   1146       return tk;
   1147     }
   1148   }
   1149 }
   1150 
   1151 void lex_error(const char *message,
   1152 	       const errarg &arg1,
   1153 	       const errarg &arg2,
   1154 	       const errarg &arg3)
   1155 {
   1156   char *filename;
   1157   int lineno;
   1158   if (!get_location(&filename, &lineno))
   1159     error(message, arg1, arg2, arg3);
   1160   else
   1161     error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
   1162 }
   1163 
   1164 void yyerror(const char *s)
   1165 {
   1166   char *filename;
   1167   int lineno;
   1168   if (!get_location(&filename, &lineno))
   1169     error(s);
   1170   else
   1171     error_with_file_and_line(filename, lineno, s);
   1172   show_context();
   1173 }
   1174 
   1175