xref: /netbsd-src/external/gpl2/groff/dist/src/preproc/eqn/lex.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
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@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 
definition()46 definition::definition() : is_macro(1), is_simple(0)
47 {
48   contents = 0;
49 }
50 
~definition()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 
init_table(const char * device)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 
input(input * x)347 input::input(input *x) : next(x)
348 {
349 }
350 
~input()351 input::~input()
352 {
353 }
354 
get_location(char **,int *)355 int input::get_location(char **, int *)
356 {
357   return 0;
358 }
359 
file_input(FILE * f,const char * fn,input * p)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 
~file_input()367 file_input::~file_input()
368 {
369   a_delete filename;
370   fclose(fp);
371 }
372 
read_line()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 
get()403 int file_input::get()
404 {
405   if (*ptr != '\0' || read_line())
406     return *ptr++ & 0377;
407   else
408     return EOF;
409 }
410 
peek()411 int file_input::peek()
412 {
413   if (*ptr != '\0' || read_line())
414     return *ptr;
415   else
416     return EOF;
417 }
418 
get_location(char ** fnp,int * lnp)419 int file_input::get_location(char **fnp, int *lnp)
420 {
421   *fnp = filename;
422   *lnp = lineno;
423   return 1;
424 }
425 
macro_input(const char * str,input * x)426 macro_input::macro_input(const char *str, input *x) : input(x)
427 {
428   p = s = strsave(str);
429 }
430 
~macro_input()431 macro_input::~macro_input()
432 {
433   a_delete s;
434 }
435 
get()436 int macro_input::get()
437 {
438   if (p == 0 || *p == '\0')
439     return EOF;
440   else
441     return *p++ & 0377;
442 }
443 
peek()444 int macro_input::peek()
445 {
446   if (p == 0 || *p == '\0')
447     return EOF;
448   else
449     return *p & 0377;
450 }
451 
top_input(const char * str,const char * fn,int ln,input * x)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 
~top_input()458 top_input::~top_input()
459 {
460   a_delete filename;
461 }
462 
get()463 int top_input::get()
464 {
465   int c = macro_input::get();
466   if (c == '\n')
467     lineno++;
468   return c;
469 }
470 
get_location(char ** fnp,int * lnp)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 
argument_macro_input(const char * body,int ac,char ** av,input * x)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 
~argument_macro_input()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 
get()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 
peek()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 
get_char()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 
peek_char()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 
get_location(char ** fnp,int * lnp)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 
flush_context()597 void flush_context()
598 {
599   for (int i = 0; i < NCONTEXT; i++)
600     context_ring[i] = "";
601   context_index = 0;
602 }
603 
show_context()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 
add_context(const string & s)625 void add_context(const string &s)
626 {
627   context_ring[context_index] = s;
628   context_index = (context_index + 1) % NCONTEXT;
629 }
630 
add_context(char c)631 void add_context(char c)
632 {
633   context_ring[context_index] = c;
634   context_index = (context_index + 1) % NCONTEXT;
635 }
636 
add_quoted_context(const string & s)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 
init_lex(const char * str,const char * filename,int lineno)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 
get_delimited_text()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 
interpolate_macro_with_args(const char * body)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 
get_token(int lookup_flag=0)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 
do_include()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 
ignore_definition()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 
do_definition(int is_simple)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 
do_undef()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 
do_gsize()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 
do_gfont()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 
do_grfont()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 
do_gbfont()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 
do_space()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 
do_ifdef()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 
do_delim()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 
do_chartype()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 
do_set()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 
yylex()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 
lex_error(const char * message,const errarg & arg1,const errarg & arg2,const errarg & arg3)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 
yyerror(const char * s)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