2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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.
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
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/>. */
22 #include "stringclass.h"
26 // declarations to avoid friend name injection problems
29 int get_location(char **, int *);
42 definition::definition() : is_macro(1), is_simple(0)
47 definition::~definition()
53 declare_ptable(definition)
54 implement_ptable(definition)
56 PTABLE(definition) macro_table;
63 { "smallover", SMALLOVER },
84 { "uaccent", UACCENT },
100 { "define", DEFINE },
101 { "sdefine", SDEFINE },
102 { "ndefine", NDEFINE },
103 { "tdefine", TDEFINE },
106 { "include", INCLUDE },
109 { "chartype", CHARTYPE },
111 { "vcenter", VCENTER },
113 { "opprime", PRIME },
114 { "grfont", GRFONT },
115 { "gbfont", GBFONT },
117 { "nosplit", NOSPLIT },
118 { "special", SPECIAL },
126 static struct builtin_def common_defs[] = {
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 { "times", "type \"binary\" \\(mu" },
219 { "ldots", "type \"inner\" { . . . }" },
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\" \\(>>" },
236 { "approx", "type \"relation\" \"\\(~=\"" },
239 { "cdot", "type \"binary\" \\(md" },
240 { "cdots", "type \"inner\" { \\(md \\(md \\(md }" },
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 { . . . }" },
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\" { . . . }" },
279 void init_table(const char *device)
282 for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
283 definition *def = new definition[1];
285 def->tok = token_table[i].token;
286 macro_table.define(token_table[i].name, def);
288 for (i = 0; i < sizeof(common_defs)/sizeof(common_defs[0]); i++) {
289 definition *def = new definition[1];
291 def->contents = strsave(common_defs[i].def);
293 macro_table.define(common_defs[i].name, def);
295 if (output_format == troff) {
296 for (i = 0; i < sizeof(troff_defs)/sizeof(troff_defs[0]); i++) {
297 definition *def = new definition[1];
299 def->contents = strsave(troff_defs[i].def);
301 macro_table.define(troff_defs[i].name, def);
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];
308 def->contents = strsave(mathml_defs[i].def);
310 macro_table.define(mathml_defs[i].name, def);
313 definition *def = new definition[1];
315 def->contents = strsave("1");
316 macro_table.define(device, def);
324 virtual int get() = 0;
325 virtual int peek() = 0;
326 virtual int get_location(char **, int *);
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);
334 class file_input : public input {
342 file_input(FILE *, const char *, input *);
346 int get_location(char **, int *);
350 class macro_input : public input {
354 macro_input(const char *, input *);
360 class top_input : public macro_input {
364 top_input(const char *, const char *, int, input *);
367 int get_location(char **, int *);
370 class argument_macro_input: public input {
377 argument_macro_input(const char *, int, char **, input *);
378 ~argument_macro_input();
383 input::input(input *x) : next(x)
391 int input::get_location(char **, int *)
396 file_input::file_input(FILE *f, const char *fn, input *p)
397 : input(p), lineno(0), ptr("")
400 filename = strsave(fn);
403 file_input::~file_input()
409 int file_input::read_line()
419 lex_error("invalid input character code %1", '\r');
423 else if (invalid_input_char(c))
424 lex_error("invalid input character code %1", c);
431 if (line.length() == 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))) {
438 ptr = line.contents();
444 int file_input::get()
446 if (*ptr != '\0' || read_line())
447 return *ptr++ & 0377;
452 int file_input::peek()
454 if (*ptr != '\0' || read_line())
460 int file_input::get_location(char **fnp, int *lnp)
467 macro_input::macro_input(const char *str, input *x) : input(x)
469 p = s = strsave(str);
472 macro_input::~macro_input()
477 int macro_input::get()
479 if (p == 0 || *p == '\0')
485 int macro_input::peek()
487 if (p == 0 || *p == '\0')
493 top_input::top_input(const char *str, const char *fn, int ln, input *x)
494 : macro_input(str, x), lineno(ln)
496 filename = strsave(fn);
499 top_input::~top_input()
506 int c = macro_input::get();
512 int top_input::get_location(char **fnp, int *lnp)
519 // Character representing $1. Must be invalid input character.
522 argument_macro_input::argument_macro_input(const char *body, int ac,
524 : input(x), ap(0), argc(ac)
527 for (i = 0; i < argc; i++)
529 p = s = strsave(body);
531 for (i = 0; s[i] != '\0'; i++)
532 if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
534 s[j++] = ARG1 + s[++i] - '1';
542 argument_macro_input::~argument_macro_input()
544 for (int i = 0; i < argc; i++)
549 int argument_macro_input::get()
558 while (*p >= ARG1 && *p <= ARG1 + 8) {
560 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
570 int argument_macro_input::peek()
579 while (*p >= ARG1 && *p <= ARG1 + 8) {
581 if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
591 static input *current_input = 0;
593 /* we insert a newline between input from different levels */
597 if (current_input == 0)
600 int c = current_input->get();
604 input *tem = current_input;
605 current_input = current_input->next;
614 if (current_input == 0)
617 int c = current_input->peek();
625 int get_location(char **fnp, int *lnp)
627 for (input *p = current_input; p; p = p->next)
628 if (p->get_location(fnp, lnp))
634 const int NCONTEXT = 4;
635 string context_ring[NCONTEXT];
636 int context_index = 0;
640 for (int i = 0; i < NCONTEXT; i++)
641 context_ring[i] = "";
647 int i = context_index;
648 fputs(" context is\n\t", stderr);
650 int j = (i + 1) % NCONTEXT;
651 if (j == context_index) {
652 fputs(">>> ", stderr);
653 put_string(context_ring[i], stderr);
654 fputs(" <<<", stderr);
657 else if (context_ring[i].length() > 0) {
658 put_string(context_ring[i], stderr);
666 void add_context(const string &s)
668 context_ring[context_index] = s;
669 context_index = (context_index + 1) % NCONTEXT;
672 void add_context(char c)
674 context_ring[context_index] = c;
675 context_index = (context_index + 1) % NCONTEXT;
678 void add_quoted_context(const string &s)
680 string &r = context_ring[context_index];
682 for (int i = 0; i < s.length(); i++)
688 context_index = (context_index + 1) % NCONTEXT;
691 void init_lex(const char *str, const char *filename, int lineno)
693 while (current_input != 0) {
694 input *tem = current_input;
695 current_input = current_input->next;
698 current_input = new top_input(str, filename, lineno, 0);
703 void get_delimited_text()
707 int got_location = get_location(&filename, &lineno);
708 int start = get_char();
709 while (start == ' ' || start == '\t' || start == '\n')
711 token_buffer.clear();
714 error_with_file_and_line(filename, lineno,
715 "end of input while defining macro");
717 error("end of input while defining macro");
724 error_with_file_and_line(filename, lineno,
725 "end of input while defining macro");
727 error("end of input while defining macro");
728 add_context(start + token_buffer);
733 token_buffer += char(c);
735 add_context(start + token_buffer + start);
738 void interpolate_macro_with_args(const char *body)
743 for (i = 0; i < 9; i++)
748 token_buffer.clear();
752 lex_error("end of input while scanning macro arguments");
755 if (level == 0 && (c == ',' || c == ')')) {
756 if (token_buffer.length() > 0) {
757 token_buffer += '\0';
758 argv[argc] = strsave(token_buffer.contents());
760 // for `foo()', argc = 0
761 if (argc > 0 || c != ')' || i > 0)
765 token_buffer += char(c);
771 } while (c != ')' && c != EOF);
772 current_input = new argument_macro_input(body, argc, argv, current_input);
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.
779 int get_token(int lookup_flag = 0)
783 while (c == ' ' || c == '\n')
788 add_context("end of input");
794 token_buffer.clear();
798 lex_error("missing \"");
801 else if (c == '\n') {
802 lex_error("newline before end of quoted text");
808 token_buffer[token_buffer.length() - 1] = '"';
813 quoted = quoted ? 0 : c == '\\';
817 add_quoted_context(token_buffer);
830 token_buffer.clear();
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);
847 token_buffer.set_length(token_buffer.length() - 1);
853 lex_error("`\\' ignored at end of equation");
857 lex_error("`\\' ignored because followed by newline");
861 lex_error("`\\' ignored because followed by tab");
870 token_buffer += '\\';
894 token_buffer += char(c);
899 if (break_flag || token_buffer.length() == 0)
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);
907 current_input = new macro_input(def->contents, current_input);
910 else if (lookup_flag == 1) {
911 add_context(token_buffer);
916 add_context(token_buffer);
925 int t = get_token(2);
926 if (t != TEXT && t != QUOTED_TEXT) {
927 lex_error("bad filename for include");
930 token_buffer += '\0';
931 const char *filename = token_buffer.contents();
933 FILE *fp = fopen(filename, "r");
935 lex_error("can't open included file `%1'", filename);
938 current_input = new file_input(fp, filename, current_input);
941 void ignore_definition()
945 lex_error("bad definition");
948 get_delimited_text();
951 void do_definition(int is_simple)
955 lex_error("bad definition");
958 token_buffer += '\0';
959 const char *name = token_buffer.contents();
960 definition *def = macro_table.lookup(name);
962 def = new definition[1];
963 macro_table.define(name, def);
965 else if (def->is_macro) {
966 a_delete def->contents;
968 get_delimited_text();
969 token_buffer += '\0';
971 def->contents = strsave(token_buffer.contents());
972 def->is_simple = is_simple;
979 lex_error("bad undef command");
982 token_buffer += '\0';
983 macro_table.define(token_buffer.contents(), 0);
988 int t = get_token(2);
989 if (t != TEXT && t != QUOTED_TEXT) {
990 lex_error("bad argument to gsize command");
993 token_buffer += '\0';
994 if (!set_gsize(token_buffer.contents()))
995 lex_error("invalid size `%1'", token_buffer.contents());
1000 int t = get_token(2);
1001 if (t != TEXT && t != QUOTED_TEXT) {
1002 lex_error("bad argument to gfont command");
1005 token_buffer += '\0';
1006 set_gfont(token_buffer.contents());
1011 int t = get_token(2);
1012 if (t != TEXT && t != QUOTED_TEXT) {
1013 lex_error("bad argument to grfont command");
1016 token_buffer += '\0';
1017 set_grfont(token_buffer.contents());
1022 int t = get_token(2);
1023 if (t != TEXT && t != QUOTED_TEXT) {
1024 lex_error("bad argument to gbfont command");
1027 token_buffer += '\0';
1028 set_gbfont(token_buffer.contents());
1033 int t = get_token(2);
1034 if (t != TEXT && t != QUOTED_TEXT) {
1035 lex_error("bad argument to space command");
1038 token_buffer += '\0';
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());
1049 int t = get_token();
1051 lex_error("bad ifdef");
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();
1059 token_buffer += '\0';
1060 current_input = new macro_input(token_buffer.contents(), current_input);
1064 char start_delim_saved = '\0';
1065 char end_delim_saved = '\0';
1070 while (c == ' ' || c == '\n')
1073 if (c == EOF || (d = get_char()) == EOF)
1074 lex_error("end of file while reading argument to `delim'");
1076 if (c == 'o' && d == 'f' && peek_char() == 'f') {
1078 start_delim_saved = start_delim;
1079 end_delim_saved = end_delim;
1080 start_delim = end_delim = '\0';
1082 else if (c == 'o' && d == 'n' && !compatible_flag) {
1083 start_delim = start_delim_saved;
1084 end_delim = end_delim_saved;
1095 int t = get_token(2);
1096 if (t != TEXT && t != QUOTED_TEXT) {
1097 lex_error("bad chartype");
1100 token_buffer += '\0';
1101 string type = token_buffer;
1103 if (t != TEXT && t != QUOTED_TEXT) {
1104 lex_error("bad chartype");
1107 token_buffer += '\0';
1108 set_char_type(type.contents(), strsave(token_buffer.contents()));
1113 int t = get_token(2);
1114 if (t != TEXT && t != QUOTED_TEXT) {
1115 lex_error("bad set");
1118 token_buffer += '\0';
1119 string param = token_buffer;
1121 if (t != TEXT && t != QUOTED_TEXT) {
1122 lex_error("bad set");
1125 token_buffer += '\0';
1127 if (sscanf(&token_buffer[0], "%d", &n) != 1) {
1128 lex_error("bad number `%1'", token_buffer.contents());
1131 set_param(param.contents(), n);
1137 int tk = get_token(1);
1152 ignore_definition();
1158 ignore_definition();
1192 token_buffer += '\0';
1193 yylval.str = strsave(token_buffer.contents());
1201 void lex_error(const char *message,
1208 if (!get_location(&filename, &lineno))
1209 error(message, arg1, arg2, arg3);
1211 error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1214 void yyerror(const char *s)
1218 if (!get_location(&filename, &lineno))
1221 error_with_file_and_line(filename, lineno, s);