2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005, 2007,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
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.
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
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/>. */
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 },
128 static struct builtin_def common_defs[] = {
129 { "ALPHA", "\\(*A" },
132 { "DELTA", "\\(*D" },
133 { "EPSILON", "\\(*E" },
135 { "GAMMA", "\\(*G" },
137 { "KAPPA", "\\(*K" },
138 { "LAMBDA", "\\(*L" },
141 { "OMEGA", "\\(*W" },
142 { "OMICRON", "\\(*O" },
147 { "SIGMA", "\\(*S" },
149 { "THETA", "\\(*H" },
150 { "UPSILON", "\\(*U" },
153 { "Alpha", "\\(*A" },
156 { "Delta", "\\(*D" },
157 { "Epsilon", "\\(*E" },
159 { "Gamma", "\\(*G" },
161 { "Kappa", "\\(*K" },
162 { "Lambda", "\\(*L" },
165 { "Omega", "\\(*W" },
166 { "Omicron", "\\(*O" },
171 { "Sigma", "\\(*S" },
173 { "Theta", "\\(*H" },
174 { "Upsilon", "\\(*U" },
177 { "alpha", "\\(*a" },
180 { "delta", "\\(*d" },
181 { "epsilon", "\\(*e" },
183 { "gamma", "\\(*g" },
185 { "kappa", "\\(*k" },
186 { "lambda", "\\(*l" },
189 { "omega", "\\(*w" },
190 { "omicron", "\\(*o" },
195 { "sigma", "\\(*s" },
197 { "theta", "\\(*h" },
198 { "upsilon", "\\(*u" },
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\" { . . . }" },
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\" \\(>>" },
238 { "approx", "type \"relation\" \"\\(~=\"" },
241 { "cdot", "type \"binary\" \\(md" },
242 { "cdots", "type \"inner\" { \\(md \\(md \\(md }" },
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 { . . . }" },
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\" { . . . }" },
281 void init_table(const char *device)
284 for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
285 definition *def = new definition[1];
287 def->tok = token_table[i].token;
288 macro_table.define(token_table[i].name, def);
290 for (i = 0; i < sizeof(common_defs)/sizeof(common_defs[0]); i++) {
291 definition *def = new definition[1];
293 def->contents = strsave(common_defs[i].def);
295 macro_table.define(common_defs[i].name, def);
297 if (output_format == troff) {
298 for (i = 0; i < sizeof(troff_defs)/sizeof(troff_defs[0]); i++) {
299 definition *def = new definition[1];
301 def->contents = strsave(troff_defs[i].def);
303 macro_table.define(troff_defs[i].name, def);
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];
310 def->contents = strsave(mathml_defs[i].def);
312 macro_table.define(mathml_defs[i].name, def);
315 definition *def = new definition[1];
317 def->contents = strsave("1");
318 macro_table.define(device, def);
326 virtual int get() = 0;
327 virtual int peek() = 0;
328 virtual int get_location(char **, int *);
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);
336 class file_input : public input {
344 file_input(FILE *, const char *, input *);
348 int get_location(char **, int *);
352 class macro_input : public input {
356 macro_input(const char *, input *);
362 class top_input : public macro_input {
366 top_input(const char *, const char *, int, input *);
369 int get_location(char **, int *);
372 class argument_macro_input: public input {
379 argument_macro_input(const char *, int, char **, input *);
380 ~argument_macro_input();
385 input::input(input *x) : next(x)
393 int input::get_location(char **, int *)
398 file_input::file_input(FILE *f, const char *fn, input *p)
399 : input(p), lineno(0), ptr("")
402 filename = strsave(fn);
405 file_input::~file_input()
411 int file_input::read_line()
420 else if (invalid_input_char(c))
421 lex_error("invalid input character code %1", c);
428 if (line.length() == 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))) {
435 ptr = line.contents();
441 int file_input::get()
443 if (*ptr != '\0' || read_line())
444 return *ptr++ & 0377;
449 int file_input::peek()
451 if (*ptr != '\0' || read_line())
457 int file_input::get_location(char **fnp, int *lnp)
464 macro_input::macro_input(const char *str, input *x) : input(x)
466 p = s = strsave(str);
469 macro_input::~macro_input()
474 int macro_input::get()
476 if (p == 0 || *p == '\0')
482 int macro_input::peek()
484 if (p == 0 || *p == '\0')
490 top_input::top_input(const char *str, const char *fn, int ln, input *x)
491 : macro_input(str, x), lineno(ln)
493 filename = strsave(fn);
496 top_input::~top_input()
503 int c = macro_input::get();
509 int top_input::get_location(char **fnp, int *lnp)
516 // Character representing $1. Must be invalid input character.
519 argument_macro_input::argument_macro_input(const char *body, int ac,
521 : input(x), ap(0), argc(ac)
524 for (i = 0; i < argc; i++)
526 p = s = strsave(body);
528 for (i = 0; s[i] != '\0'; i++)
529 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
531 s[j++] = ARG1 + s[++i] - '1';
539 argument_macro_input::~argument_macro_input()
541 for (int i = 0; i < argc; i++)
546 int argument_macro_input::get()
555 while (*p >= ARG1 && *p <= ARG1 + 8) {
557 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
567 int argument_macro_input::peek()
576 while (*p >= ARG1 && *p <= ARG1 + 8) {
578 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
588 static input *current_input = 0;
590 /* we insert a newline between input from different levels */
594 if (current_input == 0)
597 int c = current_input->get();
601 input *tem = current_input;
602 current_input = current_input->next;
611 if (current_input == 0)
614 int c = current_input->peek();
622 int get_location(char **fnp, int *lnp)
624 for (input *p = current_input; p; p = p->next)
625 if (p->get_location(fnp, lnp))
631 const int NCONTEXT = 4;
632 string context_ring[NCONTEXT];
633 int context_index = 0;
637 for (int i = 0; i < NCONTEXT; i++)
638 context_ring[i] = "";
644 int i = context_index;
645 fputs(" context is\n\t", stderr);
647 int j = (i + 1) % NCONTEXT;
648 if (j == context_index) {
649 fputs(">>> ", stderr);
650 put_string(context_ring[i], stderr);
651 fputs(" <<<", stderr);
654 else if (context_ring[i].length() > 0) {
655 put_string(context_ring[i], stderr);
663 void add_context(const string &s)
665 context_ring[context_index] = s;
666 context_index = (context_index + 1) % NCONTEXT;
669 void add_context(char c)
671 context_ring[context_index] = c;
672 context_index = (context_index + 1) % NCONTEXT;
675 void add_quoted_context(const string &s)
677 string &r = context_ring[context_index];
679 for (int i = 0; i < s.length(); i++)
685 context_index = (context_index + 1) % NCONTEXT;
688 void init_lex(const char *str, const char *filename, int lineno)
690 while (current_input != 0) {
691 input *tem = current_input;
692 current_input = current_input->next;
695 current_input = new top_input(str, filename, lineno, 0);
700 void get_delimited_text()
704 int got_location = get_location(&filename, &lineno);
705 int start = get_char();
706 while (start == ' ' || start == '\t' || start == '\n')
708 token_buffer.clear();
711 error_with_file_and_line(filename, lineno,
712 "end of input while defining macro");
714 error("end of input while defining macro");
721 error_with_file_and_line(filename, lineno,
722 "end of input while defining macro");
724 error("end of input while defining macro");
725 add_context(start + token_buffer);
730 token_buffer += char(c);
732 add_context(start + token_buffer + start);
735 void interpolate_macro_with_args(const char *body)
740 for (i = 0; i < 9; i++)
745 token_buffer.clear();
749 lex_error("end of input while scanning macro arguments");
752 if (level == 0 && (c == ',' || c == ')')) {
753 if (token_buffer.length() > 0) {
754 token_buffer += '\0';
755 argv[argc] = strsave(token_buffer.contents());
757 // for `foo()', argc = 0
758 if (argc > 0 || c != ')' || i > 0)
762 token_buffer += char(c);
768 } while (c != ')' && c != EOF);
769 current_input = new argument_macro_input(body, argc, argv, current_input);
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.
776 int get_token(int lookup_flag = 0)
780 while (c == ' ' || c == '\n')
785 add_context("end of input");
791 token_buffer.clear();
795 lex_error("missing \"");
798 else if (c == '\n') {
799 lex_error("newline before end of quoted text");
805 token_buffer[token_buffer.length() - 1] = '"';
810 quoted = quoted ? 0 : c == '\\';
814 add_quoted_context(token_buffer);
827 token_buffer.clear();
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);
844 token_buffer.set_length(token_buffer.length() - 1);
850 lex_error("`\\' ignored at end of equation");
854 lex_error("`\\' ignored because followed by newline");
858 lex_error("`\\' ignored because followed by tab");
867 token_buffer += '\\';
891 token_buffer += char(c);
896 if (break_flag || token_buffer.length() == 0)
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);
904 current_input = new macro_input(def->contents, current_input);
907 else if (lookup_flag == 1) {
908 add_context(token_buffer);
913 add_context(token_buffer);
922 int t = get_token(2);
923 if (t != TEXT && t != QUOTED_TEXT) {
924 lex_error("bad filename for include");
927 token_buffer += '\0';
928 const char *filename = token_buffer.contents();
930 FILE *fp = fopen(filename, "r");
932 lex_error("can't open included file `%1'", filename);
935 current_input = new file_input(fp, filename, current_input);
938 void ignore_definition()
942 lex_error("bad definition");
945 get_delimited_text();
948 void do_definition(int is_simple)
952 lex_error("bad definition");
955 token_buffer += '\0';
956 const char *name = token_buffer.contents();
957 definition *def = macro_table.lookup(name);
959 def = new definition[1];
960 macro_table.define(name, def);
962 else if (def->is_macro) {
963 a_delete def->contents;
965 get_delimited_text();
966 token_buffer += '\0';
968 def->contents = strsave(token_buffer.contents());
969 def->is_simple = is_simple;
976 lex_error("bad undef command");
979 token_buffer += '\0';
980 macro_table.define(token_buffer.contents(), 0);
985 int t = get_token(2);
986 if (t != TEXT && t != QUOTED_TEXT) {
987 lex_error("bad argument to gsize command");
990 token_buffer += '\0';
991 if (!set_gsize(token_buffer.contents()))
992 lex_error("invalid size `%1'", token_buffer.contents());
997 int t = get_token(2);
998 if (t != TEXT && t != QUOTED_TEXT) {
999 lex_error("bad argument to gfont command");
1002 token_buffer += '\0';
1003 set_gfont(token_buffer.contents());
1008 int t = get_token(2);
1009 if (t != TEXT && t != QUOTED_TEXT) {
1010 lex_error("bad argument to grfont command");
1013 token_buffer += '\0';
1014 set_grfont(token_buffer.contents());
1019 int t = get_token(2);
1020 if (t != TEXT && t != QUOTED_TEXT) {
1021 lex_error("bad argument to gbfont command");
1024 token_buffer += '\0';
1025 set_gbfont(token_buffer.contents());
1030 int t = get_token(2);
1031 if (t != TEXT && t != QUOTED_TEXT) {
1032 lex_error("bad argument to space command");
1035 token_buffer += '\0';
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());
1046 int t = get_token();
1048 lex_error("bad ifdef");
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();
1056 token_buffer += '\0';
1057 current_input = new macro_input(token_buffer.contents(), current_input);
1064 while (c == ' ' || c == '\n')
1067 if (c == EOF || (d = get_char()) == EOF)
1068 lex_error("end of file while reading argument to `delim'");
1070 if (c == 'o' && d == 'f' && peek_char() == 'f') {
1072 start_delim = end_delim = '\0';
1083 int t = get_token(2);
1084 if (t != TEXT && t != QUOTED_TEXT) {
1085 lex_error("bad chartype");
1088 token_buffer += '\0';
1089 string type = token_buffer;
1091 if (t != TEXT && t != QUOTED_TEXT) {
1092 lex_error("bad chartype");
1095 token_buffer += '\0';
1096 set_char_type(type.contents(), strsave(token_buffer.contents()));
1101 int t = get_token(2);
1102 if (t != TEXT && t != QUOTED_TEXT) {
1103 lex_error("bad set");
1106 token_buffer += '\0';
1107 string param = token_buffer;
1109 if (t != TEXT && t != QUOTED_TEXT) {
1110 lex_error("bad set");
1113 token_buffer += '\0';
1115 if (sscanf(&token_buffer[0], "%d", &n) != 1) {
1116 lex_error("bad number `%1'", token_buffer.contents());
1119 set_param(param.contents(), n);
1125 int tk = get_token(1);
1140 ignore_definition();
1146 ignore_definition();
1180 token_buffer += '\0';
1181 yylval.str = strsave(token_buffer.contents());
1189 void lex_error(const char *message,
1196 if (!get_location(&filename, &lineno))
1197 error(message, arg1, arg2, arg3);
1199 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1202 void yyerror(const char *s)
1206 if (!get_location(&filename, &lineno))
1209 error_with_file_and_line(filename, lineno, s);