2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 - 2003, 2005, 2007, 2008
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 * This 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 "eqn-config.h"
28 #include "file_case.h"
30 #include "stringclass.h"
32 // declarations to avoid friend name injection problems
35 int get_location(char **, int *);
50 definition::definition() : is_macro(1), is_simple(0)
55 definition::~definition()
61 declare_ptable(definition
)
62 implement_ptable(definition
)
64 PTABLE(definition
) macro_table
;
69 } token_table
[] = { // FIXME const
71 { "smallover", SMALLOVER
},
92 { "uaccent", UACCENT
},
104 { "lineup", LINEUP
},
108 { "define", DEFINE
},
109 { "sdefine", SDEFINE
},
110 { "ndefine", NDEFINE
},
111 { "tdefine", TDEFINE
},
114 { "include", INCLUDE
},
117 { "chartype", CHARTYPE
},
119 { "vcenter", VCENTER
},
121 { "opprime", PRIME
},
122 { "grfont", GRFONT
},
123 { "gbfont", GBFONT
},
125 { "nosplit", NOSPLIT
},
126 { "special", SPECIAL
},
134 static struct builtin_def common_defs
[] = { // FIXME const
135 { "ALPHA", "\\(*A" },
138 { "DELTA", "\\(*D" },
139 { "EPSILON", "\\(*E" },
141 { "GAMMA", "\\(*G" },
143 { "KAPPA", "\\(*K" },
144 { "LAMBDA", "\\(*L" },
147 { "OMEGA", "\\(*W" },
148 { "OMICRON", "\\(*O" },
153 { "SIGMA", "\\(*S" },
155 { "THETA", "\\(*H" },
156 { "UPSILON", "\\(*U" },
159 { "Alpha", "\\(*A" },
162 { "Delta", "\\(*D" },
163 { "Epsilon", "\\(*E" },
165 { "Gamma", "\\(*G" },
167 { "Kappa", "\\(*K" },
168 { "Lambda", "\\(*L" },
171 { "Omega", "\\(*W" },
172 { "Omicron", "\\(*O" },
177 { "Sigma", "\\(*S" },
179 { "Theta", "\\(*H" },
180 { "Upsilon", "\\(*U" },
183 { "alpha", "\\(*a" },
186 { "delta", "\\(*d" },
187 { "epsilon", "\\(*e" },
189 { "gamma", "\\(*g" },
191 { "kappa", "\\(*k" },
192 { "lambda", "\\(*l" },
195 { "omega", "\\(*w" },
196 { "omicron", "\\(*o" },
201 { "sigma", "\\(*s" },
203 { "theta", "\\(*h" },
204 { "upsilon", "\\(*u" },
207 { "max", "{type \"operator\" roman \"max\"}" },
208 { "min", "{type \"operator\" roman \"min\"}" },
209 { "lim", "{type \"operator\" roman \"lim\"}" },
210 { "sin", "{type \"operator\" roman \"sin\"}" },
211 { "cos", "{type \"operator\" roman \"cos\"}" },
212 { "tan", "{type \"operator\" roman \"tan\"}" },
213 { "sinh", "{type \"operator\" roman \"sinh\"}" },
214 { "cosh", "{type \"operator\" roman \"cosh\"}" },
215 { "tanh", "{type \"operator\" roman \"tanh\"}" },
216 { "arc", "{type \"operator\" roman \"arc\"}" },
217 { "log", "{type \"operator\" roman \"log\"}" },
218 { "ln", "{type \"operator\" roman \"ln\"}" },
219 { "exp", "{type \"operator\" roman \"exp\"}" },
220 { "Re", "{type \"operator\" roman \"Re\"}" },
221 { "Im", "{type \"operator\" roman \"Im\"}" },
222 { "det", "{type \"operator\" roman \"det\"}" },
223 { "and", "{roman \"and\"}" },
224 { "if", "{roman \"if\"}" },
225 { "for", "{roman \"for\"}" },
226 { "times", "type \"binary\" \\(mu" },
227 { "ldots", "type \"inner\" { . . . }" },
229 { "partial", "\\(pd" },
230 { "nothing", "\"\"" },
231 { "half", "{1 smallover 2}" },
232 { "hat_def", "roman \"^\"" },
233 { "hat", "accent { hat_def }" },
234 { "tilde_def", "\"~\"" },
235 { "tilde", "accent { tilde_def }" },
236 { "==", "type \"relation\" \\(==" },
237 { "!=", "type \"relation\" \\(!=" },
238 { "+-", "type \"binary\" \\(+-" },
239 { "->", "type \"relation\" \\(->" },
240 { "<-", "type \"relation\" \\(<-" },
241 { "<<", "type \"relation\" \\(<<" },
242 { ">>", "type \"relation\" \\(>>" },
244 { "approx", "type \"relation\" \"\\(~=\"" },
247 { "cdot", "type \"binary\" \\(md" },
248 { "cdots", "type \"inner\" { \\(md \\(md \\(md }" },
252 /* composite definitions that require troff size and motion operators */
253 static struct builtin_def troff_defs
[] = { // FIXME const
254 { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
255 { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
256 { "int", "{type \"operator\" vcenter size +8 \\(is}" },
257 { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
258 { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
259 { "dot_def", "up 52 back 15 \".\"" },
260 { "dot", "accent { dot_def }" },
261 { "dotdot_def", "up 52 back 25 \"..\"" },
262 { "dotdot", "accent { dotdot_def }" },
263 { "utilde_def", "down 75 \"~\"" },
264 { "utilde", "uaccent { utilde_def }" },
265 { "vec_def", "up 52 size -5 \\(->" },
266 { "vec", "accent { vec_def }" },
267 { "dyad_def", "up 52 size -5 { \\(<> }" },
268 { "dyad", "accent { dyad_def }" },
269 { "...", "type \"inner\" vcenter { . . . }" },
272 /* equivalent definitions for MathML mode */
273 static struct builtin_def mathml_defs
[] = { // FIXME const
274 { "sum", "{type \"operator\" size big \\(*S}" },
275 { "prod", "{type \"operator\" size big \\(*P}" },
276 { "int", "{type \"operator\" size big \\(is}" },
277 { "union", "{type \"operator\" size big \\(cu}" },
278 { "inter", "{type \"operator\" size big \\(ca}" },
279 { "dot", "accent { \".\" }" },
280 { "dotdot", "accent { \"..\" }" },
281 { "utilde", "uaccent { \"~\" }" },
282 { "vec", "accent { \\(-> }" },
283 { "dyad", "accent { \\(<> }" },
284 { "...", "type \"inner\" { . . . }" },
287 void init_table(const char *device
)
290 for (i
= 0; i
< NELEM(token_table
); i
++) {
291 definition
*def
= new definition
[1];
293 def
->tok
= token_table
[i
].token
;
294 macro_table
.define(token_table
[i
].name
, def
);
296 for (i
= 0; i
< NELEM(common_defs
); i
++) {
297 definition
*def
= new definition
[1];
299 def
->contents
= strsave(common_defs
[i
].def
);
301 macro_table
.define(common_defs
[i
].name
, def
);
303 if (output_format
== troff
) {
304 for (i
= 0; i
< NELEM(troff_defs
); i
++) {
305 definition
*def
= new definition
[1];
307 def
->contents
= strsave(troff_defs
[i
].def
);
309 macro_table
.define(troff_defs
[i
].name
, def
);
312 else if (output_format
== mathml
) {
313 for (i
= 0; i
< NELEM(mathml_defs
); i
++) {
314 definition
*def
= new definition
[1];
316 def
->contents
= strsave(mathml_defs
[i
].def
);
318 macro_table
.define(mathml_defs
[i
].name
, def
);
321 definition
*def
= new definition
[1];
323 def
->contents
= strsave("1");
324 macro_table
.define(device
, def
);
329 friend int get_char();
330 friend int peek_char();
331 friend int get_location(char **, int *);
332 friend void init_lex(const char *str
, const char *filename
, int lineno
);
339 virtual int get() = 0;
340 virtual int peek() = 0;
341 virtual int get_location(char **, int *);
356 file_input(file_case
*, const char *, input
*);
360 int get_location(char **, int *);
370 macro_input(const char *, input
*);
383 top_input(const char *, const char *, int, input
*);
386 int get_location(char **, int *);
389 class argument_macro_input
399 argument_macro_input(const char *, int, char **, input
*);
400 ~argument_macro_input();
405 input::input(input
*x
) : next(x
)
413 int input::get_location(char **, int *)
418 file_input::file_input(file_case
*fcp
, const char *fn
, input
*p
)
419 : input(p
), _fcp(fcp
), lineno(0), ptr("")
421 filename
= strsave(fn
);
424 file_input::~file_input()
430 int file_input::read_line() /* TODO lib-roff */
436 int c
= _fcp
->get_c();
437 if (c
== '\r' && (c
= _fcp
->get_c()) != '\n')
438 lex_error("invalid input character CR (carriage return)");
441 else if (invalid_input_char(c
))
442 lex_error("invalid input character code %1", c
);
449 if (line
.length() == 0)
451 if (!(line
.length() >= 3 && line
[0] == '.' && line
[1] == 'E'
452 && (line
[2] == 'Q' || line
[2] == 'N')
453 && (line
.length() == 3 || line
[3] == ' ' || line
[3] == '\n'
454 || compatible_flag
))) {
456 ptr
= line
.contents();
462 int file_input::get()
464 if (*ptr
!= '\0' || read_line())
465 return *ptr
++ & 0377;
470 int file_input::peek()
472 if (*ptr
!= '\0' || read_line())
478 int file_input::get_location(char **fnp
, int *lnp
)
485 macro_input::macro_input(const char *str
, input
*x
) : input(x
)
487 p
= s
= strsave(str
);
490 macro_input::~macro_input()
495 int macro_input::get()
497 if (p
== 0 || *p
== '\0')
503 int macro_input::peek()
505 if (p
== 0 || *p
== '\0')
511 top_input::top_input(const char *str
, const char *fn
, int ln
, input
*x
)
512 : macro_input(str
, x
), lineno(ln
)
514 filename
= strsave(fn
);
517 top_input::~top_input()
524 int c
= macro_input::get();
530 int top_input::get_location(char **fnp
, int *lnp
)
537 argument_macro_input::argument_macro_input(const char *body
, int ac
,
539 : input(x
), ap(0), argc(ac
)
542 for (i
= 0; i
< argc
; i
++)
544 p
= s
= strsave(body
);
546 for (i
= 0; s
[i
] != '\0'; i
++)
547 if (s
[i
] == '$' && s
[i
+1] >= '0' && s
[i
+1] <= '9') {
549 s
[j
++] = ARG1
+ s
[++i
] - '1';
556 argument_macro_input::~argument_macro_input()
558 for (int i
= 0; i
< argc
; i
++)
563 int argument_macro_input::get()
572 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
574 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
584 int argument_macro_input::peek()
593 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
595 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
605 static input
*current_input
= 0;
607 /* we insert a newline between input from different levels */
611 if (current_input
== 0)
614 int c
= current_input
->get();
618 input
*tem
= current_input
;
619 current_input
= current_input
->next
;
628 if (current_input
== 0)
631 int c
= current_input
->peek();
639 int get_location(char **fnp
, int *lnp
)
641 for (input
*p
= current_input
; p
; p
= p
->next
)
642 if (p
->get_location(fnp
, lnp
))
648 const int NCONTEXT
= 4;
649 string context_ring
[NCONTEXT
];
650 int context_index
= 0;
654 for (int i
= 0; i
< NCONTEXT
; i
++)
655 context_ring
[i
] = "";
661 int i
= context_index
;
662 fputs(" context is\n\t", stderr
);
664 int j
= (i
+ 1) % NCONTEXT
;
665 if (j
== context_index
) {
666 fputs(">>> ", stderr
);
667 put_string(context_ring
[i
], stderr
);
668 fputs(" <<<", stderr
);
671 else if (context_ring
[i
].length() > 0) {
672 put_string(context_ring
[i
], stderr
);
680 void add_context(const string
&s
)
682 context_ring
[context_index
] = s
;
683 context_index
= (context_index
+ 1) % NCONTEXT
;
686 void add_context(char c
)
688 context_ring
[context_index
] = c
;
689 context_index
= (context_index
+ 1) % NCONTEXT
;
692 void add_quoted_context(const string
&s
)
694 string
&r
= context_ring
[context_index
];
696 for (int i
= 0; i
< s
.length(); i
++)
702 context_index
= (context_index
+ 1) % NCONTEXT
;
705 void init_lex(const char *str
, const char *filename
, int lineno
)
707 while (current_input
!= 0) {
708 input
*tem
= current_input
;
709 current_input
= current_input
->next
;
712 current_input
= new top_input(str
, filename
, lineno
, 0);
716 void get_delimited_text()
720 int got_location
= get_location(&filename
, &lineno
);
721 int start
= get_char();
722 while (start
== ' ' || start
== '\t' || start
== '\n')
724 token_buffer
.clear();
727 error_with_file_and_line(filename
, lineno
,
728 "end of input while defining macro");
730 error("end of input while defining macro");
737 error_with_file_and_line(filename
, lineno
,
738 "end of input while defining macro");
740 error("end of input while defining macro");
741 add_context(start
+ token_buffer
);
746 token_buffer
+= char(c
);
748 add_context(start
+ token_buffer
+ start
);
751 void interpolate_macro_with_args(const char *body
)
756 for (i
= 0; i
< 9; i
++)
761 token_buffer
.clear();
765 lex_error("end of input while scanning macro arguments");
768 if (level
== 0 && (c
== ',' || c
== ')')) {
769 if (token_buffer
.length() > 0) {
770 token_buffer
+= '\0';
771 argv
[argc
] = strsave(token_buffer
.contents());
773 // for `foo()', argc = 0
774 if (argc
> 0 || c
!= ')' || i
> 0)
778 token_buffer
+= char(c
);
784 } while (c
!= ')' && c
!= EOF
);
785 current_input
= new argument_macro_input(body
, argc
, argv
, current_input
);
788 /* If lookup flag is non-zero the token will be looked up to see
789 if it is macro. If it's 1, it will looked up to see if it's a token.
792 int get_token(int lookup_flag
= 0)
796 while (c
== ' ' || c
== '\n')
801 add_context("end of input");
807 token_buffer
.clear();
811 lex_error("missing \"");
814 else if (c
== '\n') {
815 lex_error("newline before end of quoted text");
821 token_buffer
[token_buffer
.length() - 1] = '"';
826 quoted
= quoted
? 0 : c
== '\\';
830 add_quoted_context(token_buffer
);
843 token_buffer
.clear();
851 if (!quoted
&& lookup_flag
!= 0 && c
== '(') {
852 token_buffer
+= '\0';
853 definition
*def
= macro_table
.lookup(token_buffer
.contents());
854 if (def
&& def
->is_macro
&& !def
->is_simple
) {
855 (void)get_char(); // skip initial '('
856 interpolate_macro_with_args(def
->contents
);
860 token_buffer
.set_length(token_buffer
.length() - 1);
866 lex_error("`\\' ignored at end of equation");
870 lex_error("`\\' ignored because followed by newline");
874 lex_error("`\\' ignored because followed by tab");
883 token_buffer
+= '\\';
907 token_buffer
+= char(c
);
912 if (break_flag
|| token_buffer
.length() == 0)
914 if (lookup_flag
!= 0) {
915 token_buffer
+= '\0';
916 definition
*def
= macro_table
.lookup(token_buffer
.contents());
917 token_buffer
.set_length(token_buffer
.length() - 1);
920 current_input
= new macro_input(def
->contents
, current_input
);
923 else if (lookup_flag
== 1) {
924 add_context(token_buffer
);
929 add_context(token_buffer
);
938 int t
= get_token(2);
939 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
940 lex_error("bad filename for include");
943 token_buffer
+= '\0';
944 const char *filename
= token_buffer
.contents();
945 file_case
*fcp
= file_case::muxer(filename
);
947 lex_error("can't open included file `%1'", filename
);
950 current_input
= new file_input(fcp
, filename
, current_input
);
953 void ignore_definition()
957 lex_error("bad definition");
960 get_delimited_text();
963 void do_definition(int is_simple
)
967 lex_error("bad definition");
970 token_buffer
+= '\0';
971 const char *name
= token_buffer
.contents();
972 definition
*def
= macro_table
.lookup(name
);
974 def
= new definition
[1];
975 macro_table
.define(name
, def
);
977 else if (def
->is_macro
) {
978 a_delete def
->contents
;
980 get_delimited_text();
981 token_buffer
+= '\0';
983 def
->contents
= strsave(token_buffer
.contents());
984 def
->is_simple
= is_simple
;
991 lex_error("bad undef command");
994 token_buffer
+= '\0';
995 macro_table
.define(token_buffer
.contents(), 0);
1000 int t
= get_token(2);
1001 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1002 lex_error("bad argument to gsize command");
1005 token_buffer
+= '\0';
1006 if (!set_gsize(token_buffer
.contents()))
1007 lex_error("invalid size `%1'", token_buffer
.contents());
1012 int t
= get_token(2);
1013 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1014 lex_error("bad argument to gfont command");
1017 token_buffer
+= '\0';
1018 set_gfont(token_buffer
.contents());
1023 int t
= get_token(2);
1024 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1025 lex_error("bad argument to grfont command");
1028 token_buffer
+= '\0';
1029 set_grfont(token_buffer
.contents());
1034 int t
= get_token(2);
1035 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1036 lex_error("bad argument to gbfont command");
1039 token_buffer
+= '\0';
1040 set_gbfont(token_buffer
.contents());
1045 int t
= get_token(2);
1046 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1047 lex_error("bad argument to space command");
1050 token_buffer
+= '\0';
1052 long n
= strtol(token_buffer
.contents(), &ptr
, 10);
1053 if (n
== 0 && ptr
== token_buffer
.contents())
1054 lex_error("bad argument `%1' to space command", token_buffer
.contents());
1061 int t
= get_token();
1063 lex_error("bad ifdef");
1066 token_buffer
+= '\0';
1067 definition
*def
= macro_table
.lookup(token_buffer
.contents());
1068 int result
= def
&& def
->is_macro
&& !def
->is_simple
;
1069 get_delimited_text();
1071 token_buffer
+= '\0';
1072 current_input
= new macro_input(token_buffer
.contents(), current_input
);
1079 while (c
== ' ' || c
== '\n')
1082 if (c
== EOF
|| (d
= get_char()) == EOF
)
1083 lex_error("end of file while reading argument to `delim'");
1085 if (c
== 'o' && d
== 'f' && peek_char() == 'f') {
1087 start_delim
= end_delim
= '\0';
1098 int t
= get_token(2);
1099 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1100 lex_error("bad chartype");
1103 token_buffer
+= '\0';
1104 string type
= token_buffer
;
1106 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1107 lex_error("bad chartype");
1110 token_buffer
+= '\0';
1111 set_char_type(type
.contents(), strsave(token_buffer
.contents()));
1116 int t
= get_token(2);
1117 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1118 lex_error("bad set");
1121 token_buffer
+= '\0';
1122 string param
= token_buffer
;
1124 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1125 lex_error("bad set");
1128 token_buffer
+= '\0';
1130 if (sscanf(&token_buffer
[0], "%d", &n
) != 1) {
1131 lex_error("bad number `%1'", token_buffer
.contents());
1134 set_param(param
.contents(), n
);
1140 int tk
= get_token(1);
1155 ignore_definition();
1161 ignore_definition();
1195 token_buffer
+= '\0';
1196 yylval
.str
= strsave(token_buffer
.contents());
1204 void lex_error(const char *message
,
1211 if (!get_location(&filename
, &lineno
))
1212 error(message
, arg1
, arg2
, arg3
);
1214 error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1217 void yyerror(const char *s
)
1221 if (!get_location(&filename
, &lineno
))
1224 error_with_file_and_line(filename
, lineno
, s
);