2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "stringclass.h"
28 // declarations to avoid friend name injection problems
31 int get_location(char **, int *);
44 definition::definition() : is_macro(1), is_simple(0)
49 definition::~definition()
55 declare_ptable(definition)
56 implement_ptable(definition)
58 PTABLE(definition) macro_table;
65 { "smallover", SMALLOVER },
86 { "uaccent", UACCENT },
102 { "define", DEFINE },
103 { "sdefine", SDEFINE },
104 { "ndefine", NDEFINE },
105 { "tdefine", TDEFINE },
108 { "include", INCLUDE },
111 { "chartype", CHARTYPE },
113 { "vcenter", VCENTER },
115 { "opprime", PRIME },
116 { "grfont", GRFONT },
117 { "gbfont", GBFONT },
119 { "nosplit", NOSPLIT },
120 { "special", SPECIAL },
127 { "ALPHA", "\\(*A" },
130 { "DELTA", "\\(*D" },
131 { "EPSILON", "\\(*E" },
133 { "GAMMA", "\\(*G" },
135 { "KAPPA", "\\(*K" },
136 { "LAMBDA", "\\(*L" },
139 { "OMEGA", "\\(*W" },
140 { "OMICRON", "\\(*O" },
145 { "SIGMA", "\\(*S" },
147 { "THETA", "\\(*H" },
148 { "UPSILON", "\\(*U" },
151 { "Alpha", "\\(*A" },
154 { "Delta", "\\(*D" },
155 { "Epsilon", "\\(*E" },
157 { "Gamma", "\\(*G" },
159 { "Kappa", "\\(*K" },
160 { "Lambda", "\\(*L" },
163 { "Omega", "\\(*W" },
164 { "Omicron", "\\(*O" },
169 { "Sigma", "\\(*S" },
171 { "Theta", "\\(*H" },
172 { "Upsilon", "\\(*U" },
175 { "alpha", "\\(*a" },
178 { "delta", "\\(*d" },
179 { "epsilon", "\\(*e" },
181 { "gamma", "\\(*g" },
183 { "kappa", "\\(*k" },
184 { "lambda", "\\(*l" },
187 { "omega", "\\(*w" },
188 { "omicron", "\\(*o" },
193 { "sigma", "\\(*s" },
195 { "theta", "\\(*h" },
196 { "upsilon", "\\(*u" },
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 { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
219 { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
220 { "int", "{type \"operator\" vcenter size +8 \\(is}" },
221 { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
222 { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
223 { "times", "type \"binary\" \\(mu" },
224 { "ldots", "type \"inner\" { . . . }" },
226 { "partial", "\\(pd" },
227 { "nothing", "\"\"" },
228 { "half", "{1 smallover 2}" },
229 { "hat_def", "roman \"^\"" },
230 { "hat", "accent { hat_def }" },
231 { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" },
232 { "dot", "accent { dot_def }" },
233 { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" },
234 { "dotdot", "accent { dotdot_def }" },
235 { "tilde_def", "\"~\"" },
236 { "tilde", "accent { tilde_def }" },
237 { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" },
238 { "utilde", "uaccent { utilde_def }" },
239 { "vec_def", "up 52 size -5 \\(->" },
240 { "vec", "accent { vec_def }" },
241 { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" },
242 { "dyad", "accent { dyad_def }" },
243 { "==", "type \"relation\" \\(==" },
244 { "!=", "type \"relation\" \\(!=" },
245 { "+-", "type \"binary\" \\(+-" },
246 { "->", "type \"relation\" \\(->" },
247 { "<-", "type \"relation\" \\(<-" },
248 { "<<", "{ < back 20 < }" },
249 { ">>", "{ > back 20 > }" },
250 { "...", "type \"inner\" vcenter { . . . }" },
252 { "approx", "type \"relation\" \"\\(~=\"" },
255 { "cdot", "type \"binary\" vcenter ." },
259 void init_table(const char *device)
262 for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
263 definition *def = new definition[1];
265 def->tok = token_table[i].token;
266 macro_table.define(token_table[i].name, def);
268 for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) {
269 definition *def = new definition[1];
271 def->contents = strsave(def_table[i].def);
273 macro_table.define(def_table[i].name, def);
275 definition *def = new definition[1];
277 def->contents = strsave("1");
278 macro_table.define(device, def);
286 virtual int get() = 0;
287 virtual int peek() = 0;
288 virtual int get_location(char **, int *);
290 friend int get_char();
291 friend int peek_char();
292 friend int get_location(char **, int *);
293 friend void init_lex(const char *str, const char *filename, int lineno);
296 class file_input : public input {
304 file_input(FILE *, const char *, input *);
308 int get_location(char **, int *);
312 class macro_input : public input {
316 macro_input(const char *, input *);
322 class top_input : public macro_input {
326 top_input(const char *, const char *, int, input *);
329 int get_location(char **, int *);
332 class argument_macro_input: public input {
339 argument_macro_input(const char *, int, char **, input *);
340 ~argument_macro_input();
345 input::input(input *x) : next(x)
353 int input::get_location(char **, int *)
358 file_input::file_input(FILE *f, const char *fn, input *p)
359 : input(p), lineno(0), ptr("")
362 filename = strsave(fn);
365 file_input::~file_input()
371 int file_input::read_line()
380 else if (invalid_input_char(c))
381 lex_error("invalid input character code %1", c);
388 if (line.length() == 0)
390 if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
391 && (line[2] == 'Q' || line[2] == 'N')
392 && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
393 || compatible_flag))) {
395 ptr = line.contents();
401 int file_input::get()
403 if (*ptr != '\0' || read_line())
404 return *ptr++ & 0377;
409 int file_input::peek()
411 if (*ptr != '\0' || read_line())
417 int file_input::get_location(char **fnp, int *lnp)
424 macro_input::macro_input(const char *str, input *x) : input(x)
426 p = s = strsave(str);
429 macro_input::~macro_input()
434 int macro_input::get()
436 if (p == 0 || *p == '\0')
442 int macro_input::peek()
444 if (p == 0 || *p == '\0')
450 top_input::top_input(const char *str, const char *fn, int ln, input *x)
451 : macro_input(str, x), lineno(ln)
453 filename = strsave(fn);
456 top_input::~top_input()
463 int c = macro_input::get();
469 int top_input::get_location(char **fnp, int *lnp)
476 // Character representing $1. Must be invalid input character.
479 argument_macro_input::argument_macro_input(const char *body, int ac,
481 : input(x), ap(0), argc(ac)
484 for (i = 0; i < argc; i++)
486 p = s = strsave(body);
488 for (i = 0; s[i] != '\0'; i++)
489 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
491 s[j++] = ARG1 + s[++i] - '1';
499 argument_macro_input::~argument_macro_input()
501 for (int i = 0; i < argc; i++)
506 int argument_macro_input::get()
515 while (*p >= ARG1 && *p <= ARG1 + 8) {
517 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
527 int argument_macro_input::peek()
536 while (*p >= ARG1 && *p <= ARG1 + 8) {
538 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
548 static input *current_input = 0;
550 /* we insert a newline between input from different levels */
554 if (current_input == 0)
557 int c = current_input->get();
561 input *tem = current_input;
562 current_input = current_input->next;
571 if (current_input == 0)
574 int c = current_input->peek();
582 int get_location(char **fnp, int *lnp)
584 for (input *p = current_input; p; p = p->next)
585 if (p->get_location(fnp, lnp))
591 const int NCONTEXT = 4;
592 string context_ring[NCONTEXT];
593 int context_index = 0;
597 for (int i = 0; i < NCONTEXT; i++)
598 context_ring[i] = "";
604 int i = context_index;
605 fputs(" context is\n\t", stderr);
607 int j = (i + 1) % NCONTEXT;
608 if (j == context_index) {
609 fputs(">>> ", stderr);
610 put_string(context_ring[i], stderr);
611 fputs(" <<<", stderr);
614 else if (context_ring[i].length() > 0) {
615 put_string(context_ring[i], stderr);
623 void add_context(const string &s)
625 context_ring[context_index] = s;
626 context_index = (context_index + 1) % NCONTEXT;
629 void add_context(char c)
631 context_ring[context_index] = c;
632 context_index = (context_index + 1) % NCONTEXT;
635 void add_quoted_context(const string &s)
637 string &r = context_ring[context_index];
639 for (int i = 0; i < s.length(); i++)
645 context_index = (context_index + 1) % NCONTEXT;
648 void init_lex(const char *str, const char *filename, int lineno)
650 while (current_input != 0) {
651 input *tem = current_input;
652 current_input = current_input->next;
655 current_input = new top_input(str, filename, lineno, 0);
660 void get_delimited_text()
664 int got_location = get_location(&filename, &lineno);
665 int start = get_char();
666 while (start == ' ' || start == '\t' || start == '\n')
668 token_buffer.clear();
671 error_with_file_and_line(filename, lineno,
672 "end of input while defining macro");
674 error("end of input while defining macro");
681 error_with_file_and_line(filename, lineno,
682 "end of input while defining macro");
684 error("end of input while defining macro");
685 add_context(start + token_buffer);
690 token_buffer += char(c);
692 add_context(start + token_buffer + start);
695 void interpolate_macro_with_args(const char *body)
700 for (i = 0; i < 9; i++)
705 token_buffer.clear();
709 lex_error("end of input while scanning macro arguments");
712 if (level == 0 && (c == ',' || c == ')')) {
713 if (token_buffer.length() > 0) {
714 token_buffer += '\0';
715 argv[argc] = strsave(token_buffer.contents());
717 // for `foo()', argc = 0
718 if (argc > 0 || c != ')' || i > 0)
722 token_buffer += char(c);
728 } while (c != ')' && c != EOF);
729 current_input = new argument_macro_input(body, argc, argv, current_input);
732 /* If lookup flag is non-zero the token will be looked up to see
733 if it is macro. If it's 1, it will looked up to see if it's a token.
736 int get_token(int lookup_flag = 0)
740 while (c == ' ' || c == '\n')
745 add_context("end of input");
751 token_buffer.clear();
755 lex_error("missing \"");
758 else if (c == '\n') {
759 lex_error("newline before end of quoted text");
765 token_buffer[token_buffer.length() - 1] = '"';
770 quoted = quoted ? 0 : c == '\\';
774 add_quoted_context(token_buffer);
787 token_buffer.clear();
795 if (!quoted && lookup_flag != 0 && c == '(') {
796 token_buffer += '\0';
797 definition *def = macro_table.lookup(token_buffer.contents());
798 if (def && def->is_macro && !def->is_simple) {
799 (void)get_char(); // skip initial '('
800 interpolate_macro_with_args(def->contents);
804 token_buffer.set_length(token_buffer.length() - 1);
810 lex_error("`\\' ignored at end of equation");
814 lex_error("`\\' ignored because followed by newline");
818 lex_error("`\\' ignored because followed by tab");
827 token_buffer += '\\';
851 token_buffer += char(c);
856 if (break_flag || token_buffer.length() == 0)
858 if (lookup_flag != 0) {
859 token_buffer += '\0';
860 definition *def = macro_table.lookup(token_buffer.contents());
861 token_buffer.set_length(token_buffer.length() - 1);
864 current_input = new macro_input(def->contents, current_input);
867 else if (lookup_flag == 1) {
868 add_context(token_buffer);
873 add_context(token_buffer);
882 int t = get_token(2);
883 if (t != TEXT && t != QUOTED_TEXT) {
884 lex_error("bad filename for include");
887 token_buffer += '\0';
888 const char *filename = token_buffer.contents();
890 FILE *fp = fopen(filename, "r");
892 lex_error("can't open included file `%1'", filename);
895 current_input = new file_input(fp, filename, current_input);
898 void ignore_definition()
902 lex_error("bad definition");
905 get_delimited_text();
908 void do_definition(int is_simple)
912 lex_error("bad definition");
915 token_buffer += '\0';
916 const char *name = token_buffer.contents();
917 definition *def = macro_table.lookup(name);
919 def = new definition[1];
920 macro_table.define(name, def);
922 else if (def->is_macro) {
923 a_delete def->contents;
925 get_delimited_text();
926 token_buffer += '\0';
928 def->contents = strsave(token_buffer.contents());
929 def->is_simple = is_simple;
936 lex_error("bad undef command");
939 token_buffer += '\0';
940 macro_table.define(token_buffer.contents(), 0);
945 int t = get_token(2);
946 if (t != TEXT && t != QUOTED_TEXT) {
947 lex_error("bad argument to gsize command");
950 token_buffer += '\0';
951 if (!set_gsize(token_buffer.contents()))
952 lex_error("invalid size `%1'", token_buffer.contents());
957 int t = get_token(2);
958 if (t != TEXT && t != QUOTED_TEXT) {
959 lex_error("bad argument to gfont command");
962 token_buffer += '\0';
963 set_gfont(token_buffer.contents());
968 int t = get_token(2);
969 if (t != TEXT && t != QUOTED_TEXT) {
970 lex_error("bad argument to grfont command");
973 token_buffer += '\0';
974 set_grfont(token_buffer.contents());
979 int t = get_token(2);
980 if (t != TEXT && t != QUOTED_TEXT) {
981 lex_error("bad argument to gbfont command");
984 token_buffer += '\0';
985 set_gbfont(token_buffer.contents());
990 int t = get_token(2);
991 if (t != TEXT && t != QUOTED_TEXT) {
992 lex_error("bad argument to space command");
995 token_buffer += '\0';
997 long n = strtol(token_buffer.contents(), &ptr, 10);
998 if (n == 0 && ptr == token_buffer.contents())
999 lex_error("bad argument `%1' to space command", token_buffer.contents());
1006 int t = get_token();
1008 lex_error("bad ifdef");
1011 token_buffer += '\0';
1012 definition *def = macro_table.lookup(token_buffer.contents());
1013 int result = def && def->is_macro && !def->is_simple;
1014 get_delimited_text();
1016 token_buffer += '\0';
1017 current_input = new macro_input(token_buffer.contents(), current_input);
1024 while (c == ' ' || c == '\n')
1027 if (c == EOF || (d = get_char()) == EOF)
1028 lex_error("end of file while reading argument to `delim'");
1030 if (c == 'o' && d == 'f' && peek_char() == 'f') {
1032 start_delim = end_delim = '\0';
1043 int t = get_token(2);
1044 if (t != TEXT && t != QUOTED_TEXT) {
1045 lex_error("bad chartype");
1048 token_buffer += '\0';
1049 string type = token_buffer;
1051 if (t != TEXT && t != QUOTED_TEXT) {
1052 lex_error("bad chartype");
1055 token_buffer += '\0';
1056 set_char_type(type.contents(), strsave(token_buffer.contents()));
1061 int t = get_token(2);
1062 if (t != TEXT && t != QUOTED_TEXT) {
1063 lex_error("bad set");
1066 token_buffer += '\0';
1067 string param = token_buffer;
1069 if (t != TEXT && t != QUOTED_TEXT) {
1070 lex_error("bad set");
1073 token_buffer += '\0';
1075 if (sscanf(&token_buffer[0], "%d", &n) != 1) {
1076 lex_error("bad number `%1'", token_buffer.contents());
1079 set_param(param.contents(), n);
1085 int tk = get_token(1);
1100 ignore_definition();
1106 ignore_definition();
1140 token_buffer += '\0';
1141 yylval.str = strsave(token_buffer.contents());
1149 void lex_error(const char *message,
1156 if (!get_location(&filename, &lineno))
1157 error(message, arg1, arg2, arg3);
1159 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1162 void yyerror(const char *s)
1166 if (!get_location(&filename, &lineno))
1169 error_with_file_and_line(filename, lineno, s);