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