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