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)
8 This file is part of groff.
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
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
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. */
26 #include "stringclass.h"
30 // declarations to avoid friend name injection problems
33 int get_location(char **, int *);
46 definition::definition() : is_macro(1), is_simple(0)
51 definition::~definition()
57 declare_ptable(definition
)
58 implement_ptable(definition
)
60 PTABLE(definition
) macro_table
;
67 { "smallover", SMALLOVER
},
88 { "uaccent", UACCENT
},
100 { "lineup", LINEUP
},
104 { "define", DEFINE
},
105 { "sdefine", SDEFINE
},
106 { "ndefine", NDEFINE
},
107 { "tdefine", TDEFINE
},
110 { "include", INCLUDE
},
113 { "chartype", CHARTYPE
},
115 { "vcenter", VCENTER
},
117 { "opprime", PRIME
},
118 { "grfont", GRFONT
},
119 { "gbfont", GBFONT
},
121 { "nosplit", NOSPLIT
},
122 { "special", SPECIAL
},
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 { "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\" { . . . }" },
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 { . . . }" },
254 { "approx", "type \"relation\" \"\\(~=\"" },
257 { "cdot", "type \"binary\" vcenter ." },
261 void init_table(const char *device
)
264 for (i
= 0; i
< sizeof(token_table
)/sizeof(token_table
[0]); i
++) {
265 definition
*def
= new definition
[1];
267 def
->tok
= token_table
[i
].token
;
268 macro_table
.define(token_table
[i
].name
, def
);
270 for (i
= 0; i
< sizeof(def_table
)/sizeof(def_table
[0]); i
++) {
271 definition
*def
= new definition
[1];
273 def
->contents
= strsave(def_table
[i
].def
);
275 macro_table
.define(def_table
[i
].name
, def
);
277 definition
*def
= new definition
[1];
279 def
->contents
= strsave("1");
280 macro_table
.define(device
, def
);
288 virtual int get() = 0;
289 virtual int peek() = 0;
290 virtual int get_location(char **, int *);
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
);
298 class file_input
: public input
{
306 file_input(FILE *, const char *, input
*);
310 int get_location(char **, int *);
314 class macro_input
: public input
{
318 macro_input(const char *, input
*);
324 class top_input
: public macro_input
{
328 top_input(const char *, const char *, int, input
*);
331 int get_location(char **, int *);
334 class argument_macro_input
: public input
{
341 argument_macro_input(const char *, int, char **, input
*);
342 ~argument_macro_input();
347 input::input(input
*x
) : next(x
)
355 int input::get_location(char **, int *)
360 file_input::file_input(FILE *f
, const char *fn
, input
*p
)
361 : input(p
), lineno(0), ptr("")
364 filename
= strsave(fn
);
367 file_input::~file_input()
373 int file_input::read_line()
382 else if (invalid_input_char(c
))
383 lex_error("invalid input character code %1", c
);
390 if (line
.length() == 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
))) {
397 ptr
= line
.contents();
403 int file_input::get()
405 if (*ptr
!= '\0' || read_line())
406 return *ptr
++ & 0377;
411 int file_input::peek()
413 if (*ptr
!= '\0' || read_line())
419 int file_input::get_location(char **fnp
, int *lnp
)
426 macro_input::macro_input(const char *str
, input
*x
) : input(x
)
428 p
= s
= strsave(str
);
431 macro_input::~macro_input()
436 int macro_input::get()
438 if (p
== 0 || *p
== '\0')
444 int macro_input::peek()
446 if (p
== 0 || *p
== '\0')
452 top_input::top_input(const char *str
, const char *fn
, int ln
, input
*x
)
453 : macro_input(str
, x
), lineno(ln
)
455 filename
= strsave(fn
);
458 top_input::~top_input()
465 int c
= macro_input::get();
471 int top_input::get_location(char **fnp
, int *lnp
)
478 // Character representing $1. Must be invalid input character.
481 argument_macro_input::argument_macro_input(const char *body
, int ac
,
483 : input(x
), ap(0), argc(ac
)
486 for (i
= 0; i
< argc
; i
++)
488 p
= s
= strsave(body
);
490 for (i
= 0; s
[i
] != '\0'; i
++)
491 if (s
[i
] == '$' && s
[i
+1] >= '0' && s
[i
+1] <= '9') {
493 s
[j
++] = ARG1
+ s
[++i
] - '1';
501 argument_macro_input::~argument_macro_input()
503 for (int i
= 0; i
< argc
; i
++)
508 int argument_macro_input::get()
517 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
519 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
529 int argument_macro_input::peek()
538 while (*p
>= ARG1
&& *p
<= ARG1
+ 8) {
540 if (i
< argc
&& argv
[i
] != 0 && argv
[i
][0] != '\0') {
550 static input
*current_input
= 0;
552 /* we insert a newline between input from different levels */
556 if (current_input
== 0)
559 int c
= current_input
->get();
563 input
*tem
= current_input
;
564 current_input
= current_input
->next
;
573 if (current_input
== 0)
576 int c
= current_input
->peek();
584 int get_location(char **fnp
, int *lnp
)
586 for (input
*p
= current_input
; p
; p
= p
->next
)
587 if (p
->get_location(fnp
, lnp
))
593 const int NCONTEXT
= 4;
594 string context_ring
[NCONTEXT
];
595 int context_index
= 0;
599 for (int i
= 0; i
< NCONTEXT
; i
++)
600 context_ring
[i
] = "";
606 int i
= context_index
;
607 fputs(" context is\n\t", stderr
);
609 int j
= (i
+ 1) % NCONTEXT
;
610 if (j
== context_index
) {
611 fputs(">>> ", stderr
);
612 put_string(context_ring
[i
], stderr
);
613 fputs(" <<<", stderr
);
616 else if (context_ring
[i
].length() > 0) {
617 put_string(context_ring
[i
], stderr
);
625 void add_context(const string
&s
)
627 context_ring
[context_index
] = s
;
628 context_index
= (context_index
+ 1) % NCONTEXT
;
631 void add_context(char c
)
633 context_ring
[context_index
] = c
;
634 context_index
= (context_index
+ 1) % NCONTEXT
;
637 void add_quoted_context(const string
&s
)
639 string
&r
= context_ring
[context_index
];
641 for (int i
= 0; i
< s
.length(); i
++)
647 context_index
= (context_index
+ 1) % NCONTEXT
;
650 void init_lex(const char *str
, const char *filename
, int lineno
)
652 while (current_input
!= 0) {
653 input
*tem
= current_input
;
654 current_input
= current_input
->next
;
657 current_input
= new top_input(str
, filename
, lineno
, 0);
662 void get_delimited_text()
666 int got_location
= get_location(&filename
, &lineno
);
667 int start
= get_char();
668 while (start
== ' ' || start
== '\t' || start
== '\n')
670 token_buffer
.clear();
673 error_with_file_and_line(filename
, lineno
,
674 "end of input while defining macro");
676 error("end of input while defining macro");
683 error_with_file_and_line(filename
, lineno
,
684 "end of input while defining macro");
686 error("end of input while defining macro");
687 add_context(start
+ token_buffer
);
692 token_buffer
+= char(c
);
694 add_context(start
+ token_buffer
+ start
);
697 void interpolate_macro_with_args(const char *body
)
702 for (i
= 0; i
< 9; i
++)
707 token_buffer
.clear();
711 lex_error("end of input while scanning macro arguments");
714 if (level
== 0 && (c
== ',' || c
== ')')) {
715 if (token_buffer
.length() > 0) {
716 token_buffer
+= '\0';
717 argv
[argc
] = strsave(token_buffer
.contents());
719 // for `foo()', argc = 0
720 if (argc
> 0 || c
!= ')' || i
> 0)
724 token_buffer
+= char(c
);
730 } while (c
!= ')' && c
!= EOF
);
731 current_input
= new argument_macro_input(body
, argc
, argv
, current_input
);
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.
738 int get_token(int lookup_flag
= 0)
742 while (c
== ' ' || c
== '\n')
747 add_context("end of input");
753 token_buffer
.clear();
757 lex_error("missing \"");
760 else if (c
== '\n') {
761 lex_error("newline before end of quoted text");
767 token_buffer
[token_buffer
.length() - 1] = '"';
772 quoted
= quoted
? 0 : c
== '\\';
776 add_quoted_context(token_buffer
);
789 token_buffer
.clear();
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
);
806 token_buffer
.set_length(token_buffer
.length() - 1);
812 lex_error("`\\' ignored at end of equation");
816 lex_error("`\\' ignored because followed by newline");
820 lex_error("`\\' ignored because followed by tab");
829 token_buffer
+= '\\';
853 token_buffer
+= char(c
);
858 if (break_flag
|| token_buffer
.length() == 0)
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);
866 current_input
= new macro_input(def
->contents
, current_input
);
869 else if (lookup_flag
== 1) {
870 add_context(token_buffer
);
875 add_context(token_buffer
);
884 int t
= get_token(2);
885 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
886 lex_error("bad filename for include");
889 token_buffer
+= '\0';
890 const char *filename
= token_buffer
.contents();
892 FILE *fp
= fopen(filename
, "r");
894 lex_error("can't open included file `%1'", filename
);
897 current_input
= new file_input(fp
, filename
, current_input
);
900 void ignore_definition()
904 lex_error("bad definition");
907 get_delimited_text();
910 void do_definition(int is_simple
)
914 lex_error("bad definition");
917 token_buffer
+= '\0';
918 const char *name
= token_buffer
.contents();
919 definition
*def
= macro_table
.lookup(name
);
921 def
= new definition
[1];
922 macro_table
.define(name
, def
);
924 else if (def
->is_macro
) {
925 a_delete def
->contents
;
927 get_delimited_text();
928 token_buffer
+= '\0';
930 def
->contents
= strsave(token_buffer
.contents());
931 def
->is_simple
= is_simple
;
938 lex_error("bad undef command");
941 token_buffer
+= '\0';
942 macro_table
.define(token_buffer
.contents(), 0);
947 int t
= get_token(2);
948 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
949 lex_error("bad argument to gsize command");
952 token_buffer
+= '\0';
953 if (!set_gsize(token_buffer
.contents()))
954 lex_error("invalid size `%1'", token_buffer
.contents());
959 int t
= get_token(2);
960 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
961 lex_error("bad argument to gfont command");
964 token_buffer
+= '\0';
965 set_gfont(token_buffer
.contents());
970 int t
= get_token(2);
971 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
972 lex_error("bad argument to grfont command");
975 token_buffer
+= '\0';
976 set_grfont(token_buffer
.contents());
981 int t
= get_token(2);
982 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
983 lex_error("bad argument to gbfont command");
986 token_buffer
+= '\0';
987 set_gbfont(token_buffer
.contents());
992 int t
= get_token(2);
993 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
994 lex_error("bad argument to space command");
997 token_buffer
+= '\0';
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());
1008 int t
= get_token();
1010 lex_error("bad ifdef");
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();
1018 token_buffer
+= '\0';
1019 current_input
= new macro_input(token_buffer
.contents(), current_input
);
1026 while (c
== ' ' || c
== '\n')
1029 if (c
== EOF
|| (d
= get_char()) == EOF
)
1030 lex_error("end of file while reading argument to `delim'");
1032 if (c
== 'o' && d
== 'f' && peek_char() == 'f') {
1034 start_delim
= end_delim
= '\0';
1045 int t
= get_token(2);
1046 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1047 lex_error("bad chartype");
1050 token_buffer
+= '\0';
1051 string type
= token_buffer
;
1053 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1054 lex_error("bad chartype");
1057 token_buffer
+= '\0';
1058 set_char_type(type
.contents(), strsave(token_buffer
.contents()));
1063 int t
= get_token(2);
1064 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1065 lex_error("bad set");
1068 token_buffer
+= '\0';
1069 string param
= token_buffer
;
1071 if (t
!= TEXT
&& t
!= QUOTED_TEXT
) {
1072 lex_error("bad set");
1075 token_buffer
+= '\0';
1077 if (sscanf(&token_buffer
[0], "%d", &n
) != 1) {
1078 lex_error("bad number `%1'", token_buffer
.contents());
1081 set_param(param
.contents(), n
);
1087 int tk
= get_token(1);
1102 ignore_definition();
1108 ignore_definition();
1142 token_buffer
+= '\0';
1143 yylval
.str
= strsave(token_buffer
.contents());
1151 void lex_error(const char *message
,
1158 if (!get_location(&filename
, &lineno
))
1159 error(message
, arg1
, arg2
, arg3
);
1161 error_with_file_and_line(filename
, lineno
, message
, arg1
, arg2
, arg3
);
1164 void yyerror(const char *s
)
1168 if (!get_location(&filename
, &lineno
))
1171 error_with_file_and_line(filename
, lineno
, s
);